17224cc9e2318fd3dd3902a21019a18bf8ff48c0
[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              cn : [
2757                 {
2758                     cls: "modal-dialog " + size,
2759                     cn : [
2760                         {
2761                             cls : "modal-content",
2762                             cn : [
2763                                 {
2764                                     cls : 'modal-header',
2765                                     cn : header
2766                                 },
2767                                 bdy,
2768                                 {
2769                                     cls : 'modal-footer',
2770                                     cn : [
2771                                         {
2772                                             tag: 'div',
2773                                             cls: 'btn-' + this.buttonPosition
2774                                         }
2775                                     ]
2776
2777                                 }
2778
2779
2780                             ]
2781
2782                         }
2783                     ]
2784
2785                 }
2786             ]
2787         };
2788
2789         if(this.animate){
2790             modal.cls += ' fade';
2791         }
2792
2793         return modal;
2794
2795     },
2796     getChildContainer : function() {
2797
2798          return this.bodyEl;
2799
2800     },
2801     getButtonContainer : function() {
2802          return this.el.select('.modal-footer div',true).first();
2803
2804     },
2805     initEvents : function()
2806     {
2807         if (this.allow_close) {
2808             this.closeEl.on('click', this.hide, this);
2809         }
2810         Roo.EventManager.onWindowResize(this.resize, this, true);
2811
2812
2813     },
2814
2815     resize : function()
2816     {
2817         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2818         if (this.fitwindow) {
2819             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2820             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2821             this.setSize(w,h);
2822         }
2823     },
2824
2825     setSize : function(w,h)
2826     {
2827         if (!w && !h) {
2828             return;
2829         }
2830         this.resizeTo(w,h);
2831     },
2832
2833     show : function() {
2834
2835         if (!this.rendered) {
2836             this.render();
2837         }
2838
2839         //this.el.setStyle('display', 'block');
2840         this.el.addClass('show');
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                 this.el.addClass('hideing');
2888                 (function(){
2889                     if (!this.el.hasClass('hideing')) {
2890                         return; // it's been shown again...
2891                     }
2892                     this.el.removeClass('show');
2893                     this.el.removeClass('hideing');
2894                 }).defer(150,this);
2895                 
2896             }else{
2897                  this.el.removeClass('show');
2898             }
2899             this.fireEvent('hide', this);
2900         }
2901     },
2902     isVisible : function()
2903     {
2904         
2905         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2906         
2907     },
2908
2909     addButton : function(str, cb)
2910     {
2911
2912
2913         var b = Roo.apply({}, { html : str } );
2914         b.xns = b.xns || Roo.bootstrap;
2915         b.xtype = b.xtype || 'Button';
2916         if (typeof(b.listeners) == 'undefined') {
2917             b.listeners = { click : cb.createDelegate(this)  };
2918         }
2919
2920         var btn = Roo.factory(b);
2921
2922         btn.render(this.el.select('.modal-footer div').first());
2923
2924         return btn;
2925
2926     },
2927
2928     setDefaultButton : function(btn)
2929     {
2930         //this.el.select('.modal-footer').()
2931     },
2932     diff : false,
2933
2934     resizeTo: function(w,h)
2935     {
2936         // skip.. ?? why??
2937
2938         this.dialogEl.setWidth(w);
2939         if (this.diff === false) {
2940             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2941         }
2942
2943         this.bodyEl.setHeight(h-this.diff);
2944
2945         this.fireEvent('resize', this);
2946
2947     },
2948     setContentSize  : function(w, h)
2949     {
2950
2951     },
2952     onButtonClick: function(btn,e)
2953     {
2954         //Roo.log([a,b,c]);
2955         this.fireEvent('btnclick', btn.name, e);
2956     },
2957      /**
2958      * Set the title of the Dialog
2959      * @param {String} str new Title
2960      */
2961     setTitle: function(str) {
2962         this.titleEl.dom.innerHTML = str;
2963     },
2964     /**
2965      * Set the body of the Dialog
2966      * @param {String} str new Title
2967      */
2968     setBody: function(str) {
2969         this.bodyEl.dom.innerHTML = str;
2970     },
2971     /**
2972      * Set the body of the Dialog using the template
2973      * @param {Obj} data - apply this data to the template and replace the body contents.
2974      */
2975     applyBody: function(obj)
2976     {
2977         if (!this.tmpl) {
2978             Roo.log("Error - using apply Body without a template");
2979             //code
2980         }
2981         this.tmpl.overwrite(this.bodyEl, obj);
2982     }
2983
2984 });
2985
2986
2987 Roo.apply(Roo.bootstrap.Modal,  {
2988     /**
2989          * Button config that displays a single OK button
2990          * @type Object
2991          */
2992         OK :  [{
2993             name : 'ok',
2994             weight : 'primary',
2995             html : 'OK'
2996         }],
2997         /**
2998          * Button config that displays Yes and No buttons
2999          * @type Object
3000          */
3001         YESNO : [
3002             {
3003                 name  : 'no',
3004                 html : 'No'
3005             },
3006             {
3007                 name  :'yes',
3008                 weight : 'primary',
3009                 html : 'Yes'
3010             }
3011         ],
3012
3013         /**
3014          * Button config that displays OK and Cancel buttons
3015          * @type Object
3016          */
3017         OKCANCEL : [
3018             {
3019                name : 'cancel',
3020                 html : 'Cancel'
3021             },
3022             {
3023                 name : 'ok',
3024                 weight : 'primary',
3025                 html : 'OK'
3026             }
3027         ],
3028         /**
3029          * Button config that displays Yes, No and Cancel buttons
3030          * @type Object
3031          */
3032         YESNOCANCEL : [
3033             {
3034                 name : 'yes',
3035                 weight : 'primary',
3036                 html : 'Yes'
3037             },
3038             {
3039                 name : 'no',
3040                 html : 'No'
3041             },
3042             {
3043                 name : 'cancel',
3044                 html : 'Cancel'
3045             }
3046         ],
3047         
3048         zIndex : 10001
3049 });
3050 /*
3051  * - LGPL
3052  *
3053  * messagebox - can be used as a replace
3054  * 
3055  */
3056 /**
3057  * @class Roo.MessageBox
3058  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3059  * Example usage:
3060  *<pre><code>
3061 // Basic alert:
3062 Roo.Msg.alert('Status', 'Changes saved successfully.');
3063
3064 // Prompt for user data:
3065 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3066     if (btn == 'ok'){
3067         // process text value...
3068     }
3069 });
3070
3071 // Show a dialog using config options:
3072 Roo.Msg.show({
3073    title:'Save Changes?',
3074    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3075    buttons: Roo.Msg.YESNOCANCEL,
3076    fn: processResult,
3077    animEl: 'elId'
3078 });
3079 </code></pre>
3080  * @singleton
3081  */
3082 Roo.bootstrap.MessageBox = function(){
3083     var dlg, opt, mask, waitTimer;
3084     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3085     var buttons, activeTextEl, bwidth;
3086
3087     
3088     // private
3089     var handleButton = function(button){
3090         dlg.hide();
3091         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3092     };
3093
3094     // private
3095     var handleHide = function(){
3096         if(opt && opt.cls){
3097             dlg.el.removeClass(opt.cls);
3098         }
3099         //if(waitTimer){
3100         //    Roo.TaskMgr.stop(waitTimer);
3101         //    waitTimer = null;
3102         //}
3103     };
3104
3105     // private
3106     var updateButtons = function(b){
3107         var width = 0;
3108         if(!b){
3109             buttons["ok"].hide();
3110             buttons["cancel"].hide();
3111             buttons["yes"].hide();
3112             buttons["no"].hide();
3113             //dlg.footer.dom.style.display = 'none';
3114             return width;
3115         }
3116         dlg.footerEl.dom.style.display = '';
3117         for(var k in buttons){
3118             if(typeof buttons[k] != "function"){
3119                 if(b[k]){
3120                     buttons[k].show();
3121                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3122                     width += buttons[k].el.getWidth()+15;
3123                 }else{
3124                     buttons[k].hide();
3125                 }
3126             }
3127         }
3128         return width;
3129     };
3130
3131     // private
3132     var handleEsc = function(d, k, e){
3133         if(opt && opt.closable !== false){
3134             dlg.hide();
3135         }
3136         if(e){
3137             e.stopEvent();
3138         }
3139     };
3140
3141     return {
3142         /**
3143          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3144          * @return {Roo.BasicDialog} The BasicDialog element
3145          */
3146         getDialog : function(){
3147            if(!dlg){
3148                 dlg = new Roo.bootstrap.Modal( {
3149                     //draggable: true,
3150                     //resizable:false,
3151                     //constraintoviewport:false,
3152                     //fixedcenter:true,
3153                     //collapsible : false,
3154                     //shim:true,
3155                     //modal: true,
3156                 //    width: 'auto',
3157                   //  height:100,
3158                     //buttonAlign:"center",
3159                     closeClick : function(){
3160                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3161                             handleButton("no");
3162                         }else{
3163                             handleButton("cancel");
3164                         }
3165                     }
3166                 });
3167                 dlg.render();
3168                 dlg.on("hide", handleHide);
3169                 mask = dlg.mask;
3170                 //dlg.addKeyListener(27, handleEsc);
3171                 buttons = {};
3172                 this.buttons = buttons;
3173                 var bt = this.buttonText;
3174                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3175                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3176                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3177                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3178                 //Roo.log(buttons);
3179                 bodyEl = dlg.bodyEl.createChild({
3180
3181                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3182                         '<textarea class="roo-mb-textarea"></textarea>' +
3183                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3184                 });
3185                 msgEl = bodyEl.dom.firstChild;
3186                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3187                 textboxEl.enableDisplayMode();
3188                 textboxEl.addKeyListener([10,13], function(){
3189                     if(dlg.isVisible() && opt && opt.buttons){
3190                         if(opt.buttons.ok){
3191                             handleButton("ok");
3192                         }else if(opt.buttons.yes){
3193                             handleButton("yes");
3194                         }
3195                     }
3196                 });
3197                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3198                 textareaEl.enableDisplayMode();
3199                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3200                 progressEl.enableDisplayMode();
3201                 
3202                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3203                 var pf = progressEl.dom.firstChild;
3204                 if (pf) {
3205                     pp = Roo.get(pf.firstChild);
3206                     pp.setHeight(pf.offsetHeight);
3207                 }
3208                 
3209             }
3210             return dlg;
3211         },
3212
3213         /**
3214          * Updates the message box body text
3215          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3216          * the XHTML-compliant non-breaking space character '&amp;#160;')
3217          * @return {Roo.MessageBox} This message box
3218          */
3219         updateText : function(text)
3220         {
3221             if(!dlg.isVisible() && !opt.width){
3222                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3223                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3224             }
3225             msgEl.innerHTML = text || '&#160;';
3226       
3227             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3228             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3229             var w = Math.max(
3230                     Math.min(opt.width || cw , this.maxWidth), 
3231                     Math.max(opt.minWidth || this.minWidth, bwidth)
3232             );
3233             if(opt.prompt){
3234                 activeTextEl.setWidth(w);
3235             }
3236             if(dlg.isVisible()){
3237                 dlg.fixedcenter = false;
3238             }
3239             // to big, make it scroll. = But as usual stupid IE does not support
3240             // !important..
3241             
3242             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3243                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3244                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3245             } else {
3246                 bodyEl.dom.style.height = '';
3247                 bodyEl.dom.style.overflowY = '';
3248             }
3249             if (cw > w) {
3250                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3251             } else {
3252                 bodyEl.dom.style.overflowX = '';
3253             }
3254             
3255             dlg.setContentSize(w, bodyEl.getHeight());
3256             if(dlg.isVisible()){
3257                 dlg.fixedcenter = true;
3258             }
3259             return this;
3260         },
3261
3262         /**
3263          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3264          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3265          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3266          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3267          * @return {Roo.MessageBox} This message box
3268          */
3269         updateProgress : function(value, text){
3270             if(text){
3271                 this.updateText(text);
3272             }
3273             
3274             if (pp) { // weird bug on my firefox - for some reason this is not defined
3275                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3276                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3277             }
3278             return this;
3279         },        
3280
3281         /**
3282          * Returns true if the message box is currently displayed
3283          * @return {Boolean} True if the message box is visible, else false
3284          */
3285         isVisible : function(){
3286             return dlg && dlg.isVisible();  
3287         },
3288
3289         /**
3290          * Hides the message box if it is displayed
3291          */
3292         hide : function(){
3293             if(this.isVisible()){
3294                 dlg.hide();
3295             }  
3296         },
3297
3298         /**
3299          * Displays a new message box, or reinitializes an existing message box, based on the config options
3300          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3301          * The following config object properties are supported:
3302          * <pre>
3303 Property    Type             Description
3304 ----------  ---------------  ------------------------------------------------------------------------------------
3305 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3306                                    closes (defaults to undefined)
3307 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3308                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3309 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3310                                    progress and wait dialogs will ignore this property and always hide the
3311                                    close button as they can only be closed programmatically.
3312 cls               String           A custom CSS class to apply to the message box element
3313 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3314                                    displayed (defaults to 75)
3315 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3316                                    function will be btn (the name of the button that was clicked, if applicable,
3317                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3318                                    Progress and wait dialogs will ignore this option since they do not respond to
3319                                    user actions and can only be closed programmatically, so any required function
3320                                    should be called by the same code after it closes the dialog.
3321 icon              String           A CSS class that provides a background image to be used as an icon for
3322                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3323 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3324 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3325 modal             Boolean          False to allow user interaction with the page while the message box is
3326                                    displayed (defaults to true)
3327 msg               String           A string that will replace the existing message box body text (defaults
3328                                    to the XHTML-compliant non-breaking space character '&#160;')
3329 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3330 progress          Boolean          True to display a progress bar (defaults to false)
3331 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3332 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3333 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3334 title             String           The title text
3335 value             String           The string value to set into the active textbox element if displayed
3336 wait              Boolean          True to display a progress bar (defaults to false)
3337 width             Number           The width of the dialog in pixels
3338 </pre>
3339          *
3340          * Example usage:
3341          * <pre><code>
3342 Roo.Msg.show({
3343    title: 'Address',
3344    msg: 'Please enter your address:',
3345    width: 300,
3346    buttons: Roo.MessageBox.OKCANCEL,
3347    multiline: true,
3348    fn: saveAddress,
3349    animEl: 'addAddressBtn'
3350 });
3351 </code></pre>
3352          * @param {Object} config Configuration options
3353          * @return {Roo.MessageBox} This message box
3354          */
3355         show : function(options)
3356         {
3357             
3358             // this causes nightmares if you show one dialog after another
3359             // especially on callbacks..
3360              
3361             if(this.isVisible()){
3362                 
3363                 this.hide();
3364                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3365                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3366                 Roo.log("New Dialog Message:" +  options.msg )
3367                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3368                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3369                 
3370             }
3371             var d = this.getDialog();
3372             opt = options;
3373             d.setTitle(opt.title || "&#160;");
3374             d.closeEl.setDisplayed(opt.closable !== false);
3375             activeTextEl = textboxEl;
3376             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3377             if(opt.prompt){
3378                 if(opt.multiline){
3379                     textboxEl.hide();
3380                     textareaEl.show();
3381                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3382                         opt.multiline : this.defaultTextHeight);
3383                     activeTextEl = textareaEl;
3384                 }else{
3385                     textboxEl.show();
3386                     textareaEl.hide();
3387                 }
3388             }else{
3389                 textboxEl.hide();
3390                 textareaEl.hide();
3391             }
3392             progressEl.setDisplayed(opt.progress === true);
3393             this.updateProgress(0);
3394             activeTextEl.dom.value = opt.value || "";
3395             if(opt.prompt){
3396                 dlg.setDefaultButton(activeTextEl);
3397             }else{
3398                 var bs = opt.buttons;
3399                 var db = null;
3400                 if(bs && bs.ok){
3401                     db = buttons["ok"];
3402                 }else if(bs && bs.yes){
3403                     db = buttons["yes"];
3404                 }
3405                 dlg.setDefaultButton(db);
3406             }
3407             bwidth = updateButtons(opt.buttons);
3408             this.updateText(opt.msg);
3409             if(opt.cls){
3410                 d.el.addClass(opt.cls);
3411             }
3412             d.proxyDrag = opt.proxyDrag === true;
3413             d.modal = opt.modal !== false;
3414             d.mask = opt.modal !== false ? mask : false;
3415             if(!d.isVisible()){
3416                 // force it to the end of the z-index stack so it gets a cursor in FF
3417                 document.body.appendChild(dlg.el.dom);
3418                 d.animateTarget = null;
3419                 d.show(options.animEl);
3420             }
3421             return this;
3422         },
3423
3424         /**
3425          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3426          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3427          * and closing the message box when the process is complete.
3428          * @param {String} title The title bar text
3429          * @param {String} msg The message box body text
3430          * @return {Roo.MessageBox} This message box
3431          */
3432         progress : function(title, msg){
3433             this.show({
3434                 title : title,
3435                 msg : msg,
3436                 buttons: false,
3437                 progress:true,
3438                 closable:false,
3439                 minWidth: this.minProgressWidth,
3440                 modal : true
3441             });
3442             return this;
3443         },
3444
3445         /**
3446          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3447          * If a callback function is passed it will be called after the user clicks the button, and the
3448          * id of the button that was clicked will be passed as the only parameter to the callback
3449          * (could also be the top-right close button).
3450          * @param {String} title The title bar text
3451          * @param {String} msg The message box body text
3452          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3453          * @param {Object} scope (optional) The scope of the callback function
3454          * @return {Roo.MessageBox} This message box
3455          */
3456         alert : function(title, msg, fn, scope)
3457         {
3458             this.show({
3459                 title : title,
3460                 msg : msg,
3461                 buttons: this.OK,
3462                 fn: fn,
3463                 closable : false,
3464                 scope : scope,
3465                 modal : true
3466             });
3467             return this;
3468         },
3469
3470         /**
3471          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3472          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3473          * You are responsible for closing the message box when the process is complete.
3474          * @param {String} msg The message box body text
3475          * @param {String} title (optional) The title bar text
3476          * @return {Roo.MessageBox} This message box
3477          */
3478         wait : function(msg, title){
3479             this.show({
3480                 title : title,
3481                 msg : msg,
3482                 buttons: false,
3483                 closable:false,
3484                 progress:true,
3485                 modal:true,
3486                 width:300,
3487                 wait:true
3488             });
3489             waitTimer = Roo.TaskMgr.start({
3490                 run: function(i){
3491                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3492                 },
3493                 interval: 1000
3494             });
3495             return this;
3496         },
3497
3498         /**
3499          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3500          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3501          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3502          * @param {String} title The title bar text
3503          * @param {String} msg The message box body text
3504          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3505          * @param {Object} scope (optional) The scope of the callback function
3506          * @return {Roo.MessageBox} This message box
3507          */
3508         confirm : function(title, msg, fn, scope){
3509             this.show({
3510                 title : title,
3511                 msg : msg,
3512                 buttons: this.YESNO,
3513                 fn: fn,
3514                 scope : scope,
3515                 modal : true
3516             });
3517             return this;
3518         },
3519
3520         /**
3521          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3522          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3523          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3524          * (could also be the top-right close button) and the text that was entered will be passed as the two
3525          * parameters to the callback.
3526          * @param {String} title The title bar text
3527          * @param {String} msg The message box body text
3528          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3529          * @param {Object} scope (optional) The scope of the callback function
3530          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3531          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3532          * @return {Roo.MessageBox} This message box
3533          */
3534         prompt : function(title, msg, fn, scope, multiline){
3535             this.show({
3536                 title : title,
3537                 msg : msg,
3538                 buttons: this.OKCANCEL,
3539                 fn: fn,
3540                 minWidth:250,
3541                 scope : scope,
3542                 prompt:true,
3543                 multiline: multiline,
3544                 modal : true
3545             });
3546             return this;
3547         },
3548
3549         /**
3550          * Button config that displays a single OK button
3551          * @type Object
3552          */
3553         OK : {ok:true},
3554         /**
3555          * Button config that displays Yes and No buttons
3556          * @type Object
3557          */
3558         YESNO : {yes:true, no:true},
3559         /**
3560          * Button config that displays OK and Cancel buttons
3561          * @type Object
3562          */
3563         OKCANCEL : {ok:true, cancel:true},
3564         /**
3565          * Button config that displays Yes, No and Cancel buttons
3566          * @type Object
3567          */
3568         YESNOCANCEL : {yes:true, no:true, cancel:true},
3569
3570         /**
3571          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3572          * @type Number
3573          */
3574         defaultTextHeight : 75,
3575         /**
3576          * The maximum width in pixels of the message box (defaults to 600)
3577          * @type Number
3578          */
3579         maxWidth : 600,
3580         /**
3581          * The minimum width in pixels of the message box (defaults to 100)
3582          * @type Number
3583          */
3584         minWidth : 100,
3585         /**
3586          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3587          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3588          * @type Number
3589          */
3590         minProgressWidth : 250,
3591         /**
3592          * An object containing the default button text strings that can be overriden for localized language support.
3593          * Supported properties are: ok, cancel, yes and no.
3594          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3595          * @type Object
3596          */
3597         buttonText : {
3598             ok : "OK",
3599             cancel : "Cancel",
3600             yes : "Yes",
3601             no : "No"
3602         }
3603     };
3604 }();
3605
3606 /**
3607  * Shorthand for {@link Roo.MessageBox}
3608  */
3609 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3610 Roo.Msg = Roo.Msg || Roo.MessageBox;
3611 /*
3612  * - LGPL
3613  *
3614  * navbar
3615  * 
3616  */
3617
3618 /**
3619  * @class Roo.bootstrap.Navbar
3620  * @extends Roo.bootstrap.Component
3621  * Bootstrap Navbar class
3622
3623  * @constructor
3624  * Create a new Navbar
3625  * @param {Object} config The config object
3626  */
3627
3628
3629 Roo.bootstrap.Navbar = function(config){
3630     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3631     this.addEvents({
3632         // raw events
3633         /**
3634          * @event beforetoggle
3635          * Fire before toggle the menu
3636          * @param {Roo.EventObject} e
3637          */
3638         "beforetoggle" : true
3639     });
3640 };
3641
3642 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3643     
3644     
3645    
3646     // private
3647     navItems : false,
3648     loadMask : false,
3649     
3650     
3651     getAutoCreate : function(){
3652         
3653         
3654         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3655         
3656     },
3657     
3658     initEvents :function ()
3659     {
3660         //Roo.log(this.el.select('.navbar-toggle',true));
3661         this.el.select('.navbar-toggle',true).on('click', function() {
3662             if(this.fireEvent('beforetoggle', this) !== false){
3663                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3664             }
3665             
3666         }, this);
3667         
3668         var mark = {
3669             tag: "div",
3670             cls:"x-dlg-mask"
3671         };
3672         
3673         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3674         
3675         var size = this.el.getSize();
3676         this.maskEl.setSize(size.width, size.height);
3677         this.maskEl.enableDisplayMode("block");
3678         this.maskEl.hide();
3679         
3680         if(this.loadMask){
3681             this.maskEl.show();
3682         }
3683     },
3684     
3685     
3686     getChildContainer : function()
3687     {
3688         if (this.el.select('.collapse').getCount()) {
3689             return this.el.select('.collapse',true).first();
3690         }
3691         
3692         return this.el;
3693     },
3694     
3695     mask : function()
3696     {
3697         this.maskEl.show();
3698     },
3699     
3700     unmask : function()
3701     {
3702         this.maskEl.hide();
3703     } 
3704     
3705     
3706     
3707     
3708 });
3709
3710
3711
3712  
3713
3714  /*
3715  * - LGPL
3716  *
3717  * navbar
3718  * 
3719  */
3720
3721 /**
3722  * @class Roo.bootstrap.NavSimplebar
3723  * @extends Roo.bootstrap.Navbar
3724  * Bootstrap Sidebar class
3725  *
3726  * @cfg {Boolean} inverse is inverted color
3727  * 
3728  * @cfg {String} type (nav | pills | tabs)
3729  * @cfg {Boolean} arrangement stacked | justified
3730  * @cfg {String} align (left | right) alignment
3731  * 
3732  * @cfg {Boolean} main (true|false) main nav bar? default false
3733  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3734  * 
3735  * @cfg {String} tag (header|footer|nav|div) default is nav 
3736
3737  * 
3738  * 
3739  * 
3740  * @constructor
3741  * Create a new Sidebar
3742  * @param {Object} config The config object
3743  */
3744
3745
3746 Roo.bootstrap.NavSimplebar = function(config){
3747     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3748 };
3749
3750 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3751     
3752     inverse: false,
3753     
3754     type: false,
3755     arrangement: '',
3756     align : false,
3757     
3758     
3759     
3760     main : false,
3761     
3762     
3763     tag : false,
3764     
3765     
3766     getAutoCreate : function(){
3767         
3768         
3769         var cfg = {
3770             tag : this.tag || 'div',
3771             cls : 'navbar'
3772         };
3773           
3774         
3775         cfg.cn = [
3776             {
3777                 cls: 'nav',
3778                 tag : 'ul'
3779             }
3780         ];
3781         
3782          
3783         this.type = this.type || 'nav';
3784         if (['tabs','pills'].indexOf(this.type)!==-1) {
3785             cfg.cn[0].cls += ' nav-' + this.type
3786         
3787         
3788         } else {
3789             if (this.type!=='nav') {
3790                 Roo.log('nav type must be nav/tabs/pills')
3791             }
3792             cfg.cn[0].cls += ' navbar-nav'
3793         }
3794         
3795         
3796         
3797         
3798         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3799             cfg.cn[0].cls += ' nav-' + this.arrangement;
3800         }
3801         
3802         
3803         if (this.align === 'right') {
3804             cfg.cn[0].cls += ' navbar-right';
3805         }
3806         
3807         if (this.inverse) {
3808             cfg.cls += ' navbar-inverse';
3809             
3810         }
3811         
3812         
3813         return cfg;
3814     
3815         
3816     }
3817     
3818     
3819     
3820 });
3821
3822
3823
3824  
3825
3826  
3827        /*
3828  * - LGPL
3829  *
3830  * navbar
3831  * 
3832  */
3833
3834 /**
3835  * @class Roo.bootstrap.NavHeaderbar
3836  * @extends Roo.bootstrap.NavSimplebar
3837  * Bootstrap Sidebar class
3838  *
3839  * @cfg {String} brand what is brand
3840  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3841  * @cfg {String} brand_href href of the brand
3842  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3843  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3844  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3845  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3846  * 
3847  * @constructor
3848  * Create a new Sidebar
3849  * @param {Object} config The config object
3850  */
3851
3852
3853 Roo.bootstrap.NavHeaderbar = function(config){
3854     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3855       
3856 };
3857
3858 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3859     
3860     position: '',
3861     brand: '',
3862     brand_href: false,
3863     srButton : true,
3864     autohide : false,
3865     desktopCenter : false,
3866    
3867     
3868     getAutoCreate : function(){
3869         
3870         var   cfg = {
3871             tag: this.nav || 'nav',
3872             cls: 'navbar',
3873             role: 'navigation',
3874             cn: []
3875         };
3876         
3877         var cn = cfg.cn;
3878         if (this.desktopCenter) {
3879             cn.push({cls : 'container', cn : []});
3880             cn = cn[0].cn;
3881         }
3882         
3883         if(this.srButton){
3884             cn.push({
3885                 tag: 'div',
3886                 cls: 'navbar-header',
3887                 cn: [
3888                     {
3889                         tag: 'button',
3890                         type: 'button',
3891                         cls: 'navbar-toggle',
3892                         'data-toggle': 'collapse',
3893                         cn: [
3894                             {
3895                                 tag: 'span',
3896                                 cls: 'sr-only',
3897                                 html: 'Toggle navigation'
3898                             },
3899                             {
3900                                 tag: 'span',
3901                                 cls: 'icon-bar'
3902                             },
3903                             {
3904                                 tag: 'span',
3905                                 cls: 'icon-bar'
3906                             },
3907                             {
3908                                 tag: 'span',
3909                                 cls: 'icon-bar'
3910                             }
3911                         ]
3912                     }
3913                 ]
3914             });
3915         }
3916         
3917         cn.push({
3918             tag: 'div',
3919             cls: 'collapse navbar-collapse',
3920             cn : []
3921         });
3922         
3923         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3924         
3925         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3926             cfg.cls += ' navbar-' + this.position;
3927             
3928             // tag can override this..
3929             
3930             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3931         }
3932         
3933         if (this.brand !== '') {
3934             cn[0].cn.push({
3935                 tag: 'a',
3936                 href: this.brand_href ? this.brand_href : '#',
3937                 cls: 'navbar-brand',
3938                 cn: [
3939                 this.brand
3940                 ]
3941             });
3942         }
3943         
3944         if(this.main){
3945             cfg.cls += ' main-nav';
3946         }
3947         
3948         
3949         return cfg;
3950
3951         
3952     },
3953     getHeaderChildContainer : function()
3954     {
3955         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3956             return this.el.select('.navbar-header',true).first();
3957         }
3958         
3959         return this.getChildContainer();
3960     },
3961     
3962     
3963     initEvents : function()
3964     {
3965         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3966         
3967         if (this.autohide) {
3968             
3969             var prevScroll = 0;
3970             var ft = this.el;
3971             
3972             Roo.get(document).on('scroll',function(e) {
3973                 var ns = Roo.get(document).getScroll().top;
3974                 var os = prevScroll;
3975                 prevScroll = ns;
3976                 
3977                 if(ns > os){
3978                     ft.removeClass('slideDown');
3979                     ft.addClass('slideUp');
3980                     return;
3981                 }
3982                 ft.removeClass('slideUp');
3983                 ft.addClass('slideDown');
3984                  
3985               
3986           },this);
3987         }
3988     }    
3989     
3990 });
3991
3992
3993
3994  
3995
3996  /*
3997  * - LGPL
3998  *
3999  * navbar
4000  * 
4001  */
4002
4003 /**
4004  * @class Roo.bootstrap.NavSidebar
4005  * @extends Roo.bootstrap.Navbar
4006  * Bootstrap Sidebar class
4007  * 
4008  * @constructor
4009  * Create a new Sidebar
4010  * @param {Object} config The config object
4011  */
4012
4013
4014 Roo.bootstrap.NavSidebar = function(config){
4015     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4016 };
4017
4018 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4019     
4020     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4021     
4022     getAutoCreate : function(){
4023         
4024         
4025         return  {
4026             tag: 'div',
4027             cls: 'sidebar sidebar-nav'
4028         };
4029     
4030         
4031     }
4032     
4033     
4034     
4035 });
4036
4037
4038
4039  
4040
4041  /*
4042  * - LGPL
4043  *
4044  * nav group
4045  * 
4046  */
4047
4048 /**
4049  * @class Roo.bootstrap.NavGroup
4050  * @extends Roo.bootstrap.Component
4051  * Bootstrap NavGroup class
4052  * @cfg {String} align (left|right)
4053  * @cfg {Boolean} inverse
4054  * @cfg {String} type (nav|pills|tab) default nav
4055  * @cfg {String} navId - reference Id for navbar.
4056
4057  * 
4058  * @constructor
4059  * Create a new nav group
4060  * @param {Object} config The config object
4061  */
4062
4063 Roo.bootstrap.NavGroup = function(config){
4064     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4065     this.navItems = [];
4066    
4067     Roo.bootstrap.NavGroup.register(this);
4068      this.addEvents({
4069         /**
4070              * @event changed
4071              * Fires when the active item changes
4072              * @param {Roo.bootstrap.NavGroup} this
4073              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4074              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4075          */
4076         'changed': true
4077      });
4078     
4079 };
4080
4081 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4082     
4083     align: '',
4084     inverse: false,
4085     form: false,
4086     type: 'nav',
4087     navId : '',
4088     // private
4089     
4090     navItems : false, 
4091     
4092     getAutoCreate : function()
4093     {
4094         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4095         
4096         cfg = {
4097             tag : 'ul',
4098             cls: 'nav' 
4099         };
4100         
4101         if (['tabs','pills'].indexOf(this.type)!==-1) {
4102             cfg.cls += ' nav-' + this.type
4103         } else {
4104             if (this.type!=='nav') {
4105                 Roo.log('nav type must be nav/tabs/pills')
4106             }
4107             cfg.cls += ' navbar-nav'
4108         }
4109         
4110         if (this.parent() && this.parent().sidebar) {
4111             cfg = {
4112                 tag: 'ul',
4113                 cls: 'dashboard-menu sidebar-menu'
4114             };
4115             
4116             return cfg;
4117         }
4118         
4119         if (this.form === true) {
4120             cfg = {
4121                 tag: 'form',
4122                 cls: 'navbar-form'
4123             };
4124             
4125             if (this.align === 'right') {
4126                 cfg.cls += ' navbar-right';
4127             } else {
4128                 cfg.cls += ' navbar-left';
4129             }
4130         }
4131         
4132         if (this.align === 'right') {
4133             cfg.cls += ' navbar-right';
4134         }
4135         
4136         if (this.inverse) {
4137             cfg.cls += ' navbar-inverse';
4138             
4139         }
4140         
4141         
4142         return cfg;
4143     },
4144     /**
4145     * sets the active Navigation item
4146     * @param {Roo.bootstrap.NavItem} the new current navitem
4147     */
4148     setActiveItem : function(item)
4149     {
4150         var prev = false;
4151         Roo.each(this.navItems, function(v){
4152             if (v == item) {
4153                 return ;
4154             }
4155             if (v.isActive()) {
4156                 v.setActive(false, true);
4157                 prev = v;
4158                 
4159             }
4160             
4161         });
4162
4163         item.setActive(true, true);
4164         this.fireEvent('changed', this, item, prev);
4165         
4166         
4167     },
4168     /**
4169     * gets the active Navigation item
4170     * @return {Roo.bootstrap.NavItem} the current navitem
4171     */
4172     getActive : function()
4173     {
4174         
4175         var prev = false;
4176         Roo.each(this.navItems, function(v){
4177             
4178             if (v.isActive()) {
4179                 prev = v;
4180                 
4181             }
4182             
4183         });
4184         return prev;
4185     },
4186     
4187     indexOfNav : function()
4188     {
4189         
4190         var prev = false;
4191         Roo.each(this.navItems, function(v,i){
4192             
4193             if (v.isActive()) {
4194                 prev = i;
4195                 
4196             }
4197             
4198         });
4199         return prev;
4200     },
4201     /**
4202     * adds a Navigation item
4203     * @param {Roo.bootstrap.NavItem} the navitem to add
4204     */
4205     addItem : function(cfg)
4206     {
4207         var cn = new Roo.bootstrap.NavItem(cfg);
4208         this.register(cn);
4209         cn.parentId = this.id;
4210         cn.onRender(this.el, null);
4211         return cn;
4212     },
4213     /**
4214     * register a Navigation item
4215     * @param {Roo.bootstrap.NavItem} the navitem to add
4216     */
4217     register : function(item)
4218     {
4219         this.navItems.push( item);
4220         item.navId = this.navId;
4221     
4222     },
4223     
4224     /**
4225     * clear all the Navigation item
4226     */
4227    
4228     clearAll : function()
4229     {
4230         this.navItems = [];
4231         this.el.dom.innerHTML = '';
4232     },
4233     
4234     getNavItem: function(tabId)
4235     {
4236         var ret = false;
4237         Roo.each(this.navItems, function(e) {
4238             if (e.tabId == tabId) {
4239                ret =  e;
4240                return false;
4241             }
4242             return true;
4243             
4244         });
4245         return ret;
4246     },
4247     
4248     setActiveNext : function()
4249     {
4250         var i = this.indexOfNav(this.getActive());
4251         if (i > this.navItems.length) {
4252             return;
4253         }
4254         this.setActiveItem(this.navItems[i+1]);
4255     },
4256     setActivePrev : function()
4257     {
4258         var i = this.indexOfNav(this.getActive());
4259         if (i  < 1) {
4260             return;
4261         }
4262         this.setActiveItem(this.navItems[i-1]);
4263     },
4264     clearWasActive : function(except) {
4265         Roo.each(this.navItems, function(e) {
4266             if (e.tabId != except.tabId && e.was_active) {
4267                e.was_active = false;
4268                return false;
4269             }
4270             return true;
4271             
4272         });
4273     },
4274     getWasActive : function ()
4275     {
4276         var r = false;
4277         Roo.each(this.navItems, function(e) {
4278             if (e.was_active) {
4279                r = e;
4280                return false;
4281             }
4282             return true;
4283             
4284         });
4285         return r;
4286     }
4287     
4288     
4289 });
4290
4291  
4292 Roo.apply(Roo.bootstrap.NavGroup, {
4293     
4294     groups: {},
4295      /**
4296     * register a Navigation Group
4297     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4298     */
4299     register : function(navgrp)
4300     {
4301         this.groups[navgrp.navId] = navgrp;
4302         
4303     },
4304     /**
4305     * fetch a Navigation Group based on the navigation ID
4306     * @param {string} the navgroup to add
4307     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4308     */
4309     get: function(navId) {
4310         if (typeof(this.groups[navId]) == 'undefined') {
4311             return false;
4312             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4313         }
4314         return this.groups[navId] ;
4315     }
4316     
4317     
4318     
4319 });
4320
4321  /*
4322  * - LGPL
4323  *
4324  * row
4325  * 
4326  */
4327
4328 /**
4329  * @class Roo.bootstrap.NavItem
4330  * @extends Roo.bootstrap.Component
4331  * Bootstrap Navbar.NavItem class
4332  * @cfg {String} href  link to
4333  * @cfg {String} html content of button
4334  * @cfg {String} badge text inside badge
4335  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4336  * @cfg {String} glyphicon name of glyphicon
4337  * @cfg {String} icon name of font awesome icon
4338  * @cfg {Boolean} active Is item active
4339  * @cfg {Boolean} disabled Is item disabled
4340  
4341  * @cfg {Boolean} preventDefault (true | false) default false
4342  * @cfg {String} tabId the tab that this item activates.
4343  * @cfg {String} tagtype (a|span) render as a href or span?
4344  * @cfg {Boolean} animateRef (true|false) link to element default false  
4345   
4346  * @constructor
4347  * Create a new Navbar Item
4348  * @param {Object} config The config object
4349  */
4350 Roo.bootstrap.NavItem = function(config){
4351     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4352     this.addEvents({
4353         // raw events
4354         /**
4355          * @event click
4356          * The raw click event for the entire grid.
4357          * @param {Roo.EventObject} e
4358          */
4359         "click" : true,
4360          /**
4361             * @event changed
4362             * Fires when the active item active state changes
4363             * @param {Roo.bootstrap.NavItem} this
4364             * @param {boolean} state the new state
4365              
4366          */
4367         'changed': true,
4368         /**
4369             * @event scrollto
4370             * Fires when scroll to element
4371             * @param {Roo.bootstrap.NavItem} this
4372             * @param {Object} options
4373             * @param {Roo.EventObject} e
4374              
4375          */
4376         'scrollto': true
4377     });
4378    
4379 };
4380
4381 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4382     
4383     href: false,
4384     html: '',
4385     badge: '',
4386     icon: false,
4387     glyphicon: false,
4388     active: false,
4389     preventDefault : false,
4390     tabId : false,
4391     tagtype : 'a',
4392     disabled : false,
4393     animateRef : false,
4394     was_active : false,
4395     
4396     getAutoCreate : function(){
4397          
4398         var cfg = {
4399             tag: 'li',
4400             cls: 'nav-item'
4401             
4402         };
4403         
4404         if (this.active) {
4405             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4406         }
4407         if (this.disabled) {
4408             cfg.cls += ' disabled';
4409         }
4410         
4411         if (this.href || this.html || this.glyphicon || this.icon) {
4412             cfg.cn = [
4413                 {
4414                     tag: this.tagtype,
4415                     href : this.href || "#",
4416                     html: this.html || ''
4417                 }
4418             ];
4419             
4420             if (this.icon) {
4421                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4422             }
4423
4424             if(this.glyphicon) {
4425                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4426             }
4427             
4428             if (this.menu) {
4429                 
4430                 cfg.cn[0].html += " <span class='caret'></span>";
4431              
4432             }
4433             
4434             if (this.badge !== '') {
4435                  
4436                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4437             }
4438         }
4439         
4440         
4441         
4442         return cfg;
4443     },
4444     initEvents: function() 
4445     {
4446         if (typeof (this.menu) != 'undefined') {
4447             this.menu.parentType = this.xtype;
4448             this.menu.triggerEl = this.el;
4449             this.menu = this.addxtype(Roo.apply({}, this.menu));
4450         }
4451         
4452         this.el.select('a',true).on('click', this.onClick, this);
4453         
4454         if(this.tagtype == 'span'){
4455             this.el.select('span',true).on('click', this.onClick, this);
4456         }
4457        
4458         // at this point parent should be available..
4459         this.parent().register(this);
4460     },
4461     
4462     onClick : function(e)
4463     {
4464         if (e.getTarget('.dropdown-menu-item')) {
4465             // did you click on a menu itemm.... - then don't trigger onclick..
4466             return;
4467         }
4468         
4469         if(
4470                 this.preventDefault || 
4471                 this.href == '#' 
4472         ){
4473             Roo.log("NavItem - prevent Default?");
4474             e.preventDefault();
4475         }
4476         
4477         if (this.disabled) {
4478             return;
4479         }
4480         
4481         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4482         if (tg && tg.transition) {
4483             Roo.log("waiting for the transitionend");
4484             return;
4485         }
4486         
4487         
4488         
4489         //Roo.log("fire event clicked");
4490         if(this.fireEvent('click', this, e) === false){
4491             return;
4492         };
4493         
4494         if(this.tagtype == 'span'){
4495             return;
4496         }
4497         
4498         //Roo.log(this.href);
4499         var ael = this.el.select('a',true).first();
4500         //Roo.log(ael);
4501         
4502         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4503             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4504             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4505                 return; // ignore... - it's a 'hash' to another page.
4506             }
4507             Roo.log("NavItem - prevent Default?");
4508             e.preventDefault();
4509             this.scrollToElement(e);
4510         }
4511         
4512         
4513         var p =  this.parent();
4514    
4515         if (['tabs','pills'].indexOf(p.type)!==-1) {
4516             if (typeof(p.setActiveItem) !== 'undefined') {
4517                 p.setActiveItem(this);
4518             }
4519         }
4520         
4521         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4522         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4523             // remove the collapsed menu expand...
4524             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4525         }
4526     },
4527     
4528     isActive: function () {
4529         return this.active
4530     },
4531     setActive : function(state, fire, is_was_active)
4532     {
4533         if (this.active && !state && this.navId) {
4534             this.was_active = true;
4535             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4536             if (nv) {
4537                 nv.clearWasActive(this);
4538             }
4539             
4540         }
4541         this.active = state;
4542         
4543         if (!state ) {
4544             this.el.removeClass('active');
4545         } else if (!this.el.hasClass('active')) {
4546             this.el.addClass('active');
4547         }
4548         if (fire) {
4549             this.fireEvent('changed', this, state);
4550         }
4551         
4552         // show a panel if it's registered and related..
4553         
4554         if (!this.navId || !this.tabId || !state || is_was_active) {
4555             return;
4556         }
4557         
4558         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4559         if (!tg) {
4560             return;
4561         }
4562         var pan = tg.getPanelByName(this.tabId);
4563         if (!pan) {
4564             return;
4565         }
4566         // if we can not flip to new panel - go back to old nav highlight..
4567         if (false == tg.showPanel(pan)) {
4568             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4569             if (nv) {
4570                 var onav = nv.getWasActive();
4571                 if (onav) {
4572                     onav.setActive(true, false, true);
4573                 }
4574             }
4575             
4576         }
4577         
4578         
4579         
4580     },
4581      // this should not be here...
4582     setDisabled : function(state)
4583     {
4584         this.disabled = state;
4585         if (!state ) {
4586             this.el.removeClass('disabled');
4587         } else if (!this.el.hasClass('disabled')) {
4588             this.el.addClass('disabled');
4589         }
4590         
4591     },
4592     
4593     /**
4594      * Fetch the element to display the tooltip on.
4595      * @return {Roo.Element} defaults to this.el
4596      */
4597     tooltipEl : function()
4598     {
4599         return this.el.select('' + this.tagtype + '', true).first();
4600     },
4601     
4602     scrollToElement : function(e)
4603     {
4604         var c = document.body;
4605         
4606         /*
4607          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4608          */
4609         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4610             c = document.documentElement;
4611         }
4612         
4613         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4614         
4615         if(!target){
4616             return;
4617         }
4618
4619         var o = target.calcOffsetsTo(c);
4620         
4621         var options = {
4622             target : target,
4623             value : o[1]
4624         };
4625         
4626         this.fireEvent('scrollto', this, options, e);
4627         
4628         Roo.get(c).scrollTo('top', options.value, true);
4629         
4630         return;
4631     }
4632 });
4633  
4634
4635  /*
4636  * - LGPL
4637  *
4638  * sidebar item
4639  *
4640  *  li
4641  *    <span> icon </span>
4642  *    <span> text </span>
4643  *    <span>badge </span>
4644  */
4645
4646 /**
4647  * @class Roo.bootstrap.NavSidebarItem
4648  * @extends Roo.bootstrap.NavItem
4649  * Bootstrap Navbar.NavSidebarItem class
4650  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4651  * {Boolean} open is the menu open
4652  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4653  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4654  * {String} buttonSize (sm|md|lg)the extra classes for the button
4655  * {Boolean} showArrow show arrow next to the text (default true)
4656  * @constructor
4657  * Create a new Navbar Button
4658  * @param {Object} config The config object
4659  */
4660 Roo.bootstrap.NavSidebarItem = function(config){
4661     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4662     this.addEvents({
4663         // raw events
4664         /**
4665          * @event click
4666          * The raw click event for the entire grid.
4667          * @param {Roo.EventObject} e
4668          */
4669         "click" : true,
4670          /**
4671             * @event changed
4672             * Fires when the active item active state changes
4673             * @param {Roo.bootstrap.NavSidebarItem} this
4674             * @param {boolean} state the new state
4675              
4676          */
4677         'changed': true
4678     });
4679    
4680 };
4681
4682 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4683     
4684     badgeWeight : 'default',
4685     
4686     open: false,
4687     
4688     buttonView : false,
4689     
4690     buttonWeight : 'default',
4691     
4692     buttonSize : 'md',
4693     
4694     showArrow : true,
4695     
4696     getAutoCreate : function(){
4697         
4698         
4699         var a = {
4700                 tag: 'a',
4701                 href : this.href || '#',
4702                 cls: '',
4703                 html : '',
4704                 cn : []
4705         };
4706         
4707         if(this.buttonView){
4708             a = {
4709                 tag: 'button',
4710                 href : this.href || '#',
4711                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4712                 html : this.html,
4713                 cn : []
4714             };
4715         }
4716         
4717         var cfg = {
4718             tag: 'li',
4719             cls: '',
4720             cn: [ a ]
4721         };
4722         
4723         if (this.active) {
4724             cfg.cls += ' active';
4725         }
4726         
4727         if (this.disabled) {
4728             cfg.cls += ' disabled';
4729         }
4730         if (this.open) {
4731             cfg.cls += ' open x-open';
4732         }
4733         // left icon..
4734         if (this.glyphicon || this.icon) {
4735             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4736             a.cn.push({ tag : 'i', cls : c }) ;
4737         }
4738         
4739         if(!this.buttonView){
4740             var span = {
4741                 tag: 'span',
4742                 html : this.html || ''
4743             };
4744
4745             a.cn.push(span);
4746             
4747         }
4748         
4749         if (this.badge !== '') {
4750             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4751         }
4752         
4753         if (this.menu) {
4754             
4755             if(this.showArrow){
4756                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4757             }
4758             
4759             a.cls += ' dropdown-toggle treeview' ;
4760         }
4761         
4762         return cfg;
4763     },
4764     
4765     initEvents : function()
4766     { 
4767         if (typeof (this.menu) != 'undefined') {
4768             this.menu.parentType = this.xtype;
4769             this.menu.triggerEl = this.el;
4770             this.menu = this.addxtype(Roo.apply({}, this.menu));
4771         }
4772         
4773         this.el.on('click', this.onClick, this);
4774         
4775         if(this.badge !== ''){
4776             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4777         }
4778         
4779     },
4780     
4781     onClick : function(e)
4782     {
4783         if(this.disabled){
4784             e.preventDefault();
4785             return;
4786         }
4787         
4788         if(this.preventDefault){
4789             e.preventDefault();
4790         }
4791         
4792         this.fireEvent('click', this);
4793     },
4794     
4795     disable : function()
4796     {
4797         this.setDisabled(true);
4798     },
4799     
4800     enable : function()
4801     {
4802         this.setDisabled(false);
4803     },
4804     
4805     setDisabled : function(state)
4806     {
4807         if(this.disabled == state){
4808             return;
4809         }
4810         
4811         this.disabled = state;
4812         
4813         if (state) {
4814             this.el.addClass('disabled');
4815             return;
4816         }
4817         
4818         this.el.removeClass('disabled');
4819         
4820         return;
4821     },
4822     
4823     setActive : function(state)
4824     {
4825         if(this.active == state){
4826             return;
4827         }
4828         
4829         this.active = state;
4830         
4831         if (state) {
4832             this.el.addClass('active');
4833             return;
4834         }
4835         
4836         this.el.removeClass('active');
4837         
4838         return;
4839     },
4840     
4841     isActive: function () 
4842     {
4843         return this.active;
4844     },
4845     
4846     setBadge : function(str)
4847     {
4848         if(!this.badgeEl){
4849             return;
4850         }
4851         
4852         this.badgeEl.dom.innerHTML = str;
4853     }
4854     
4855    
4856      
4857  
4858 });
4859  
4860
4861  /*
4862  * - LGPL
4863  *
4864  * row
4865  * 
4866  */
4867
4868 /**
4869  * @class Roo.bootstrap.Row
4870  * @extends Roo.bootstrap.Component
4871  * Bootstrap Row class (contains columns...)
4872  * 
4873  * @constructor
4874  * Create a new Row
4875  * @param {Object} config The config object
4876  */
4877
4878 Roo.bootstrap.Row = function(config){
4879     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4880 };
4881
4882 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4883     
4884     getAutoCreate : function(){
4885        return {
4886             cls: 'row clearfix'
4887        };
4888     }
4889     
4890     
4891 });
4892
4893  
4894
4895  /*
4896  * - LGPL
4897  *
4898  * element
4899  * 
4900  */
4901
4902 /**
4903  * @class Roo.bootstrap.Element
4904  * @extends Roo.bootstrap.Component
4905  * Bootstrap Element class
4906  * @cfg {String} html contents of the element
4907  * @cfg {String} tag tag of the element
4908  * @cfg {String} cls class of the element
4909  * @cfg {Boolean} preventDefault (true|false) default false
4910  * @cfg {Boolean} clickable (true|false) default false
4911  * 
4912  * @constructor
4913  * Create a new Element
4914  * @param {Object} config The config object
4915  */
4916
4917 Roo.bootstrap.Element = function(config){
4918     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4919     
4920     this.addEvents({
4921         // raw events
4922         /**
4923          * @event click
4924          * When a element is chick
4925          * @param {Roo.bootstrap.Element} this
4926          * @param {Roo.EventObject} e
4927          */
4928         "click" : true
4929     });
4930 };
4931
4932 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4933     
4934     tag: 'div',
4935     cls: '',
4936     html: '',
4937     preventDefault: false, 
4938     clickable: false,
4939     
4940     getAutoCreate : function(){
4941         
4942         var cfg = {
4943             tag: this.tag,
4944             // cls: this.cls, double assign in parent class Component.js :: onRender
4945             html: this.html
4946         };
4947         
4948         return cfg;
4949     },
4950     
4951     initEvents: function() 
4952     {
4953         Roo.bootstrap.Element.superclass.initEvents.call(this);
4954         
4955         if(this.clickable){
4956             this.el.on('click', this.onClick, this);
4957         }
4958         
4959     },
4960     
4961     onClick : function(e)
4962     {
4963         if(this.preventDefault){
4964             e.preventDefault();
4965         }
4966         
4967         this.fireEvent('click', this, e);
4968     },
4969     
4970     getValue : function()
4971     {
4972         return this.el.dom.innerHTML;
4973     },
4974     
4975     setValue : function(value)
4976     {
4977         this.el.dom.innerHTML = value;
4978     }
4979    
4980 });
4981
4982  
4983
4984  /*
4985  * - LGPL
4986  *
4987  * pagination
4988  * 
4989  */
4990
4991 /**
4992  * @class Roo.bootstrap.Pagination
4993  * @extends Roo.bootstrap.Component
4994  * Bootstrap Pagination class
4995  * @cfg {String} size xs | sm | md | lg
4996  * @cfg {Boolean} inverse false | true
4997  * 
4998  * @constructor
4999  * Create a new Pagination
5000  * @param {Object} config The config object
5001  */
5002
5003 Roo.bootstrap.Pagination = function(config){
5004     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5005 };
5006
5007 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5008     
5009     cls: false,
5010     size: false,
5011     inverse: false,
5012     
5013     getAutoCreate : function(){
5014         var cfg = {
5015             tag: 'ul',
5016                 cls: 'pagination'
5017         };
5018         if (this.inverse) {
5019             cfg.cls += ' inverse';
5020         }
5021         if (this.html) {
5022             cfg.html=this.html;
5023         }
5024         if (this.cls) {
5025             cfg.cls += " " + this.cls;
5026         }
5027         return cfg;
5028     }
5029    
5030 });
5031
5032  
5033
5034  /*
5035  * - LGPL
5036  *
5037  * Pagination item
5038  * 
5039  */
5040
5041
5042 /**
5043  * @class Roo.bootstrap.PaginationItem
5044  * @extends Roo.bootstrap.Component
5045  * Bootstrap PaginationItem class
5046  * @cfg {String} html text
5047  * @cfg {String} href the link
5048  * @cfg {Boolean} preventDefault (true | false) default true
5049  * @cfg {Boolean} active (true | false) default false
5050  * @cfg {Boolean} disabled default false
5051  * 
5052  * 
5053  * @constructor
5054  * Create a new PaginationItem
5055  * @param {Object} config The config object
5056  */
5057
5058
5059 Roo.bootstrap.PaginationItem = function(config){
5060     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5061     this.addEvents({
5062         // raw events
5063         /**
5064          * @event click
5065          * The raw click event for the entire grid.
5066          * @param {Roo.EventObject} e
5067          */
5068         "click" : true
5069     });
5070 };
5071
5072 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5073     
5074     href : false,
5075     html : false,
5076     preventDefault: true,
5077     active : false,
5078     cls : false,
5079     disabled: false,
5080     
5081     getAutoCreate : function(){
5082         var cfg= {
5083             tag: 'li',
5084             cn: [
5085                 {
5086                     tag : 'a',
5087                     href : this.href ? this.href : '#',
5088                     html : this.html ? this.html : ''
5089                 }
5090             ]
5091         };
5092         
5093         if(this.cls){
5094             cfg.cls = this.cls;
5095         }
5096         
5097         if(this.disabled){
5098             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5099         }
5100         
5101         if(this.active){
5102             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5103         }
5104         
5105         return cfg;
5106     },
5107     
5108     initEvents: function() {
5109         
5110         this.el.on('click', this.onClick, this);
5111         
5112     },
5113     onClick : function(e)
5114     {
5115         Roo.log('PaginationItem on click ');
5116         if(this.preventDefault){
5117             e.preventDefault();
5118         }
5119         
5120         if(this.disabled){
5121             return;
5122         }
5123         
5124         this.fireEvent('click', this, e);
5125     }
5126    
5127 });
5128
5129  
5130
5131  /*
5132  * - LGPL
5133  *
5134  * slider
5135  * 
5136  */
5137
5138
5139 /**
5140  * @class Roo.bootstrap.Slider
5141  * @extends Roo.bootstrap.Component
5142  * Bootstrap Slider class
5143  *    
5144  * @constructor
5145  * Create a new Slider
5146  * @param {Object} config The config object
5147  */
5148
5149 Roo.bootstrap.Slider = function(config){
5150     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5151 };
5152
5153 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5154     
5155     getAutoCreate : function(){
5156         
5157         var cfg = {
5158             tag: 'div',
5159             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5160             cn: [
5161                 {
5162                     tag: 'a',
5163                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5164                 }
5165             ]
5166         };
5167         
5168         return cfg;
5169     }
5170    
5171 });
5172
5173  /*
5174  * Based on:
5175  * Ext JS Library 1.1.1
5176  * Copyright(c) 2006-2007, Ext JS, LLC.
5177  *
5178  * Originally Released Under LGPL - original licence link has changed is not relivant.
5179  *
5180  * Fork - LGPL
5181  * <script type="text/javascript">
5182  */
5183  
5184
5185 /**
5186  * @class Roo.grid.ColumnModel
5187  * @extends Roo.util.Observable
5188  * This is the default implementation of a ColumnModel used by the Grid. It defines
5189  * the columns in the grid.
5190  * <br>Usage:<br>
5191  <pre><code>
5192  var colModel = new Roo.grid.ColumnModel([
5193         {header: "Ticker", width: 60, sortable: true, locked: true},
5194         {header: "Company Name", width: 150, sortable: true},
5195         {header: "Market Cap.", width: 100, sortable: true},
5196         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5197         {header: "Employees", width: 100, sortable: true, resizable: false}
5198  ]);
5199  </code></pre>
5200  * <p>
5201  
5202  * The config options listed for this class are options which may appear in each
5203  * individual column definition.
5204  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5205  * @constructor
5206  * @param {Object} config An Array of column config objects. See this class's
5207  * config objects for details.
5208 */
5209 Roo.grid.ColumnModel = function(config){
5210         /**
5211      * The config passed into the constructor
5212      */
5213     this.config = config;
5214     this.lookup = {};
5215
5216     // if no id, create one
5217     // if the column does not have a dataIndex mapping,
5218     // map it to the order it is in the config
5219     for(var i = 0, len = config.length; i < len; i++){
5220         var c = config[i];
5221         if(typeof c.dataIndex == "undefined"){
5222             c.dataIndex = i;
5223         }
5224         if(typeof c.renderer == "string"){
5225             c.renderer = Roo.util.Format[c.renderer];
5226         }
5227         if(typeof c.id == "undefined"){
5228             c.id = Roo.id();
5229         }
5230         if(c.editor && c.editor.xtype){
5231             c.editor  = Roo.factory(c.editor, Roo.grid);
5232         }
5233         if(c.editor && c.editor.isFormField){
5234             c.editor = new Roo.grid.GridEditor(c.editor);
5235         }
5236         this.lookup[c.id] = c;
5237     }
5238
5239     /**
5240      * The width of columns which have no width specified (defaults to 100)
5241      * @type Number
5242      */
5243     this.defaultWidth = 100;
5244
5245     /**
5246      * Default sortable of columns which have no sortable specified (defaults to false)
5247      * @type Boolean
5248      */
5249     this.defaultSortable = false;
5250
5251     this.addEvents({
5252         /**
5253              * @event widthchange
5254              * Fires when the width of a column changes.
5255              * @param {ColumnModel} this
5256              * @param {Number} columnIndex The column index
5257              * @param {Number} newWidth The new width
5258              */
5259             "widthchange": true,
5260         /**
5261              * @event headerchange
5262              * Fires when the text of a header changes.
5263              * @param {ColumnModel} this
5264              * @param {Number} columnIndex The column index
5265              * @param {Number} newText The new header text
5266              */
5267             "headerchange": true,
5268         /**
5269              * @event hiddenchange
5270              * Fires when a column is hidden or "unhidden".
5271              * @param {ColumnModel} this
5272              * @param {Number} columnIndex The column index
5273              * @param {Boolean} hidden true if hidden, false otherwise
5274              */
5275             "hiddenchange": true,
5276             /**
5277          * @event columnmoved
5278          * Fires when a column is moved.
5279          * @param {ColumnModel} this
5280          * @param {Number} oldIndex
5281          * @param {Number} newIndex
5282          */
5283         "columnmoved" : true,
5284         /**
5285          * @event columlockchange
5286          * Fires when a column's locked state is changed
5287          * @param {ColumnModel} this
5288          * @param {Number} colIndex
5289          * @param {Boolean} locked true if locked
5290          */
5291         "columnlockchange" : true
5292     });
5293     Roo.grid.ColumnModel.superclass.constructor.call(this);
5294 };
5295 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5296     /**
5297      * @cfg {String} header The header text to display in the Grid view.
5298      */
5299     /**
5300      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5301      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5302      * specified, the column's index is used as an index into the Record's data Array.
5303      */
5304     /**
5305      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5306      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5307      */
5308     /**
5309      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5310      * Defaults to the value of the {@link #defaultSortable} property.
5311      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5312      */
5313     /**
5314      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5315      */
5316     /**
5317      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5318      */
5319     /**
5320      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5321      */
5322     /**
5323      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5324      */
5325     /**
5326      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5327      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5328      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5329      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5330      */
5331        /**
5332      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5333      */
5334     /**
5335      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5336      */
5337     /**
5338      * @cfg {String} cursor (Optional)
5339      */
5340     /**
5341      * @cfg {String} tooltip (Optional)
5342      */
5343     /**
5344      * @cfg {Number} xs (Optional)
5345      */
5346     /**
5347      * @cfg {Number} sm (Optional)
5348      */
5349     /**
5350      * @cfg {Number} md (Optional)
5351      */
5352     /**
5353      * @cfg {Number} lg (Optional)
5354      */
5355     /**
5356      * Returns the id of the column at the specified index.
5357      * @param {Number} index The column index
5358      * @return {String} the id
5359      */
5360     getColumnId : function(index){
5361         return this.config[index].id;
5362     },
5363
5364     /**
5365      * Returns the column for a specified id.
5366      * @param {String} id The column id
5367      * @return {Object} the column
5368      */
5369     getColumnById : function(id){
5370         return this.lookup[id];
5371     },
5372
5373     
5374     /**
5375      * Returns the column for a specified dataIndex.
5376      * @param {String} dataIndex The column dataIndex
5377      * @return {Object|Boolean} the column or false if not found
5378      */
5379     getColumnByDataIndex: function(dataIndex){
5380         var index = this.findColumnIndex(dataIndex);
5381         return index > -1 ? this.config[index] : false;
5382     },
5383     
5384     /**
5385      * Returns the index for a specified column id.
5386      * @param {String} id The column id
5387      * @return {Number} the index, or -1 if not found
5388      */
5389     getIndexById : function(id){
5390         for(var i = 0, len = this.config.length; i < len; i++){
5391             if(this.config[i].id == id){
5392                 return i;
5393             }
5394         }
5395         return -1;
5396     },
5397     
5398     /**
5399      * Returns the index for a specified column dataIndex.
5400      * @param {String} dataIndex The column dataIndex
5401      * @return {Number} the index, or -1 if not found
5402      */
5403     
5404     findColumnIndex : function(dataIndex){
5405         for(var i = 0, len = this.config.length; i < len; i++){
5406             if(this.config[i].dataIndex == dataIndex){
5407                 return i;
5408             }
5409         }
5410         return -1;
5411     },
5412     
5413     
5414     moveColumn : function(oldIndex, newIndex){
5415         var c = this.config[oldIndex];
5416         this.config.splice(oldIndex, 1);
5417         this.config.splice(newIndex, 0, c);
5418         this.dataMap = null;
5419         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5420     },
5421
5422     isLocked : function(colIndex){
5423         return this.config[colIndex].locked === true;
5424     },
5425
5426     setLocked : function(colIndex, value, suppressEvent){
5427         if(this.isLocked(colIndex) == value){
5428             return;
5429         }
5430         this.config[colIndex].locked = value;
5431         if(!suppressEvent){
5432             this.fireEvent("columnlockchange", this, colIndex, value);
5433         }
5434     },
5435
5436     getTotalLockedWidth : function(){
5437         var totalWidth = 0;
5438         for(var i = 0; i < this.config.length; i++){
5439             if(this.isLocked(i) && !this.isHidden(i)){
5440                 this.totalWidth += this.getColumnWidth(i);
5441             }
5442         }
5443         return totalWidth;
5444     },
5445
5446     getLockedCount : function(){
5447         for(var i = 0, len = this.config.length; i < len; i++){
5448             if(!this.isLocked(i)){
5449                 return i;
5450             }
5451         }
5452         
5453         return this.config.length;
5454     },
5455
5456     /**
5457      * Returns the number of columns.
5458      * @return {Number}
5459      */
5460     getColumnCount : function(visibleOnly){
5461         if(visibleOnly === true){
5462             var c = 0;
5463             for(var i = 0, len = this.config.length; i < len; i++){
5464                 if(!this.isHidden(i)){
5465                     c++;
5466                 }
5467             }
5468             return c;
5469         }
5470         return this.config.length;
5471     },
5472
5473     /**
5474      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5475      * @param {Function} fn
5476      * @param {Object} scope (optional)
5477      * @return {Array} result
5478      */
5479     getColumnsBy : function(fn, scope){
5480         var r = [];
5481         for(var i = 0, len = this.config.length; i < len; i++){
5482             var c = this.config[i];
5483             if(fn.call(scope||this, c, i) === true){
5484                 r[r.length] = c;
5485             }
5486         }
5487         return r;
5488     },
5489
5490     /**
5491      * Returns true if the specified column is sortable.
5492      * @param {Number} col The column index
5493      * @return {Boolean}
5494      */
5495     isSortable : function(col){
5496         if(typeof this.config[col].sortable == "undefined"){
5497             return this.defaultSortable;
5498         }
5499         return this.config[col].sortable;
5500     },
5501
5502     /**
5503      * Returns the rendering (formatting) function defined for the column.
5504      * @param {Number} col The column index.
5505      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5506      */
5507     getRenderer : function(col){
5508         if(!this.config[col].renderer){
5509             return Roo.grid.ColumnModel.defaultRenderer;
5510         }
5511         return this.config[col].renderer;
5512     },
5513
5514     /**
5515      * Sets the rendering (formatting) function for a column.
5516      * @param {Number} col The column index
5517      * @param {Function} fn The function to use to process the cell's raw data
5518      * to return HTML markup for the grid view. The render function is called with
5519      * the following parameters:<ul>
5520      * <li>Data value.</li>
5521      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5522      * <li>css A CSS style string to apply to the table cell.</li>
5523      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5524      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5525      * <li>Row index</li>
5526      * <li>Column index</li>
5527      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5528      */
5529     setRenderer : function(col, fn){
5530         this.config[col].renderer = fn;
5531     },
5532
5533     /**
5534      * Returns the width for the specified column.
5535      * @param {Number} col The column index
5536      * @return {Number}
5537      */
5538     getColumnWidth : function(col){
5539         return this.config[col].width * 1 || this.defaultWidth;
5540     },
5541
5542     /**
5543      * Sets the width for a column.
5544      * @param {Number} col The column index
5545      * @param {Number} width The new width
5546      */
5547     setColumnWidth : function(col, width, suppressEvent){
5548         this.config[col].width = width;
5549         this.totalWidth = null;
5550         if(!suppressEvent){
5551              this.fireEvent("widthchange", this, col, width);
5552         }
5553     },
5554
5555     /**
5556      * Returns the total width of all columns.
5557      * @param {Boolean} includeHidden True to include hidden column widths
5558      * @return {Number}
5559      */
5560     getTotalWidth : function(includeHidden){
5561         if(!this.totalWidth){
5562             this.totalWidth = 0;
5563             for(var i = 0, len = this.config.length; i < len; i++){
5564                 if(includeHidden || !this.isHidden(i)){
5565                     this.totalWidth += this.getColumnWidth(i);
5566                 }
5567             }
5568         }
5569         return this.totalWidth;
5570     },
5571
5572     /**
5573      * Returns the header for the specified column.
5574      * @param {Number} col The column index
5575      * @return {String}
5576      */
5577     getColumnHeader : function(col){
5578         return this.config[col].header;
5579     },
5580
5581     /**
5582      * Sets the header for a column.
5583      * @param {Number} col The column index
5584      * @param {String} header The new header
5585      */
5586     setColumnHeader : function(col, header){
5587         this.config[col].header = header;
5588         this.fireEvent("headerchange", this, col, header);
5589     },
5590
5591     /**
5592      * Returns the tooltip for the specified column.
5593      * @param {Number} col The column index
5594      * @return {String}
5595      */
5596     getColumnTooltip : function(col){
5597             return this.config[col].tooltip;
5598     },
5599     /**
5600      * Sets the tooltip for a column.
5601      * @param {Number} col The column index
5602      * @param {String} tooltip The new tooltip
5603      */
5604     setColumnTooltip : function(col, tooltip){
5605             this.config[col].tooltip = tooltip;
5606     },
5607
5608     /**
5609      * Returns the dataIndex for the specified column.
5610      * @param {Number} col The column index
5611      * @return {Number}
5612      */
5613     getDataIndex : function(col){
5614         return this.config[col].dataIndex;
5615     },
5616
5617     /**
5618      * Sets the dataIndex for a column.
5619      * @param {Number} col The column index
5620      * @param {Number} dataIndex The new dataIndex
5621      */
5622     setDataIndex : function(col, dataIndex){
5623         this.config[col].dataIndex = dataIndex;
5624     },
5625
5626     
5627     
5628     /**
5629      * Returns true if the cell is editable.
5630      * @param {Number} colIndex The column index
5631      * @param {Number} rowIndex The row index - this is nto actually used..?
5632      * @return {Boolean}
5633      */
5634     isCellEditable : function(colIndex, rowIndex){
5635         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5636     },
5637
5638     /**
5639      * Returns the editor defined for the cell/column.
5640      * return false or null to disable editing.
5641      * @param {Number} colIndex The column index
5642      * @param {Number} rowIndex The row index
5643      * @return {Object}
5644      */
5645     getCellEditor : function(colIndex, rowIndex){
5646         return this.config[colIndex].editor;
5647     },
5648
5649     /**
5650      * Sets if a column is editable.
5651      * @param {Number} col The column index
5652      * @param {Boolean} editable True if the column is editable
5653      */
5654     setEditable : function(col, editable){
5655         this.config[col].editable = editable;
5656     },
5657
5658
5659     /**
5660      * Returns true if the column is hidden.
5661      * @param {Number} colIndex The column index
5662      * @return {Boolean}
5663      */
5664     isHidden : function(colIndex){
5665         return this.config[colIndex].hidden;
5666     },
5667
5668
5669     /**
5670      * Returns true if the column width cannot be changed
5671      */
5672     isFixed : function(colIndex){
5673         return this.config[colIndex].fixed;
5674     },
5675
5676     /**
5677      * Returns true if the column can be resized
5678      * @return {Boolean}
5679      */
5680     isResizable : function(colIndex){
5681         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5682     },
5683     /**
5684      * Sets if a column is hidden.
5685      * @param {Number} colIndex The column index
5686      * @param {Boolean} hidden True if the column is hidden
5687      */
5688     setHidden : function(colIndex, hidden){
5689         this.config[colIndex].hidden = hidden;
5690         this.totalWidth = null;
5691         this.fireEvent("hiddenchange", this, colIndex, hidden);
5692     },
5693
5694     /**
5695      * Sets the editor for a column.
5696      * @param {Number} col The column index
5697      * @param {Object} editor The editor object
5698      */
5699     setEditor : function(col, editor){
5700         this.config[col].editor = editor;
5701     }
5702 });
5703
5704 Roo.grid.ColumnModel.defaultRenderer = function(value)
5705 {
5706     if(typeof value == "object") {
5707         return value;
5708     }
5709         if(typeof value == "string" && value.length < 1){
5710             return "&#160;";
5711         }
5712     
5713         return String.format("{0}", value);
5714 };
5715
5716 // Alias for backwards compatibility
5717 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5718 /*
5719  * Based on:
5720  * Ext JS Library 1.1.1
5721  * Copyright(c) 2006-2007, Ext JS, LLC.
5722  *
5723  * Originally Released Under LGPL - original licence link has changed is not relivant.
5724  *
5725  * Fork - LGPL
5726  * <script type="text/javascript">
5727  */
5728  
5729 /**
5730  * @class Roo.LoadMask
5731  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5732  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5733  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5734  * element's UpdateManager load indicator and will be destroyed after the initial load.
5735  * @constructor
5736  * Create a new LoadMask
5737  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5738  * @param {Object} config The config object
5739  */
5740 Roo.LoadMask = function(el, config){
5741     this.el = Roo.get(el);
5742     Roo.apply(this, config);
5743     if(this.store){
5744         this.store.on('beforeload', this.onBeforeLoad, this);
5745         this.store.on('load', this.onLoad, this);
5746         this.store.on('loadexception', this.onLoadException, this);
5747         this.removeMask = false;
5748     }else{
5749         var um = this.el.getUpdateManager();
5750         um.showLoadIndicator = false; // disable the default indicator
5751         um.on('beforeupdate', this.onBeforeLoad, this);
5752         um.on('update', this.onLoad, this);
5753         um.on('failure', this.onLoad, this);
5754         this.removeMask = true;
5755     }
5756 };
5757
5758 Roo.LoadMask.prototype = {
5759     /**
5760      * @cfg {Boolean} removeMask
5761      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5762      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5763      */
5764     /**
5765      * @cfg {String} msg
5766      * The text to display in a centered loading message box (defaults to 'Loading...')
5767      */
5768     msg : 'Loading...',
5769     /**
5770      * @cfg {String} msgCls
5771      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5772      */
5773     msgCls : 'x-mask-loading',
5774
5775     /**
5776      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5777      * @type Boolean
5778      */
5779     disabled: false,
5780
5781     /**
5782      * Disables the mask to prevent it from being displayed
5783      */
5784     disable : function(){
5785        this.disabled = true;
5786     },
5787
5788     /**
5789      * Enables the mask so that it can be displayed
5790      */
5791     enable : function(){
5792         this.disabled = false;
5793     },
5794     
5795     onLoadException : function()
5796     {
5797         Roo.log(arguments);
5798         
5799         if (typeof(arguments[3]) != 'undefined') {
5800             Roo.MessageBox.alert("Error loading",arguments[3]);
5801         } 
5802         /*
5803         try {
5804             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5805                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5806             }   
5807         } catch(e) {
5808             
5809         }
5810         */
5811     
5812         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5813     },
5814     // private
5815     onLoad : function()
5816     {
5817         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5818     },
5819
5820     // private
5821     onBeforeLoad : function(){
5822         if(!this.disabled){
5823             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5824         }
5825     },
5826
5827     // private
5828     destroy : function(){
5829         if(this.store){
5830             this.store.un('beforeload', this.onBeforeLoad, this);
5831             this.store.un('load', this.onLoad, this);
5832             this.store.un('loadexception', this.onLoadException, this);
5833         }else{
5834             var um = this.el.getUpdateManager();
5835             um.un('beforeupdate', this.onBeforeLoad, this);
5836             um.un('update', this.onLoad, this);
5837             um.un('failure', this.onLoad, this);
5838         }
5839     }
5840 };/*
5841  * - LGPL
5842  *
5843  * table
5844  * 
5845  */
5846
5847 /**
5848  * @class Roo.bootstrap.Table
5849  * @extends Roo.bootstrap.Component
5850  * Bootstrap Table class
5851  * @cfg {String} cls table class
5852  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5853  * @cfg {String} bgcolor Specifies the background color for a table
5854  * @cfg {Number} border Specifies whether the table cells should have borders or not
5855  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5856  * @cfg {Number} cellspacing Specifies the space between cells
5857  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5858  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5859  * @cfg {String} sortable Specifies that the table should be sortable
5860  * @cfg {String} summary Specifies a summary of the content of a table
5861  * @cfg {Number} width Specifies the width of a table
5862  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5863  * 
5864  * @cfg {boolean} striped Should the rows be alternative striped
5865  * @cfg {boolean} bordered Add borders to the table
5866  * @cfg {boolean} hover Add hover highlighting
5867  * @cfg {boolean} condensed Format condensed
5868  * @cfg {boolean} responsive Format condensed
5869  * @cfg {Boolean} loadMask (true|false) default false
5870  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5871  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5872  * @cfg {Boolean} rowSelection (true|false) default false
5873  * @cfg {Boolean} cellSelection (true|false) default false
5874  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5875  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5876  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5877  
5878  * 
5879  * @constructor
5880  * Create a new Table
5881  * @param {Object} config The config object
5882  */
5883
5884 Roo.bootstrap.Table = function(config){
5885     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5886     
5887   
5888     
5889     // BC...
5890     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5891     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5892     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5893     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5894     
5895     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5896     if (this.sm) {
5897         this.sm.grid = this;
5898         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5899         this.sm = this.selModel;
5900         this.sm.xmodule = this.xmodule || false;
5901     }
5902     
5903     if (this.cm && typeof(this.cm.config) == 'undefined') {
5904         this.colModel = new Roo.grid.ColumnModel(this.cm);
5905         this.cm = this.colModel;
5906         this.cm.xmodule = this.xmodule || false;
5907     }
5908     if (this.store) {
5909         this.store= Roo.factory(this.store, Roo.data);
5910         this.ds = this.store;
5911         this.ds.xmodule = this.xmodule || false;
5912          
5913     }
5914     if (this.footer && this.store) {
5915         this.footer.dataSource = this.ds;
5916         this.footer = Roo.factory(this.footer);
5917     }
5918     
5919     /** @private */
5920     this.addEvents({
5921         /**
5922          * @event cellclick
5923          * Fires when a cell is clicked
5924          * @param {Roo.bootstrap.Table} this
5925          * @param {Roo.Element} el
5926          * @param {Number} rowIndex
5927          * @param {Number} columnIndex
5928          * @param {Roo.EventObject} e
5929          */
5930         "cellclick" : true,
5931         /**
5932          * @event celldblclick
5933          * Fires when a cell is double clicked
5934          * @param {Roo.bootstrap.Table} this
5935          * @param {Roo.Element} el
5936          * @param {Number} rowIndex
5937          * @param {Number} columnIndex
5938          * @param {Roo.EventObject} e
5939          */
5940         "celldblclick" : true,
5941         /**
5942          * @event rowclick
5943          * Fires when a row is clicked
5944          * @param {Roo.bootstrap.Table} this
5945          * @param {Roo.Element} el
5946          * @param {Number} rowIndex
5947          * @param {Roo.EventObject} e
5948          */
5949         "rowclick" : true,
5950         /**
5951          * @event rowdblclick
5952          * Fires when a row is double clicked
5953          * @param {Roo.bootstrap.Table} this
5954          * @param {Roo.Element} el
5955          * @param {Number} rowIndex
5956          * @param {Roo.EventObject} e
5957          */
5958         "rowdblclick" : true,
5959         /**
5960          * @event mouseover
5961          * Fires when a mouseover occur
5962          * @param {Roo.bootstrap.Table} this
5963          * @param {Roo.Element} el
5964          * @param {Number} rowIndex
5965          * @param {Number} columnIndex
5966          * @param {Roo.EventObject} e
5967          */
5968         "mouseover" : true,
5969         /**
5970          * @event mouseout
5971          * Fires when a mouseout occur
5972          * @param {Roo.bootstrap.Table} this
5973          * @param {Roo.Element} el
5974          * @param {Number} rowIndex
5975          * @param {Number} columnIndex
5976          * @param {Roo.EventObject} e
5977          */
5978         "mouseout" : true,
5979         /**
5980          * @event rowclass
5981          * Fires when a row is rendered, so you can change add a style to it.
5982          * @param {Roo.bootstrap.Table} this
5983          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5984          */
5985         'rowclass' : true,
5986           /**
5987          * @event rowsrendered
5988          * Fires when all the  rows have been rendered
5989          * @param {Roo.bootstrap.Table} this
5990          */
5991         'rowsrendered' : true,
5992         /**
5993          * @event contextmenu
5994          * The raw contextmenu event for the entire grid.
5995          * @param {Roo.EventObject} e
5996          */
5997         "contextmenu" : true,
5998         /**
5999          * @event rowcontextmenu
6000          * Fires when a row is right clicked
6001          * @param {Roo.bootstrap.Table} this
6002          * @param {Number} rowIndex
6003          * @param {Roo.EventObject} e
6004          */
6005         "rowcontextmenu" : true,
6006         /**
6007          * @event cellcontextmenu
6008          * Fires when a cell is right clicked
6009          * @param {Roo.bootstrap.Table} this
6010          * @param {Number} rowIndex
6011          * @param {Number} cellIndex
6012          * @param {Roo.EventObject} e
6013          */
6014          "cellcontextmenu" : true,
6015          /**
6016          * @event headercontextmenu
6017          * Fires when a header is right clicked
6018          * @param {Roo.bootstrap.Table} this
6019          * @param {Number} columnIndex
6020          * @param {Roo.EventObject} e
6021          */
6022         "headercontextmenu" : true
6023     });
6024 };
6025
6026 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6027     
6028     cls: false,
6029     align: false,
6030     bgcolor: false,
6031     border: false,
6032     cellpadding: false,
6033     cellspacing: false,
6034     frame: false,
6035     rules: false,
6036     sortable: false,
6037     summary: false,
6038     width: false,
6039     striped : false,
6040     scrollBody : false,
6041     bordered: false,
6042     hover:  false,
6043     condensed : false,
6044     responsive : false,
6045     sm : false,
6046     cm : false,
6047     store : false,
6048     loadMask : false,
6049     footerShow : true,
6050     headerShow : true,
6051   
6052     rowSelection : false,
6053     cellSelection : false,
6054     layout : false,
6055     
6056     // Roo.Element - the tbody
6057     mainBody: false,
6058     // Roo.Element - thead element
6059     mainHead: false,
6060     
6061     container: false, // used by gridpanel...
6062     
6063     lazyLoad : false,
6064     
6065     CSS : Roo.util.CSS,
6066     
6067     getAutoCreate : function()
6068     {
6069         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6070         
6071         cfg = {
6072             tag: 'table',
6073             cls : 'table',
6074             cn : []
6075         };
6076         if (this.scrollBody) {
6077             cfg.cls += ' table-body-fixed';
6078         }    
6079         if (this.striped) {
6080             cfg.cls += ' table-striped';
6081         }
6082         
6083         if (this.hover) {
6084             cfg.cls += ' table-hover';
6085         }
6086         if (this.bordered) {
6087             cfg.cls += ' table-bordered';
6088         }
6089         if (this.condensed) {
6090             cfg.cls += ' table-condensed';
6091         }
6092         if (this.responsive) {
6093             cfg.cls += ' table-responsive';
6094         }
6095         
6096         if (this.cls) {
6097             cfg.cls+=  ' ' +this.cls;
6098         }
6099         
6100         // this lot should be simplifed...
6101         
6102         if (this.align) {
6103             cfg.align=this.align;
6104         }
6105         if (this.bgcolor) {
6106             cfg.bgcolor=this.bgcolor;
6107         }
6108         if (this.border) {
6109             cfg.border=this.border;
6110         }
6111         if (this.cellpadding) {
6112             cfg.cellpadding=this.cellpadding;
6113         }
6114         if (this.cellspacing) {
6115             cfg.cellspacing=this.cellspacing;
6116         }
6117         if (this.frame) {
6118             cfg.frame=this.frame;
6119         }
6120         if (this.rules) {
6121             cfg.rules=this.rules;
6122         }
6123         if (this.sortable) {
6124             cfg.sortable=this.sortable;
6125         }
6126         if (this.summary) {
6127             cfg.summary=this.summary;
6128         }
6129         if (this.width) {
6130             cfg.width=this.width;
6131         }
6132         if (this.layout) {
6133             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6134         }
6135         
6136         if(this.store || this.cm){
6137             if(this.headerShow){
6138                 cfg.cn.push(this.renderHeader());
6139             }
6140             
6141             cfg.cn.push(this.renderBody());
6142             
6143             if(this.footerShow){
6144                 cfg.cn.push(this.renderFooter());
6145             }
6146             // where does this come from?
6147             //cfg.cls+=  ' TableGrid';
6148         }
6149         
6150         return { cn : [ cfg ] };
6151     },
6152     
6153     initEvents : function()
6154     {   
6155         if(!this.store || !this.cm){
6156             return;
6157         }
6158         if (this.selModel) {
6159             this.selModel.initEvents();
6160         }
6161         
6162         
6163         //Roo.log('initEvents with ds!!!!');
6164         
6165         this.mainBody = this.el.select('tbody', true).first();
6166         this.mainHead = this.el.select('thead', true).first();
6167         
6168         
6169         
6170         
6171         var _this = this;
6172         
6173         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6174             e.on('click', _this.sort, _this);
6175         });
6176         
6177         this.mainBody.on("click", this.onClick, this);
6178         this.mainBody.on("dblclick", this.onDblClick, this);
6179         
6180         // why is this done????? = it breaks dialogs??
6181         //this.parent().el.setStyle('position', 'relative');
6182         
6183         
6184         if (this.footer) {
6185             this.footer.parentId = this.id;
6186             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6187             
6188             if(this.lazyLoad){
6189                 this.el.select('tfoot tr td').first().addClass('hide');
6190             }
6191         } 
6192         
6193         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6194         
6195         this.store.on('load', this.onLoad, this);
6196         this.store.on('beforeload', this.onBeforeLoad, this);
6197         this.store.on('update', this.onUpdate, this);
6198         this.store.on('add', this.onAdd, this);
6199         this.store.on("clear", this.clear, this);
6200         
6201         this.el.on("contextmenu", this.onContextMenu, this);
6202         
6203         this.mainBody.on('scroll', this.onBodyScroll, this);
6204         
6205         this.cm.on("headerchange", this.onHeaderChange, this);
6206         
6207         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6208         
6209     },
6210     
6211     onContextMenu : function(e, t)
6212     {
6213         this.processEvent("contextmenu", e);
6214     },
6215     
6216     processEvent : function(name, e)
6217     {
6218         if (name != 'touchstart' ) {
6219             this.fireEvent(name, e);    
6220         }
6221         
6222         var t = e.getTarget();
6223         
6224         var cell = Roo.get(t);
6225         
6226         if(!cell){
6227             return;
6228         }
6229         
6230         if(cell.findParent('tfoot', false, true)){
6231             return;
6232         }
6233         
6234         if(cell.findParent('thead', false, true)){
6235             
6236             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6237                 cell = Roo.get(t).findParent('th', false, true);
6238                 if (!cell) {
6239                     Roo.log("failed to find th in thead?");
6240                     Roo.log(e.getTarget());
6241                     return;
6242                 }
6243             }
6244             
6245             var cellIndex = cell.dom.cellIndex;
6246             
6247             var ename = name == 'touchstart' ? 'click' : name;
6248             this.fireEvent("header" + ename, this, cellIndex, e);
6249             
6250             return;
6251         }
6252         
6253         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6254             cell = Roo.get(t).findParent('td', false, true);
6255             if (!cell) {
6256                 Roo.log("failed to find th in tbody?");
6257                 Roo.log(e.getTarget());
6258                 return;
6259             }
6260         }
6261         
6262         var row = cell.findParent('tr', false, true);
6263         var cellIndex = cell.dom.cellIndex;
6264         var rowIndex = row.dom.rowIndex - 1;
6265         
6266         if(row !== false){
6267             
6268             this.fireEvent("row" + name, this, rowIndex, e);
6269             
6270             if(cell !== false){
6271             
6272                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6273             }
6274         }
6275         
6276     },
6277     
6278     onMouseover : function(e, el)
6279     {
6280         var cell = Roo.get(el);
6281         
6282         if(!cell){
6283             return;
6284         }
6285         
6286         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6287             cell = cell.findParent('td', false, true);
6288         }
6289         
6290         var row = cell.findParent('tr', false, true);
6291         var cellIndex = cell.dom.cellIndex;
6292         var rowIndex = row.dom.rowIndex - 1; // start from 0
6293         
6294         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6295         
6296     },
6297     
6298     onMouseout : function(e, el)
6299     {
6300         var cell = Roo.get(el);
6301         
6302         if(!cell){
6303             return;
6304         }
6305         
6306         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6307             cell = cell.findParent('td', false, true);
6308         }
6309         
6310         var row = cell.findParent('tr', false, true);
6311         var cellIndex = cell.dom.cellIndex;
6312         var rowIndex = row.dom.rowIndex - 1; // start from 0
6313         
6314         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6315         
6316     },
6317     
6318     onClick : function(e, el)
6319     {
6320         var cell = Roo.get(el);
6321         
6322         if(!cell || (!this.cellSelection && !this.rowSelection)){
6323             return;
6324         }
6325         
6326         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6327             cell = cell.findParent('td', false, true);
6328         }
6329         
6330         if(!cell || typeof(cell) == 'undefined'){
6331             return;
6332         }
6333         
6334         var row = cell.findParent('tr', false, true);
6335         
6336         if(!row || typeof(row) == 'undefined'){
6337             return;
6338         }
6339         
6340         var cellIndex = cell.dom.cellIndex;
6341         var rowIndex = this.getRowIndex(row);
6342         
6343         // why??? - should these not be based on SelectionModel?
6344         if(this.cellSelection){
6345             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6346         }
6347         
6348         if(this.rowSelection){
6349             this.fireEvent('rowclick', this, row, rowIndex, e);
6350         }
6351         
6352         
6353     },
6354         
6355     onDblClick : function(e,el)
6356     {
6357         var cell = Roo.get(el);
6358         
6359         if(!cell || (!this.cellSelection && !this.rowSelection)){
6360             return;
6361         }
6362         
6363         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6364             cell = cell.findParent('td', false, true);
6365         }
6366         
6367         if(!cell || typeof(cell) == 'undefined'){
6368             return;
6369         }
6370         
6371         var row = cell.findParent('tr', false, true);
6372         
6373         if(!row || typeof(row) == 'undefined'){
6374             return;
6375         }
6376         
6377         var cellIndex = cell.dom.cellIndex;
6378         var rowIndex = this.getRowIndex(row);
6379         
6380         if(this.cellSelection){
6381             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6382         }
6383         
6384         if(this.rowSelection){
6385             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6386         }
6387     },
6388     
6389     sort : function(e,el)
6390     {
6391         var col = Roo.get(el);
6392         
6393         if(!col.hasClass('sortable')){
6394             return;
6395         }
6396         
6397         var sort = col.attr('sort');
6398         var dir = 'ASC';
6399         
6400         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6401             dir = 'DESC';
6402         }
6403         
6404         this.store.sortInfo = {field : sort, direction : dir};
6405         
6406         if (this.footer) {
6407             Roo.log("calling footer first");
6408             this.footer.onClick('first');
6409         } else {
6410         
6411             this.store.load({ params : { start : 0 } });
6412         }
6413     },
6414     
6415     renderHeader : function()
6416     {
6417         var header = {
6418             tag: 'thead',
6419             cn : []
6420         };
6421         
6422         var cm = this.cm;
6423         this.totalWidth = 0;
6424         
6425         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6426             
6427             var config = cm.config[i];
6428             
6429             var c = {
6430                 tag: 'th',
6431                 cls : 'x-hcol-' + i,
6432                 style : '',
6433                 html: cm.getColumnHeader(i)
6434             };
6435             
6436             var hh = '';
6437             
6438             if(typeof(config.sortable) != 'undefined' && config.sortable){
6439                 c.cls = 'sortable';
6440                 c.html = '<i class="glyphicon"></i>' + c.html;
6441             }
6442             
6443             if(typeof(config.lgHeader) != 'undefined'){
6444                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6445             }
6446             
6447             if(typeof(config.mdHeader) != 'undefined'){
6448                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6449             }
6450             
6451             if(typeof(config.smHeader) != 'undefined'){
6452                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6453             }
6454             
6455             if(typeof(config.xsHeader) != 'undefined'){
6456                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6457             }
6458             
6459             if(hh.length){
6460                 c.html = hh;
6461             }
6462             
6463             if(typeof(config.tooltip) != 'undefined'){
6464                 c.tooltip = config.tooltip;
6465             }
6466             
6467             if(typeof(config.colspan) != 'undefined'){
6468                 c.colspan = config.colspan;
6469             }
6470             
6471             if(typeof(config.hidden) != 'undefined' && config.hidden){
6472                 c.style += ' display:none;';
6473             }
6474             
6475             if(typeof(config.dataIndex) != 'undefined'){
6476                 c.sort = config.dataIndex;
6477             }
6478             
6479            
6480             
6481             if(typeof(config.align) != 'undefined' && config.align.length){
6482                 c.style += ' text-align:' + config.align + ';';
6483             }
6484             
6485             if(typeof(config.width) != 'undefined'){
6486                 c.style += ' width:' + config.width + 'px;';
6487                 this.totalWidth += config.width;
6488             } else {
6489                 this.totalWidth += 100; // assume minimum of 100 per column?
6490             }
6491             
6492             if(typeof(config.cls) != 'undefined'){
6493                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6494             }
6495             
6496             ['xs','sm','md','lg'].map(function(size){
6497                 
6498                 if(typeof(config[size]) == 'undefined'){
6499                     return;
6500                 }
6501                 
6502                 if (!config[size]) { // 0 = hidden
6503                     c.cls += ' hidden-' + size;
6504                     return;
6505                 }
6506                 
6507                 c.cls += ' col-' + size + '-' + config[size];
6508
6509             });
6510             
6511             header.cn.push(c)
6512         }
6513         
6514         return header;
6515     },
6516     
6517     renderBody : function()
6518     {
6519         var body = {
6520             tag: 'tbody',
6521             cn : [
6522                 {
6523                     tag: 'tr',
6524                     cn : [
6525                         {
6526                             tag : 'td',
6527                             colspan :  this.cm.getColumnCount()
6528                         }
6529                     ]
6530                 }
6531             ]
6532         };
6533         
6534         return body;
6535     },
6536     
6537     renderFooter : function()
6538     {
6539         var footer = {
6540             tag: 'tfoot',
6541             cn : [
6542                 {
6543                     tag: 'tr',
6544                     cn : [
6545                         {
6546                             tag : 'td',
6547                             colspan :  this.cm.getColumnCount()
6548                         }
6549                     ]
6550                 }
6551             ]
6552         };
6553         
6554         return footer;
6555     },
6556     
6557     
6558     
6559     onLoad : function()
6560     {
6561 //        Roo.log('ds onload');
6562         this.clear();
6563         
6564         var _this = this;
6565         var cm = this.cm;
6566         var ds = this.store;
6567         
6568         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6569             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6570             if (_this.store.sortInfo) {
6571                     
6572                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6573                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6574                 }
6575                 
6576                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6577                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6578                 }
6579             }
6580         });
6581         
6582         var tbody =  this.mainBody;
6583               
6584         if(ds.getCount() > 0){
6585             ds.data.each(function(d,rowIndex){
6586                 var row =  this.renderRow(cm, ds, rowIndex);
6587                 
6588                 tbody.createChild(row);
6589                 
6590                 var _this = this;
6591                 
6592                 if(row.cellObjects.length){
6593                     Roo.each(row.cellObjects, function(r){
6594                         _this.renderCellObject(r);
6595                     })
6596                 }
6597                 
6598             }, this);
6599         }
6600         
6601         Roo.each(this.el.select('tbody td', true).elements, function(e){
6602             e.on('mouseover', _this.onMouseover, _this);
6603         });
6604         
6605         Roo.each(this.el.select('tbody td', true).elements, function(e){
6606             e.on('mouseout', _this.onMouseout, _this);
6607         });
6608         this.fireEvent('rowsrendered', this);
6609         //if(this.loadMask){
6610         //    this.maskEl.hide();
6611         //}
6612         
6613         this.autoSize();
6614     },
6615     
6616     
6617     onUpdate : function(ds,record)
6618     {
6619         this.refreshRow(record);
6620         this.autoSize();
6621     },
6622     
6623     onRemove : function(ds, record, index, isUpdate){
6624         if(isUpdate !== true){
6625             this.fireEvent("beforerowremoved", this, index, record);
6626         }
6627         var bt = this.mainBody.dom;
6628         
6629         var rows = this.el.select('tbody > tr', true).elements;
6630         
6631         if(typeof(rows[index]) != 'undefined'){
6632             bt.removeChild(rows[index].dom);
6633         }
6634         
6635 //        if(bt.rows[index]){
6636 //            bt.removeChild(bt.rows[index]);
6637 //        }
6638         
6639         if(isUpdate !== true){
6640             //this.stripeRows(index);
6641             //this.syncRowHeights(index, index);
6642             //this.layout();
6643             this.fireEvent("rowremoved", this, index, record);
6644         }
6645     },
6646     
6647     onAdd : function(ds, records, rowIndex)
6648     {
6649         //Roo.log('on Add called');
6650         // - note this does not handle multiple adding very well..
6651         var bt = this.mainBody.dom;
6652         for (var i =0 ; i < records.length;i++) {
6653             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6654             //Roo.log(records[i]);
6655             //Roo.log(this.store.getAt(rowIndex+i));
6656             this.insertRow(this.store, rowIndex + i, false);
6657             return;
6658         }
6659         
6660     },
6661     
6662     
6663     refreshRow : function(record){
6664         var ds = this.store, index;
6665         if(typeof record == 'number'){
6666             index = record;
6667             record = ds.getAt(index);
6668         }else{
6669             index = ds.indexOf(record);
6670         }
6671         this.insertRow(ds, index, true);
6672         this.autoSize();
6673         this.onRemove(ds, record, index+1, true);
6674         this.autoSize();
6675         //this.syncRowHeights(index, index);
6676         //this.layout();
6677         this.fireEvent("rowupdated", this, index, record);
6678     },
6679     
6680     insertRow : function(dm, rowIndex, isUpdate){
6681         
6682         if(!isUpdate){
6683             this.fireEvent("beforerowsinserted", this, rowIndex);
6684         }
6685             //var s = this.getScrollState();
6686         var row = this.renderRow(this.cm, this.store, rowIndex);
6687         // insert before rowIndex..
6688         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6689         
6690         var _this = this;
6691                 
6692         if(row.cellObjects.length){
6693             Roo.each(row.cellObjects, function(r){
6694                 _this.renderCellObject(r);
6695             })
6696         }
6697             
6698         if(!isUpdate){
6699             this.fireEvent("rowsinserted", this, rowIndex);
6700             //this.syncRowHeights(firstRow, lastRow);
6701             //this.stripeRows(firstRow);
6702             //this.layout();
6703         }
6704         
6705     },
6706     
6707     
6708     getRowDom : function(rowIndex)
6709     {
6710         var rows = this.el.select('tbody > tr', true).elements;
6711         
6712         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6713         
6714     },
6715     // returns the object tree for a tr..
6716   
6717     
6718     renderRow : function(cm, ds, rowIndex) 
6719     {
6720         var d = ds.getAt(rowIndex);
6721         
6722         var row = {
6723             tag : 'tr',
6724             cls : 'x-row-' + rowIndex,
6725             cn : []
6726         };
6727             
6728         var cellObjects = [];
6729         
6730         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6731             var config = cm.config[i];
6732             
6733             var renderer = cm.getRenderer(i);
6734             var value = '';
6735             var id = false;
6736             
6737             if(typeof(renderer) !== 'undefined'){
6738                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6739             }
6740             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6741             // and are rendered into the cells after the row is rendered - using the id for the element.
6742             
6743             if(typeof(value) === 'object'){
6744                 id = Roo.id();
6745                 cellObjects.push({
6746                     container : id,
6747                     cfg : value 
6748                 })
6749             }
6750             
6751             var rowcfg = {
6752                 record: d,
6753                 rowIndex : rowIndex,
6754                 colIndex : i,
6755                 rowClass : ''
6756             };
6757
6758             this.fireEvent('rowclass', this, rowcfg);
6759             
6760             var td = {
6761                 tag: 'td',
6762                 cls : rowcfg.rowClass + ' x-col-' + i,
6763                 style: '',
6764                 html: (typeof(value) === 'object') ? '' : value
6765             };
6766             
6767             if (id) {
6768                 td.id = id;
6769             }
6770             
6771             if(typeof(config.colspan) != 'undefined'){
6772                 td.colspan = config.colspan;
6773             }
6774             
6775             if(typeof(config.hidden) != 'undefined' && config.hidden){
6776                 td.style += ' display:none;';
6777             }
6778             
6779             if(typeof(config.align) != 'undefined' && config.align.length){
6780                 td.style += ' text-align:' + config.align + ';';
6781             }
6782             
6783             if(typeof(config.width) != 'undefined'){
6784                 td.style += ' width:' +  config.width + 'px;';
6785             }
6786             
6787             if(typeof(config.cursor) != 'undefined'){
6788                 td.style += ' cursor:' +  config.cursor + ';';
6789             }
6790             
6791             if(typeof(config.cls) != 'undefined'){
6792                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6793             }
6794             
6795             ['xs','sm','md','lg'].map(function(size){
6796                 
6797                 if(typeof(config[size]) == 'undefined'){
6798                     return;
6799                 }
6800                 
6801                 if (!config[size]) { // 0 = hidden
6802                     td.cls += ' hidden-' + size;
6803                     return;
6804                 }
6805                 
6806                 td.cls += ' col-' + size + '-' + config[size];
6807
6808             });
6809             
6810             row.cn.push(td);
6811            
6812         }
6813         
6814         row.cellObjects = cellObjects;
6815         
6816         return row;
6817           
6818     },
6819     
6820     
6821     
6822     onBeforeLoad : function()
6823     {
6824         //Roo.log('ds onBeforeLoad');
6825         
6826         //this.clear();
6827         
6828         //if(this.loadMask){
6829         //    this.maskEl.show();
6830         //}
6831     },
6832      /**
6833      * Remove all rows
6834      */
6835     clear : function()
6836     {
6837         this.el.select('tbody', true).first().dom.innerHTML = '';
6838     },
6839     /**
6840      * Show or hide a row.
6841      * @param {Number} rowIndex to show or hide
6842      * @param {Boolean} state hide
6843      */
6844     setRowVisibility : function(rowIndex, state)
6845     {
6846         var bt = this.mainBody.dom;
6847         
6848         var rows = this.el.select('tbody > tr', true).elements;
6849         
6850         if(typeof(rows[rowIndex]) == 'undefined'){
6851             return;
6852         }
6853         rows[rowIndex].dom.style.display = state ? '' : 'none';
6854     },
6855     
6856     
6857     getSelectionModel : function(){
6858         if(!this.selModel){
6859             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6860         }
6861         return this.selModel;
6862     },
6863     /*
6864      * Render the Roo.bootstrap object from renderder
6865      */
6866     renderCellObject : function(r)
6867     {
6868         var _this = this;
6869         
6870         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6871         
6872         var t = r.cfg.render(r.container);
6873         
6874         if(r.cfg.cn){
6875             Roo.each(r.cfg.cn, function(c){
6876                 var child = {
6877                     container: t.getChildContainer(),
6878                     cfg: c
6879                 };
6880                 _this.renderCellObject(child);
6881             })
6882         }
6883     },
6884     
6885     getRowIndex : function(row)
6886     {
6887         var rowIndex = -1;
6888         
6889         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6890             if(el != row){
6891                 return;
6892             }
6893             
6894             rowIndex = index;
6895         });
6896         
6897         return rowIndex;
6898     },
6899      /**
6900      * Returns the grid's underlying element = used by panel.Grid
6901      * @return {Element} The element
6902      */
6903     getGridEl : function(){
6904         return this.el;
6905     },
6906      /**
6907      * Forces a resize - used by panel.Grid
6908      * @return {Element} The element
6909      */
6910     autoSize : function()
6911     {
6912         //var ctr = Roo.get(this.container.dom.parentElement);
6913         var ctr = Roo.get(this.el.dom);
6914         
6915         var thd = this.getGridEl().select('thead',true).first();
6916         var tbd = this.getGridEl().select('tbody', true).first();
6917         var tfd = this.getGridEl().select('tfoot', true).first();
6918         
6919         var cw = ctr.getWidth();
6920         
6921         if (tbd) {
6922             
6923             tbd.setSize(ctr.getWidth(),
6924                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6925             );
6926             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6927             cw -= barsize;
6928         }
6929         cw = Math.max(cw, this.totalWidth);
6930         this.getGridEl().select('tr',true).setWidth(cw);
6931         // resize 'expandable coloumn?
6932         
6933         return; // we doe not have a view in this design..
6934         
6935     },
6936     onBodyScroll: function()
6937     {
6938         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6939         if(this.mainHead){
6940             this.mainHead.setStyle({
6941                 'position' : 'relative',
6942                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6943             });
6944         }
6945         
6946         if(this.lazyLoad){
6947             
6948             var scrollHeight = this.mainBody.dom.scrollHeight;
6949             
6950             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6951             
6952             var height = this.mainBody.getHeight();
6953             
6954             if(scrollHeight - height == scrollTop) {
6955                 
6956                 var total = this.ds.getTotalCount();
6957                 
6958                 if(this.footer.cursor + this.footer.pageSize < total){
6959                     
6960                     this.footer.ds.load({
6961                         params : {
6962                             start : this.footer.cursor + this.footer.pageSize,
6963                             limit : this.footer.pageSize
6964                         },
6965                         add : true
6966                     });
6967                 }
6968             }
6969             
6970         }
6971     },
6972     
6973     onHeaderChange : function()
6974     {
6975         var header = this.renderHeader();
6976         var table = this.el.select('table', true).first();
6977         
6978         this.mainHead.remove();
6979         this.mainHead = table.createChild(header, this.mainBody, false);
6980     },
6981     
6982     onHiddenChange : function(colModel, colIndex, hidden)
6983     {
6984         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6985         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6986         
6987         this.CSS.updateRule(thSelector, "display", "");
6988         this.CSS.updateRule(tdSelector, "display", "");
6989         
6990         if(hidden){
6991             this.CSS.updateRule(thSelector, "display", "none");
6992             this.CSS.updateRule(tdSelector, "display", "none");
6993         }
6994         
6995         this.onHeaderChange();
6996         this.onLoad();
6997         
6998     }
6999     
7000 });
7001
7002  
7003
7004  /*
7005  * - LGPL
7006  *
7007  * table cell
7008  * 
7009  */
7010
7011 /**
7012  * @class Roo.bootstrap.TableCell
7013  * @extends Roo.bootstrap.Component
7014  * Bootstrap TableCell class
7015  * @cfg {String} html cell contain text
7016  * @cfg {String} cls cell class
7017  * @cfg {String} tag cell tag (td|th) default td
7018  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7019  * @cfg {String} align Aligns the content in a cell
7020  * @cfg {String} axis Categorizes cells
7021  * @cfg {String} bgcolor Specifies the background color of a cell
7022  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7023  * @cfg {Number} colspan Specifies the number of columns a cell should span
7024  * @cfg {String} headers Specifies one or more header cells a cell is related to
7025  * @cfg {Number} height Sets the height of a cell
7026  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7027  * @cfg {Number} rowspan Sets the number of rows a cell should span
7028  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7029  * @cfg {String} valign Vertical aligns the content in a cell
7030  * @cfg {Number} width Specifies the width of a cell
7031  * 
7032  * @constructor
7033  * Create a new TableCell
7034  * @param {Object} config The config object
7035  */
7036
7037 Roo.bootstrap.TableCell = function(config){
7038     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7039 };
7040
7041 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7042     
7043     html: false,
7044     cls: false,
7045     tag: false,
7046     abbr: false,
7047     align: false,
7048     axis: false,
7049     bgcolor: false,
7050     charoff: false,
7051     colspan: false,
7052     headers: false,
7053     height: false,
7054     nowrap: false,
7055     rowspan: false,
7056     scope: false,
7057     valign: false,
7058     width: false,
7059     
7060     
7061     getAutoCreate : function(){
7062         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7063         
7064         cfg = {
7065             tag: 'td'
7066         };
7067         
7068         if(this.tag){
7069             cfg.tag = this.tag;
7070         }
7071         
7072         if (this.html) {
7073             cfg.html=this.html
7074         }
7075         if (this.cls) {
7076             cfg.cls=this.cls
7077         }
7078         if (this.abbr) {
7079             cfg.abbr=this.abbr
7080         }
7081         if (this.align) {
7082             cfg.align=this.align
7083         }
7084         if (this.axis) {
7085             cfg.axis=this.axis
7086         }
7087         if (this.bgcolor) {
7088             cfg.bgcolor=this.bgcolor
7089         }
7090         if (this.charoff) {
7091             cfg.charoff=this.charoff
7092         }
7093         if (this.colspan) {
7094             cfg.colspan=this.colspan
7095         }
7096         if (this.headers) {
7097             cfg.headers=this.headers
7098         }
7099         if (this.height) {
7100             cfg.height=this.height
7101         }
7102         if (this.nowrap) {
7103             cfg.nowrap=this.nowrap
7104         }
7105         if (this.rowspan) {
7106             cfg.rowspan=this.rowspan
7107         }
7108         if (this.scope) {
7109             cfg.scope=this.scope
7110         }
7111         if (this.valign) {
7112             cfg.valign=this.valign
7113         }
7114         if (this.width) {
7115             cfg.width=this.width
7116         }
7117         
7118         
7119         return cfg;
7120     }
7121    
7122 });
7123
7124  
7125
7126  /*
7127  * - LGPL
7128  *
7129  * table row
7130  * 
7131  */
7132
7133 /**
7134  * @class Roo.bootstrap.TableRow
7135  * @extends Roo.bootstrap.Component
7136  * Bootstrap TableRow class
7137  * @cfg {String} cls row class
7138  * @cfg {String} align Aligns the content in a table row
7139  * @cfg {String} bgcolor Specifies a background color for a table row
7140  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7141  * @cfg {String} valign Vertical aligns the content in a table row
7142  * 
7143  * @constructor
7144  * Create a new TableRow
7145  * @param {Object} config The config object
7146  */
7147
7148 Roo.bootstrap.TableRow = function(config){
7149     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7150 };
7151
7152 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7153     
7154     cls: false,
7155     align: false,
7156     bgcolor: false,
7157     charoff: false,
7158     valign: false,
7159     
7160     getAutoCreate : function(){
7161         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7162         
7163         cfg = {
7164             tag: 'tr'
7165         };
7166             
7167         if(this.cls){
7168             cfg.cls = this.cls;
7169         }
7170         if(this.align){
7171             cfg.align = this.align;
7172         }
7173         if(this.bgcolor){
7174             cfg.bgcolor = this.bgcolor;
7175         }
7176         if(this.charoff){
7177             cfg.charoff = this.charoff;
7178         }
7179         if(this.valign){
7180             cfg.valign = this.valign;
7181         }
7182         
7183         return cfg;
7184     }
7185    
7186 });
7187
7188  
7189
7190  /*
7191  * - LGPL
7192  *
7193  * table body
7194  * 
7195  */
7196
7197 /**
7198  * @class Roo.bootstrap.TableBody
7199  * @extends Roo.bootstrap.Component
7200  * Bootstrap TableBody class
7201  * @cfg {String} cls element class
7202  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7203  * @cfg {String} align Aligns the content inside the element
7204  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7205  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7206  * 
7207  * @constructor
7208  * Create a new TableBody
7209  * @param {Object} config The config object
7210  */
7211
7212 Roo.bootstrap.TableBody = function(config){
7213     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7214 };
7215
7216 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7217     
7218     cls: false,
7219     tag: false,
7220     align: false,
7221     charoff: false,
7222     valign: false,
7223     
7224     getAutoCreate : function(){
7225         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7226         
7227         cfg = {
7228             tag: 'tbody'
7229         };
7230             
7231         if (this.cls) {
7232             cfg.cls=this.cls
7233         }
7234         if(this.tag){
7235             cfg.tag = this.tag;
7236         }
7237         
7238         if(this.align){
7239             cfg.align = this.align;
7240         }
7241         if(this.charoff){
7242             cfg.charoff = this.charoff;
7243         }
7244         if(this.valign){
7245             cfg.valign = this.valign;
7246         }
7247         
7248         return cfg;
7249     }
7250     
7251     
7252 //    initEvents : function()
7253 //    {
7254 //        
7255 //        if(!this.store){
7256 //            return;
7257 //        }
7258 //        
7259 //        this.store = Roo.factory(this.store, Roo.data);
7260 //        this.store.on('load', this.onLoad, this);
7261 //        
7262 //        this.store.load();
7263 //        
7264 //    },
7265 //    
7266 //    onLoad: function () 
7267 //    {   
7268 //        this.fireEvent('load', this);
7269 //    }
7270 //    
7271 //   
7272 });
7273
7274  
7275
7276  /*
7277  * Based on:
7278  * Ext JS Library 1.1.1
7279  * Copyright(c) 2006-2007, Ext JS, LLC.
7280  *
7281  * Originally Released Under LGPL - original licence link has changed is not relivant.
7282  *
7283  * Fork - LGPL
7284  * <script type="text/javascript">
7285  */
7286
7287 // as we use this in bootstrap.
7288 Roo.namespace('Roo.form');
7289  /**
7290  * @class Roo.form.Action
7291  * Internal Class used to handle form actions
7292  * @constructor
7293  * @param {Roo.form.BasicForm} el The form element or its id
7294  * @param {Object} config Configuration options
7295  */
7296
7297  
7298  
7299 // define the action interface
7300 Roo.form.Action = function(form, options){
7301     this.form = form;
7302     this.options = options || {};
7303 };
7304 /**
7305  * Client Validation Failed
7306  * @const 
7307  */
7308 Roo.form.Action.CLIENT_INVALID = 'client';
7309 /**
7310  * Server Validation Failed
7311  * @const 
7312  */
7313 Roo.form.Action.SERVER_INVALID = 'server';
7314  /**
7315  * Connect to Server Failed
7316  * @const 
7317  */
7318 Roo.form.Action.CONNECT_FAILURE = 'connect';
7319 /**
7320  * Reading Data from Server Failed
7321  * @const 
7322  */
7323 Roo.form.Action.LOAD_FAILURE = 'load';
7324
7325 Roo.form.Action.prototype = {
7326     type : 'default',
7327     failureType : undefined,
7328     response : undefined,
7329     result : undefined,
7330
7331     // interface method
7332     run : function(options){
7333
7334     },
7335
7336     // interface method
7337     success : function(response){
7338
7339     },
7340
7341     // interface method
7342     handleResponse : function(response){
7343
7344     },
7345
7346     // default connection failure
7347     failure : function(response){
7348         
7349         this.response = response;
7350         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7351         this.form.afterAction(this, false);
7352     },
7353
7354     processResponse : function(response){
7355         this.response = response;
7356         if(!response.responseText){
7357             return true;
7358         }
7359         this.result = this.handleResponse(response);
7360         return this.result;
7361     },
7362
7363     // utility functions used internally
7364     getUrl : function(appendParams){
7365         var url = this.options.url || this.form.url || this.form.el.dom.action;
7366         if(appendParams){
7367             var p = this.getParams();
7368             if(p){
7369                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7370             }
7371         }
7372         return url;
7373     },
7374
7375     getMethod : function(){
7376         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7377     },
7378
7379     getParams : function(){
7380         var bp = this.form.baseParams;
7381         var p = this.options.params;
7382         if(p){
7383             if(typeof p == "object"){
7384                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7385             }else if(typeof p == 'string' && bp){
7386                 p += '&' + Roo.urlEncode(bp);
7387             }
7388         }else if(bp){
7389             p = Roo.urlEncode(bp);
7390         }
7391         return p;
7392     },
7393
7394     createCallback : function(){
7395         return {
7396             success: this.success,
7397             failure: this.failure,
7398             scope: this,
7399             timeout: (this.form.timeout*1000),
7400             upload: this.form.fileUpload ? this.success : undefined
7401         };
7402     }
7403 };
7404
7405 Roo.form.Action.Submit = function(form, options){
7406     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7407 };
7408
7409 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7410     type : 'submit',
7411
7412     haveProgress : false,
7413     uploadComplete : false,
7414     
7415     // uploadProgress indicator.
7416     uploadProgress : function()
7417     {
7418         if (!this.form.progressUrl) {
7419             return;
7420         }
7421         
7422         if (!this.haveProgress) {
7423             Roo.MessageBox.progress("Uploading", "Uploading");
7424         }
7425         if (this.uploadComplete) {
7426            Roo.MessageBox.hide();
7427            return;
7428         }
7429         
7430         this.haveProgress = true;
7431    
7432         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7433         
7434         var c = new Roo.data.Connection();
7435         c.request({
7436             url : this.form.progressUrl,
7437             params: {
7438                 id : uid
7439             },
7440             method: 'GET',
7441             success : function(req){
7442                //console.log(data);
7443                 var rdata = false;
7444                 var edata;
7445                 try  {
7446                    rdata = Roo.decode(req.responseText)
7447                 } catch (e) {
7448                     Roo.log("Invalid data from server..");
7449                     Roo.log(edata);
7450                     return;
7451                 }
7452                 if (!rdata || !rdata.success) {
7453                     Roo.log(rdata);
7454                     Roo.MessageBox.alert(Roo.encode(rdata));
7455                     return;
7456                 }
7457                 var data = rdata.data;
7458                 
7459                 if (this.uploadComplete) {
7460                    Roo.MessageBox.hide();
7461                    return;
7462                 }
7463                    
7464                 if (data){
7465                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7466                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7467                     );
7468                 }
7469                 this.uploadProgress.defer(2000,this);
7470             },
7471        
7472             failure: function(data) {
7473                 Roo.log('progress url failed ');
7474                 Roo.log(data);
7475             },
7476             scope : this
7477         });
7478            
7479     },
7480     
7481     
7482     run : function()
7483     {
7484         // run get Values on the form, so it syncs any secondary forms.
7485         this.form.getValues();
7486         
7487         var o = this.options;
7488         var method = this.getMethod();
7489         var isPost = method == 'POST';
7490         if(o.clientValidation === false || this.form.isValid()){
7491             
7492             if (this.form.progressUrl) {
7493                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7494                     (new Date() * 1) + '' + Math.random());
7495                     
7496             } 
7497             
7498             
7499             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7500                 form:this.form.el.dom,
7501                 url:this.getUrl(!isPost),
7502                 method: method,
7503                 params:isPost ? this.getParams() : null,
7504                 isUpload: this.form.fileUpload
7505             }));
7506             
7507             this.uploadProgress();
7508
7509         }else if (o.clientValidation !== false){ // client validation failed
7510             this.failureType = Roo.form.Action.CLIENT_INVALID;
7511             this.form.afterAction(this, false);
7512         }
7513     },
7514
7515     success : function(response)
7516     {
7517         this.uploadComplete= true;
7518         if (this.haveProgress) {
7519             Roo.MessageBox.hide();
7520         }
7521         
7522         
7523         var result = this.processResponse(response);
7524         if(result === true || result.success){
7525             this.form.afterAction(this, true);
7526             return;
7527         }
7528         if(result.errors){
7529             this.form.markInvalid(result.errors);
7530             this.failureType = Roo.form.Action.SERVER_INVALID;
7531         }
7532         this.form.afterAction(this, false);
7533     },
7534     failure : function(response)
7535     {
7536         this.uploadComplete= true;
7537         if (this.haveProgress) {
7538             Roo.MessageBox.hide();
7539         }
7540         
7541         this.response = response;
7542         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7543         this.form.afterAction(this, false);
7544     },
7545     
7546     handleResponse : function(response){
7547         if(this.form.errorReader){
7548             var rs = this.form.errorReader.read(response);
7549             var errors = [];
7550             if(rs.records){
7551                 for(var i = 0, len = rs.records.length; i < len; i++) {
7552                     var r = rs.records[i];
7553                     errors[i] = r.data;
7554                 }
7555             }
7556             if(errors.length < 1){
7557                 errors = null;
7558             }
7559             return {
7560                 success : rs.success,
7561                 errors : errors
7562             };
7563         }
7564         var ret = false;
7565         try {
7566             ret = Roo.decode(response.responseText);
7567         } catch (e) {
7568             ret = {
7569                 success: false,
7570                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7571                 errors : []
7572             };
7573         }
7574         return ret;
7575         
7576     }
7577 });
7578
7579
7580 Roo.form.Action.Load = function(form, options){
7581     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7582     this.reader = this.form.reader;
7583 };
7584
7585 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7586     type : 'load',
7587
7588     run : function(){
7589         
7590         Roo.Ajax.request(Roo.apply(
7591                 this.createCallback(), {
7592                     method:this.getMethod(),
7593                     url:this.getUrl(false),
7594                     params:this.getParams()
7595         }));
7596     },
7597
7598     success : function(response){
7599         
7600         var result = this.processResponse(response);
7601         if(result === true || !result.success || !result.data){
7602             this.failureType = Roo.form.Action.LOAD_FAILURE;
7603             this.form.afterAction(this, false);
7604             return;
7605         }
7606         this.form.clearInvalid();
7607         this.form.setValues(result.data);
7608         this.form.afterAction(this, true);
7609     },
7610
7611     handleResponse : function(response){
7612         if(this.form.reader){
7613             var rs = this.form.reader.read(response);
7614             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7615             return {
7616                 success : rs.success,
7617                 data : data
7618             };
7619         }
7620         return Roo.decode(response.responseText);
7621     }
7622 });
7623
7624 Roo.form.Action.ACTION_TYPES = {
7625     'load' : Roo.form.Action.Load,
7626     'submit' : Roo.form.Action.Submit
7627 };/*
7628  * - LGPL
7629  *
7630  * form
7631  *
7632  */
7633
7634 /**
7635  * @class Roo.bootstrap.Form
7636  * @extends Roo.bootstrap.Component
7637  * Bootstrap Form class
7638  * @cfg {String} method  GET | POST (default POST)
7639  * @cfg {String} labelAlign top | left (default top)
7640  * @cfg {String} align left  | right - for navbars
7641  * @cfg {Boolean} loadMask load mask when submit (default true)
7642
7643  *
7644  * @constructor
7645  * Create a new Form
7646  * @param {Object} config The config object
7647  */
7648
7649
7650 Roo.bootstrap.Form = function(config){
7651     
7652     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7653     
7654     Roo.bootstrap.Form.popover.apply();
7655     
7656     this.addEvents({
7657         /**
7658          * @event clientvalidation
7659          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7660          * @param {Form} this
7661          * @param {Boolean} valid true if the form has passed client-side validation
7662          */
7663         clientvalidation: true,
7664         /**
7665          * @event beforeaction
7666          * Fires before any action is performed. Return false to cancel the action.
7667          * @param {Form} this
7668          * @param {Action} action The action to be performed
7669          */
7670         beforeaction: true,
7671         /**
7672          * @event actionfailed
7673          * Fires when an action fails.
7674          * @param {Form} this
7675          * @param {Action} action The action that failed
7676          */
7677         actionfailed : true,
7678         /**
7679          * @event actioncomplete
7680          * Fires when an action is completed.
7681          * @param {Form} this
7682          * @param {Action} action The action that completed
7683          */
7684         actioncomplete : true
7685     });
7686 };
7687
7688 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7689
7690      /**
7691      * @cfg {String} method
7692      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7693      */
7694     method : 'POST',
7695     /**
7696      * @cfg {String} url
7697      * The URL to use for form actions if one isn't supplied in the action options.
7698      */
7699     /**
7700      * @cfg {Boolean} fileUpload
7701      * Set to true if this form is a file upload.
7702      */
7703
7704     /**
7705      * @cfg {Object} baseParams
7706      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7707      */
7708
7709     /**
7710      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7711      */
7712     timeout: 30,
7713     /**
7714      * @cfg {Sting} align (left|right) for navbar forms
7715      */
7716     align : 'left',
7717
7718     // private
7719     activeAction : null,
7720
7721     /**
7722      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7723      * element by passing it or its id or mask the form itself by passing in true.
7724      * @type Mixed
7725      */
7726     waitMsgTarget : false,
7727
7728     loadMask : true,
7729     
7730     /**
7731      * @cfg {Boolean} errorMask (true|false) default false
7732      */
7733     errorMask : false,
7734     
7735     /**
7736      * @cfg {Number} maskOffset Default 100
7737      */
7738     maskOffset : 100,
7739     
7740     /**
7741      * @cfg {Boolean} maskBody
7742      */
7743     maskBody : false,
7744
7745     getAutoCreate : function(){
7746
7747         var cfg = {
7748             tag: 'form',
7749             method : this.method || 'POST',
7750             id : this.id || Roo.id(),
7751             cls : ''
7752         };
7753         if (this.parent().xtype.match(/^Nav/)) {
7754             cfg.cls = 'navbar-form navbar-' + this.align;
7755
7756         }
7757
7758         if (this.labelAlign == 'left' ) {
7759             cfg.cls += ' form-horizontal';
7760         }
7761
7762
7763         return cfg;
7764     },
7765     initEvents : function()
7766     {
7767         this.el.on('submit', this.onSubmit, this);
7768         // this was added as random key presses on the form where triggering form submit.
7769         this.el.on('keypress', function(e) {
7770             if (e.getCharCode() != 13) {
7771                 return true;
7772             }
7773             // we might need to allow it for textareas.. and some other items.
7774             // check e.getTarget().
7775
7776             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7777                 return true;
7778             }
7779
7780             Roo.log("keypress blocked");
7781
7782             e.preventDefault();
7783             return false;
7784         });
7785         
7786     },
7787     // private
7788     onSubmit : function(e){
7789         e.stopEvent();
7790     },
7791
7792      /**
7793      * Returns true if client-side validation on the form is successful.
7794      * @return Boolean
7795      */
7796     isValid : function(){
7797         var items = this.getItems();
7798         var valid = true;
7799         var target = false;
7800         
7801         items.each(function(f){
7802             
7803             if(f.validate()){
7804                 return;
7805             }
7806             valid = false;
7807
7808             if(!target && f.el.isVisible(true)){
7809                 target = f;
7810             }
7811            
7812         });
7813         
7814         if(this.errorMask && !valid){
7815             Roo.bootstrap.Form.popover.mask(this, target);
7816         }
7817         
7818         return valid;
7819     },
7820     
7821     /**
7822      * Returns true if any fields in this form have changed since their original load.
7823      * @return Boolean
7824      */
7825     isDirty : function(){
7826         var dirty = false;
7827         var items = this.getItems();
7828         items.each(function(f){
7829            if(f.isDirty()){
7830                dirty = true;
7831                return false;
7832            }
7833            return true;
7834         });
7835         return dirty;
7836     },
7837      /**
7838      * Performs a predefined action (submit or load) or custom actions you define on this form.
7839      * @param {String} actionName The name of the action type
7840      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7841      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7842      * accept other config options):
7843      * <pre>
7844 Property          Type             Description
7845 ----------------  ---------------  ----------------------------------------------------------------------------------
7846 url               String           The url for the action (defaults to the form's url)
7847 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7848 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7849 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7850                                    validate the form on the client (defaults to false)
7851      * </pre>
7852      * @return {BasicForm} this
7853      */
7854     doAction : function(action, options){
7855         if(typeof action == 'string'){
7856             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7857         }
7858         if(this.fireEvent('beforeaction', this, action) !== false){
7859             this.beforeAction(action);
7860             action.run.defer(100, action);
7861         }
7862         return this;
7863     },
7864
7865     // private
7866     beforeAction : function(action){
7867         var o = action.options;
7868         
7869         if(this.loadMask){
7870             
7871             if(this.maskBody){
7872                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7873             } else {
7874                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7875             }
7876         }
7877         // not really supported yet.. ??
7878
7879         //if(this.waitMsgTarget === true){
7880         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7881         //}else if(this.waitMsgTarget){
7882         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7883         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7884         //}else {
7885         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7886        // }
7887
7888     },
7889
7890     // private
7891     afterAction : function(action, success){
7892         this.activeAction = null;
7893         var o = action.options;
7894
7895         if(this.loadMask){
7896             
7897             if(this.maskBody){
7898                 Roo.get(document.body).unmask();
7899             } else {
7900                 this.el.unmask();
7901             }
7902         }
7903         
7904         //if(this.waitMsgTarget === true){
7905 //            this.el.unmask();
7906         //}else if(this.waitMsgTarget){
7907         //    this.waitMsgTarget.unmask();
7908         //}else{
7909         //    Roo.MessageBox.updateProgress(1);
7910         //    Roo.MessageBox.hide();
7911        // }
7912         //
7913         if(success){
7914             if(o.reset){
7915                 this.reset();
7916             }
7917             Roo.callback(o.success, o.scope, [this, action]);
7918             this.fireEvent('actioncomplete', this, action);
7919
7920         }else{
7921
7922             // failure condition..
7923             // we have a scenario where updates need confirming.
7924             // eg. if a locking scenario exists..
7925             // we look for { errors : { needs_confirm : true }} in the response.
7926             if (
7927                 (typeof(action.result) != 'undefined')  &&
7928                 (typeof(action.result.errors) != 'undefined')  &&
7929                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7930            ){
7931                 var _t = this;
7932                 Roo.log("not supported yet");
7933                  /*
7934
7935                 Roo.MessageBox.confirm(
7936                     "Change requires confirmation",
7937                     action.result.errorMsg,
7938                     function(r) {
7939                         if (r != 'yes') {
7940                             return;
7941                         }
7942                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7943                     }
7944
7945                 );
7946                 */
7947
7948
7949                 return;
7950             }
7951
7952             Roo.callback(o.failure, o.scope, [this, action]);
7953             // show an error message if no failed handler is set..
7954             if (!this.hasListener('actionfailed')) {
7955                 Roo.log("need to add dialog support");
7956                 /*
7957                 Roo.MessageBox.alert("Error",
7958                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7959                         action.result.errorMsg :
7960                         "Saving Failed, please check your entries or try again"
7961                 );
7962                 */
7963             }
7964
7965             this.fireEvent('actionfailed', this, action);
7966         }
7967
7968     },
7969     /**
7970      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7971      * @param {String} id The value to search for
7972      * @return Field
7973      */
7974     findField : function(id){
7975         var items = this.getItems();
7976         var field = items.get(id);
7977         if(!field){
7978              items.each(function(f){
7979                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7980                     field = f;
7981                     return false;
7982                 }
7983                 return true;
7984             });
7985         }
7986         return field || null;
7987     },
7988      /**
7989      * Mark fields in this form invalid in bulk.
7990      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7991      * @return {BasicForm} this
7992      */
7993     markInvalid : function(errors){
7994         if(errors instanceof Array){
7995             for(var i = 0, len = errors.length; i < len; i++){
7996                 var fieldError = errors[i];
7997                 var f = this.findField(fieldError.id);
7998                 if(f){
7999                     f.markInvalid(fieldError.msg);
8000                 }
8001             }
8002         }else{
8003             var field, id;
8004             for(id in errors){
8005                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8006                     field.markInvalid(errors[id]);
8007                 }
8008             }
8009         }
8010         //Roo.each(this.childForms || [], function (f) {
8011         //    f.markInvalid(errors);
8012         //});
8013
8014         return this;
8015     },
8016
8017     /**
8018      * Set values for fields in this form in bulk.
8019      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8020      * @return {BasicForm} this
8021      */
8022     setValues : function(values){
8023         if(values instanceof Array){ // array of objects
8024             for(var i = 0, len = values.length; i < len; i++){
8025                 var v = values[i];
8026                 var f = this.findField(v.id);
8027                 if(f){
8028                     f.setValue(v.value);
8029                     if(this.trackResetOnLoad){
8030                         f.originalValue = f.getValue();
8031                     }
8032                 }
8033             }
8034         }else{ // object hash
8035             var field, id;
8036             for(id in values){
8037                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8038
8039                     if (field.setFromData &&
8040                         field.valueField &&
8041                         field.displayField &&
8042                         // combos' with local stores can
8043                         // be queried via setValue()
8044                         // to set their value..
8045                         (field.store && !field.store.isLocal)
8046                         ) {
8047                         // it's a combo
8048                         var sd = { };
8049                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8050                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8051                         field.setFromData(sd);
8052
8053                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8054                         
8055                         field.setFromData(values);
8056                         
8057                     } else {
8058                         field.setValue(values[id]);
8059                     }
8060
8061
8062                     if(this.trackResetOnLoad){
8063                         field.originalValue = field.getValue();
8064                     }
8065                 }
8066             }
8067         }
8068
8069         //Roo.each(this.childForms || [], function (f) {
8070         //    f.setValues(values);
8071         //});
8072
8073         return this;
8074     },
8075
8076     /**
8077      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8078      * they are returned as an array.
8079      * @param {Boolean} asString
8080      * @return {Object}
8081      */
8082     getValues : function(asString){
8083         //if (this.childForms) {
8084             // copy values from the child forms
8085         //    Roo.each(this.childForms, function (f) {
8086         //        this.setValues(f.getValues());
8087         //    }, this);
8088         //}
8089
8090
8091
8092         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8093         if(asString === true){
8094             return fs;
8095         }
8096         return Roo.urlDecode(fs);
8097     },
8098
8099     /**
8100      * Returns the fields in this form as an object with key/value pairs.
8101      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8102      * @return {Object}
8103      */
8104     getFieldValues : function(with_hidden)
8105     {
8106         var items = this.getItems();
8107         var ret = {};
8108         items.each(function(f){
8109             
8110             if (!f.getName()) {
8111                 return;
8112             }
8113             
8114             var v = f.getValue();
8115             
8116             if (f.inputType =='radio') {
8117                 if (typeof(ret[f.getName()]) == 'undefined') {
8118                     ret[f.getName()] = ''; // empty..
8119                 }
8120
8121                 if (!f.el.dom.checked) {
8122                     return;
8123
8124                 }
8125                 v = f.el.dom.value;
8126
8127             }
8128             
8129             if(f.xtype == 'MoneyField'){
8130                 ret[f.currencyName] = f.getCurrency();
8131             }
8132
8133             // not sure if this supported any more..
8134             if ((typeof(v) == 'object') && f.getRawValue) {
8135                 v = f.getRawValue() ; // dates..
8136             }
8137             // combo boxes where name != hiddenName...
8138             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8139                 ret[f.name] = f.getRawValue();
8140             }
8141             ret[f.getName()] = v;
8142         });
8143
8144         return ret;
8145     },
8146
8147     /**
8148      * Clears all invalid messages in this form.
8149      * @return {BasicForm} this
8150      */
8151     clearInvalid : function(){
8152         var items = this.getItems();
8153
8154         items.each(function(f){
8155            f.clearInvalid();
8156         });
8157
8158         return this;
8159     },
8160
8161     /**
8162      * Resets this form.
8163      * @return {BasicForm} this
8164      */
8165     reset : function(){
8166         var items = this.getItems();
8167         items.each(function(f){
8168             f.reset();
8169         });
8170
8171         Roo.each(this.childForms || [], function (f) {
8172             f.reset();
8173         });
8174
8175
8176         return this;
8177     },
8178     
8179     getItems : function()
8180     {
8181         var r=new Roo.util.MixedCollection(false, function(o){
8182             return o.id || (o.id = Roo.id());
8183         });
8184         var iter = function(el) {
8185             if (el.inputEl) {
8186                 r.add(el);
8187             }
8188             if (!el.items) {
8189                 return;
8190             }
8191             Roo.each(el.items,function(e) {
8192                 iter(e);
8193             });
8194         };
8195
8196         iter(this);
8197         return r;
8198     },
8199     
8200     hideFields : function(items)
8201     {
8202         Roo.each(items, function(i){
8203             
8204             var f = this.findField(i);
8205             
8206             if(!f){
8207                 return;
8208             }
8209             
8210             if(f.xtype == 'DateField'){
8211                 f.setVisible(false);
8212                 return;
8213             }
8214             
8215             f.hide();
8216             
8217         }, this);
8218     },
8219     
8220     showFields : function(items)
8221     {
8222         Roo.each(items, function(i){
8223             
8224             var f = this.findField(i);
8225             
8226             if(!f){
8227                 return;
8228             }
8229             
8230             if(f.xtype == 'DateField'){
8231                 f.setVisible(true);
8232                 return;
8233             }
8234             
8235             f.show();
8236             
8237         }, this);
8238     }
8239
8240 });
8241
8242 Roo.apply(Roo.bootstrap.Form, {
8243     
8244     popover : {
8245         
8246         padding : 5,
8247         
8248         isApplied : false,
8249         
8250         isMasked : false,
8251         
8252         form : false,
8253         
8254         target : false,
8255         
8256         toolTip : false,
8257         
8258         intervalID : false,
8259         
8260         maskEl : false,
8261         
8262         apply : function()
8263         {
8264             if(this.isApplied){
8265                 return;
8266             }
8267             
8268             this.maskEl = {
8269                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8270                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8271                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8272                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8273             };
8274             
8275             this.maskEl.top.enableDisplayMode("block");
8276             this.maskEl.left.enableDisplayMode("block");
8277             this.maskEl.bottom.enableDisplayMode("block");
8278             this.maskEl.right.enableDisplayMode("block");
8279             
8280             this.toolTip = new Roo.bootstrap.Tooltip({
8281                 cls : 'roo-form-error-popover',
8282                 alignment : {
8283                     'left' : ['r-l', [-2,0], 'right'],
8284                     'right' : ['l-r', [2,0], 'left'],
8285                     'bottom' : ['tl-bl', [0,2], 'top'],
8286                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8287                 }
8288             });
8289             
8290             this.toolTip.render(Roo.get(document.body));
8291
8292             this.toolTip.el.enableDisplayMode("block");
8293             
8294             Roo.get(document.body).on('click', function(){
8295                 this.unmask();
8296             }, this);
8297             
8298             Roo.get(document.body).on('touchstart', function(){
8299                 this.unmask();
8300             }, this);
8301             
8302             this.isApplied = true
8303         },
8304         
8305         mask : function(form, target)
8306         {
8307             this.form = form;
8308             
8309             this.target = target;
8310             
8311             if(!this.form.errorMask || !target.el){
8312                 return;
8313             }
8314             
8315             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8316             
8317             Roo.log(scrollable);
8318             
8319             var ot = this.target.el.calcOffsetsTo(scrollable);
8320             
8321             var scrollTo = ot[1] - this.form.maskOffset;
8322             
8323             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8324             
8325             scrollable.scrollTo('top', scrollTo);
8326             
8327             var box = this.target.el.getBox();
8328             Roo.log(box);
8329             var zIndex = Roo.bootstrap.Modal.zIndex++;
8330
8331             
8332             this.maskEl.top.setStyle('position', 'absolute');
8333             this.maskEl.top.setStyle('z-index', zIndex);
8334             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8335             this.maskEl.top.setLeft(0);
8336             this.maskEl.top.setTop(0);
8337             this.maskEl.top.show();
8338             
8339             this.maskEl.left.setStyle('position', 'absolute');
8340             this.maskEl.left.setStyle('z-index', zIndex);
8341             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8342             this.maskEl.left.setLeft(0);
8343             this.maskEl.left.setTop(box.y - this.padding);
8344             this.maskEl.left.show();
8345
8346             this.maskEl.bottom.setStyle('position', 'absolute');
8347             this.maskEl.bottom.setStyle('z-index', zIndex);
8348             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8349             this.maskEl.bottom.setLeft(0);
8350             this.maskEl.bottom.setTop(box.bottom + this.padding);
8351             this.maskEl.bottom.show();
8352
8353             this.maskEl.right.setStyle('position', 'absolute');
8354             this.maskEl.right.setStyle('z-index', zIndex);
8355             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8356             this.maskEl.right.setLeft(box.right + this.padding);
8357             this.maskEl.right.setTop(box.y - this.padding);
8358             this.maskEl.right.show();
8359
8360             this.toolTip.bindEl = this.target.el;
8361
8362             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8363
8364             var tip = this.target.blankText;
8365
8366             if(this.target.getValue() !== '' ) {
8367                 
8368                 if (this.target.invalidText.length) {
8369                     tip = this.target.invalidText;
8370                 } else if (this.target.regexText.length){
8371                     tip = this.target.regexText;
8372                 }
8373             }
8374
8375             this.toolTip.show(tip);
8376
8377             this.intervalID = window.setInterval(function() {
8378                 Roo.bootstrap.Form.popover.unmask();
8379             }, 10000);
8380
8381             window.onwheel = function(){ return false;};
8382             
8383             (function(){ this.isMasked = true; }).defer(500, this);
8384             
8385         },
8386         
8387         unmask : function()
8388         {
8389             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8390                 return;
8391             }
8392             
8393             this.maskEl.top.setStyle('position', 'absolute');
8394             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8395             this.maskEl.top.hide();
8396
8397             this.maskEl.left.setStyle('position', 'absolute');
8398             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8399             this.maskEl.left.hide();
8400
8401             this.maskEl.bottom.setStyle('position', 'absolute');
8402             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8403             this.maskEl.bottom.hide();
8404
8405             this.maskEl.right.setStyle('position', 'absolute');
8406             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8407             this.maskEl.right.hide();
8408             
8409             this.toolTip.hide();
8410             
8411             this.toolTip.el.hide();
8412             
8413             window.onwheel = function(){ return true;};
8414             
8415             if(this.intervalID){
8416                 window.clearInterval(this.intervalID);
8417                 this.intervalID = false;
8418             }
8419             
8420             this.isMasked = false;
8421             
8422         }
8423         
8424     }
8425     
8426 });
8427
8428 /*
8429  * Based on:
8430  * Ext JS Library 1.1.1
8431  * Copyright(c) 2006-2007, Ext JS, LLC.
8432  *
8433  * Originally Released Under LGPL - original licence link has changed is not relivant.
8434  *
8435  * Fork - LGPL
8436  * <script type="text/javascript">
8437  */
8438 /**
8439  * @class Roo.form.VTypes
8440  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8441  * @singleton
8442  */
8443 Roo.form.VTypes = function(){
8444     // closure these in so they are only created once.
8445     var alpha = /^[a-zA-Z_]+$/;
8446     var alphanum = /^[a-zA-Z0-9_]+$/;
8447     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8448     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8449
8450     // All these messages and functions are configurable
8451     return {
8452         /**
8453          * The function used to validate email addresses
8454          * @param {String} value The email address
8455          */
8456         'email' : function(v){
8457             return email.test(v);
8458         },
8459         /**
8460          * The error text to display when the email validation function returns false
8461          * @type String
8462          */
8463         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8464         /**
8465          * The keystroke filter mask to be applied on email input
8466          * @type RegExp
8467          */
8468         'emailMask' : /[a-z0-9_\.\-@]/i,
8469
8470         /**
8471          * The function used to validate URLs
8472          * @param {String} value The URL
8473          */
8474         'url' : function(v){
8475             return url.test(v);
8476         },
8477         /**
8478          * The error text to display when the url validation function returns false
8479          * @type String
8480          */
8481         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8482         
8483         /**
8484          * The function used to validate alpha values
8485          * @param {String} value The value
8486          */
8487         'alpha' : function(v){
8488             return alpha.test(v);
8489         },
8490         /**
8491          * The error text to display when the alpha validation function returns false
8492          * @type String
8493          */
8494         'alphaText' : 'This field should only contain letters and _',
8495         /**
8496          * The keystroke filter mask to be applied on alpha input
8497          * @type RegExp
8498          */
8499         'alphaMask' : /[a-z_]/i,
8500
8501         /**
8502          * The function used to validate alphanumeric values
8503          * @param {String} value The value
8504          */
8505         'alphanum' : function(v){
8506             return alphanum.test(v);
8507         },
8508         /**
8509          * The error text to display when the alphanumeric validation function returns false
8510          * @type String
8511          */
8512         'alphanumText' : 'This field should only contain letters, numbers and _',
8513         /**
8514          * The keystroke filter mask to be applied on alphanumeric input
8515          * @type RegExp
8516          */
8517         'alphanumMask' : /[a-z0-9_]/i
8518     };
8519 }();/*
8520  * - LGPL
8521  *
8522  * Input
8523  * 
8524  */
8525
8526 /**
8527  * @class Roo.bootstrap.Input
8528  * @extends Roo.bootstrap.Component
8529  * Bootstrap Input class
8530  * @cfg {Boolean} disabled is it disabled
8531  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8532  * @cfg {String} name name of the input
8533  * @cfg {string} fieldLabel - the label associated
8534  * @cfg {string} placeholder - placeholder to put in text.
8535  * @cfg {string}  before - input group add on before
8536  * @cfg {string} after - input group add on after
8537  * @cfg {string} size - (lg|sm) or leave empty..
8538  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8539  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8540  * @cfg {Number} md colspan out of 12 for computer-sized screens
8541  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8542  * @cfg {string} value default value of the input
8543  * @cfg {Number} labelWidth set the width of label 
8544  * @cfg {Number} labellg set the width of label (1-12)
8545  * @cfg {Number} labelmd set the width of label (1-12)
8546  * @cfg {Number} labelsm set the width of label (1-12)
8547  * @cfg {Number} labelxs set the width of label (1-12)
8548  * @cfg {String} labelAlign (top|left)
8549  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8550  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8551  * @cfg {String} indicatorpos (left|right) default left
8552  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8553
8554  * @cfg {String} align (left|center|right) Default left
8555  * @cfg {Boolean} forceFeedback (true|false) Default false
8556  * 
8557  * @constructor
8558  * Create a new Input
8559  * @param {Object} config The config object
8560  */
8561
8562 Roo.bootstrap.Input = function(config){
8563     
8564     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8565     
8566     this.addEvents({
8567         /**
8568          * @event focus
8569          * Fires when this field receives input focus.
8570          * @param {Roo.form.Field} this
8571          */
8572         focus : true,
8573         /**
8574          * @event blur
8575          * Fires when this field loses input focus.
8576          * @param {Roo.form.Field} this
8577          */
8578         blur : true,
8579         /**
8580          * @event specialkey
8581          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8582          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8583          * @param {Roo.form.Field} this
8584          * @param {Roo.EventObject} e The event object
8585          */
8586         specialkey : true,
8587         /**
8588          * @event change
8589          * Fires just before the field blurs if the field value has changed.
8590          * @param {Roo.form.Field} this
8591          * @param {Mixed} newValue The new value
8592          * @param {Mixed} oldValue The original value
8593          */
8594         change : true,
8595         /**
8596          * @event invalid
8597          * Fires after the field has been marked as invalid.
8598          * @param {Roo.form.Field} this
8599          * @param {String} msg The validation message
8600          */
8601         invalid : true,
8602         /**
8603          * @event valid
8604          * Fires after the field has been validated with no errors.
8605          * @param {Roo.form.Field} this
8606          */
8607         valid : true,
8608          /**
8609          * @event keyup
8610          * Fires after the key up
8611          * @param {Roo.form.Field} this
8612          * @param {Roo.EventObject}  e The event Object
8613          */
8614         keyup : true
8615     });
8616 };
8617
8618 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8619      /**
8620      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8621       automatic validation (defaults to "keyup").
8622      */
8623     validationEvent : "keyup",
8624      /**
8625      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8626      */
8627     validateOnBlur : true,
8628     /**
8629      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8630      */
8631     validationDelay : 250,
8632      /**
8633      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8634      */
8635     focusClass : "x-form-focus",  // not needed???
8636     
8637        
8638     /**
8639      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8640      */
8641     invalidClass : "has-warning",
8642     
8643     /**
8644      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8645      */
8646     validClass : "has-success",
8647     
8648     /**
8649      * @cfg {Boolean} hasFeedback (true|false) default true
8650      */
8651     hasFeedback : true,
8652     
8653     /**
8654      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8655      */
8656     invalidFeedbackClass : "glyphicon-warning-sign",
8657     
8658     /**
8659      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8660      */
8661     validFeedbackClass : "glyphicon-ok",
8662     
8663     /**
8664      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8665      */
8666     selectOnFocus : false,
8667     
8668      /**
8669      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8670      */
8671     maskRe : null,
8672        /**
8673      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8674      */
8675     vtype : null,
8676     
8677       /**
8678      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8679      */
8680     disableKeyFilter : false,
8681     
8682        /**
8683      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8684      */
8685     disabled : false,
8686      /**
8687      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8688      */
8689     allowBlank : true,
8690     /**
8691      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8692      */
8693     blankText : "Please complete this mandatory field",
8694     
8695      /**
8696      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8697      */
8698     minLength : 0,
8699     /**
8700      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8701      */
8702     maxLength : Number.MAX_VALUE,
8703     /**
8704      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8705      */
8706     minLengthText : "The minimum length for this field is {0}",
8707     /**
8708      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8709      */
8710     maxLengthText : "The maximum length for this field is {0}",
8711   
8712     
8713     /**
8714      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8715      * If available, this function will be called only after the basic validators all return true, and will be passed the
8716      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8717      */
8718     validator : null,
8719     /**
8720      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8721      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8722      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8723      */
8724     regex : null,
8725     /**
8726      * @cfg {String} regexText -- Depricated - use Invalid Text
8727      */
8728     regexText : "",
8729     
8730     /**
8731      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8732      */
8733     invalidText : "",
8734     
8735     
8736     
8737     autocomplete: false,
8738     
8739     
8740     fieldLabel : '',
8741     inputType : 'text',
8742     
8743     name : false,
8744     placeholder: false,
8745     before : false,
8746     after : false,
8747     size : false,
8748     hasFocus : false,
8749     preventMark: false,
8750     isFormField : true,
8751     value : '',
8752     labelWidth : 2,
8753     labelAlign : false,
8754     readOnly : false,
8755     align : false,
8756     formatedValue : false,
8757     forceFeedback : false,
8758     
8759     indicatorpos : 'left',
8760     
8761     labellg : 0,
8762     labelmd : 0,
8763     labelsm : 0,
8764     labelxs : 0,
8765     
8766     capture : '',
8767     
8768     parentLabelAlign : function()
8769     {
8770         var parent = this;
8771         while (parent.parent()) {
8772             parent = parent.parent();
8773             if (typeof(parent.labelAlign) !='undefined') {
8774                 return parent.labelAlign;
8775             }
8776         }
8777         return 'left';
8778         
8779     },
8780     
8781     getAutoCreate : function()
8782     {
8783         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8784         
8785         var id = Roo.id();
8786         
8787         var cfg = {};
8788         
8789         if(this.inputType != 'hidden'){
8790             cfg.cls = 'form-group' //input-group
8791         }
8792         
8793         var input =  {
8794             tag: 'input',
8795             id : id,
8796             type : this.inputType,
8797             value : this.value,
8798             cls : 'form-control',
8799             placeholder : this.placeholder || '',
8800             autocomplete : this.autocomplete || 'new-password'
8801         };
8802         
8803         if(this.capture.length){
8804             input.capture = this.capture;
8805         }
8806         
8807         if(this.align){
8808             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8809         }
8810         
8811         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8812             input.maxLength = this.maxLength;
8813         }
8814         
8815         if (this.disabled) {
8816             input.disabled=true;
8817         }
8818         
8819         if (this.readOnly) {
8820             input.readonly=true;
8821         }
8822         
8823         if (this.name) {
8824             input.name = this.name;
8825         }
8826         
8827         if (this.size) {
8828             input.cls += ' input-' + this.size;
8829         }
8830         
8831         var settings=this;
8832         ['xs','sm','md','lg'].map(function(size){
8833             if (settings[size]) {
8834                 cfg.cls += ' col-' + size + '-' + settings[size];
8835             }
8836         });
8837         
8838         var inputblock = input;
8839         
8840         var feedback = {
8841             tag: 'span',
8842             cls: 'glyphicon form-control-feedback'
8843         };
8844             
8845         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8846             
8847             inputblock = {
8848                 cls : 'has-feedback',
8849                 cn :  [
8850                     input,
8851                     feedback
8852                 ] 
8853             };  
8854         }
8855         
8856         if (this.before || this.after) {
8857             
8858             inputblock = {
8859                 cls : 'input-group',
8860                 cn :  [] 
8861             };
8862             
8863             if (this.before && typeof(this.before) == 'string') {
8864                 
8865                 inputblock.cn.push({
8866                     tag :'span',
8867                     cls : 'roo-input-before input-group-addon',
8868                     html : this.before
8869                 });
8870             }
8871             if (this.before && typeof(this.before) == 'object') {
8872                 this.before = Roo.factory(this.before);
8873                 
8874                 inputblock.cn.push({
8875                     tag :'span',
8876                     cls : 'roo-input-before input-group-' +
8877                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8878                 });
8879             }
8880             
8881             inputblock.cn.push(input);
8882             
8883             if (this.after && typeof(this.after) == 'string') {
8884                 inputblock.cn.push({
8885                     tag :'span',
8886                     cls : 'roo-input-after input-group-addon',
8887                     html : this.after
8888                 });
8889             }
8890             if (this.after && typeof(this.after) == 'object') {
8891                 this.after = Roo.factory(this.after);
8892                 
8893                 inputblock.cn.push({
8894                     tag :'span',
8895                     cls : 'roo-input-after input-group-' +
8896                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8897                 });
8898             }
8899             
8900             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8901                 inputblock.cls += ' has-feedback';
8902                 inputblock.cn.push(feedback);
8903             }
8904         };
8905         
8906         if (align ==='left' && this.fieldLabel.length) {
8907             
8908             cfg.cls += ' roo-form-group-label-left';
8909             
8910             cfg.cn = [
8911                 {
8912                     tag : 'i',
8913                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8914                     tooltip : 'This field is required'
8915                 },
8916                 {
8917                     tag: 'label',
8918                     'for' :  id,
8919                     cls : 'control-label',
8920                     html : this.fieldLabel
8921
8922                 },
8923                 {
8924                     cls : "", 
8925                     cn: [
8926                         inputblock
8927                     ]
8928                 }
8929             ];
8930             
8931             var labelCfg = cfg.cn[1];
8932             var contentCfg = cfg.cn[2];
8933             
8934             if(this.indicatorpos == 'right'){
8935                 cfg.cn = [
8936                     {
8937                         tag: 'label',
8938                         'for' :  id,
8939                         cls : 'control-label',
8940                         cn : [
8941                             {
8942                                 tag : 'span',
8943                                 html : this.fieldLabel
8944                             },
8945                             {
8946                                 tag : 'i',
8947                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8948                                 tooltip : 'This field is required'
8949                             }
8950                         ]
8951                     },
8952                     {
8953                         cls : "",
8954                         cn: [
8955                             inputblock
8956                         ]
8957                     }
8958
8959                 ];
8960                 
8961                 labelCfg = cfg.cn[0];
8962                 contentCfg = cfg.cn[1];
8963             
8964             }
8965             
8966             if(this.labelWidth > 12){
8967                 labelCfg.style = "width: " + this.labelWidth + 'px';
8968             }
8969             
8970             if(this.labelWidth < 13 && this.labelmd == 0){
8971                 this.labelmd = this.labelWidth;
8972             }
8973             
8974             if(this.labellg > 0){
8975                 labelCfg.cls += ' col-lg-' + this.labellg;
8976                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8977             }
8978             
8979             if(this.labelmd > 0){
8980                 labelCfg.cls += ' col-md-' + this.labelmd;
8981                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8982             }
8983             
8984             if(this.labelsm > 0){
8985                 labelCfg.cls += ' col-sm-' + this.labelsm;
8986                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8987             }
8988             
8989             if(this.labelxs > 0){
8990                 labelCfg.cls += ' col-xs-' + this.labelxs;
8991                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8992             }
8993             
8994             
8995         } else if ( this.fieldLabel.length) {
8996                 
8997             cfg.cn = [
8998                 {
8999                     tag : 'i',
9000                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9001                     tooltip : 'This field is required'
9002                 },
9003                 {
9004                     tag: 'label',
9005                    //cls : 'input-group-addon',
9006                     html : this.fieldLabel
9007
9008                 },
9009
9010                inputblock
9011
9012            ];
9013            
9014            if(this.indicatorpos == 'right'){
9015                 
9016                 cfg.cn = [
9017                     {
9018                         tag: 'label',
9019                        //cls : 'input-group-addon',
9020                         html : this.fieldLabel
9021
9022                     },
9023                     {
9024                         tag : 'i',
9025                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9026                         tooltip : 'This field is required'
9027                     },
9028
9029                    inputblock
9030
9031                ];
9032
9033             }
9034
9035         } else {
9036             
9037             cfg.cn = [
9038
9039                     inputblock
9040
9041             ];
9042                 
9043                 
9044         };
9045         
9046         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9047            cfg.cls += ' navbar-form';
9048         }
9049         
9050         if (this.parentType === 'NavGroup') {
9051            cfg.cls += ' navbar-form';
9052            cfg.tag = 'li';
9053         }
9054         
9055         return cfg;
9056         
9057     },
9058     /**
9059      * return the real input element.
9060      */
9061     inputEl: function ()
9062     {
9063         return this.el.select('input.form-control',true).first();
9064     },
9065     
9066     tooltipEl : function()
9067     {
9068         return this.inputEl();
9069     },
9070     
9071     indicatorEl : function()
9072     {
9073         var indicator = this.el.select('i.roo-required-indicator',true).first();
9074         
9075         if(!indicator){
9076             return false;
9077         }
9078         
9079         return indicator;
9080         
9081     },
9082     
9083     setDisabled : function(v)
9084     {
9085         var i  = this.inputEl().dom;
9086         if (!v) {
9087             i.removeAttribute('disabled');
9088             return;
9089             
9090         }
9091         i.setAttribute('disabled','true');
9092     },
9093     initEvents : function()
9094     {
9095           
9096         this.inputEl().on("keydown" , this.fireKey,  this);
9097         this.inputEl().on("focus", this.onFocus,  this);
9098         this.inputEl().on("blur", this.onBlur,  this);
9099         
9100         this.inputEl().relayEvent('keyup', this);
9101         
9102         this.indicator = this.indicatorEl();
9103         
9104         if(this.indicator){
9105             this.indicator.addClass('invisible');
9106         }
9107  
9108         // reference to original value for reset
9109         this.originalValue = this.getValue();
9110         //Roo.form.TextField.superclass.initEvents.call(this);
9111         if(this.validationEvent == 'keyup'){
9112             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9113             this.inputEl().on('keyup', this.filterValidation, this);
9114         }
9115         else if(this.validationEvent !== false){
9116             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9117         }
9118         
9119         if(this.selectOnFocus){
9120             this.on("focus", this.preFocus, this);
9121             
9122         }
9123         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9124             this.inputEl().on("keypress", this.filterKeys, this);
9125         } else {
9126             this.inputEl().relayEvent('keypress', this);
9127         }
9128        /* if(this.grow){
9129             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9130             this.el.on("click", this.autoSize,  this);
9131         }
9132         */
9133         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9134             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9135         }
9136         
9137         if (typeof(this.before) == 'object') {
9138             this.before.render(this.el.select('.roo-input-before',true).first());
9139         }
9140         if (typeof(this.after) == 'object') {
9141             this.after.render(this.el.select('.roo-input-after',true).first());
9142         }
9143         
9144         this.inputEl().on('change', this.onChange, this);
9145         
9146     },
9147     filterValidation : function(e){
9148         if(!e.isNavKeyPress()){
9149             this.validationTask.delay(this.validationDelay);
9150         }
9151     },
9152      /**
9153      * Validates the field value
9154      * @return {Boolean} True if the value is valid, else false
9155      */
9156     validate : function(){
9157         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9158         if(this.disabled || this.validateValue(this.getRawValue())){
9159             this.markValid();
9160             return true;
9161         }
9162         
9163         this.markInvalid();
9164         return false;
9165     },
9166     
9167     
9168     /**
9169      * Validates a value according to the field's validation rules and marks the field as invalid
9170      * if the validation fails
9171      * @param {Mixed} value The value to validate
9172      * @return {Boolean} True if the value is valid, else false
9173      */
9174     validateValue : function(value)
9175     {
9176         if(this.getVisibilityEl().hasClass('hidden')){
9177             return true;
9178         }
9179         
9180         if(value.length < 1)  { // if it's blank
9181             if(this.allowBlank){
9182                 return true;
9183             }
9184             return false;
9185         }
9186         
9187         if(value.length < this.minLength){
9188             return false;
9189         }
9190         if(value.length > this.maxLength){
9191             return false;
9192         }
9193         if(this.vtype){
9194             var vt = Roo.form.VTypes;
9195             if(!vt[this.vtype](value, this)){
9196                 return false;
9197             }
9198         }
9199         if(typeof this.validator == "function"){
9200             var msg = this.validator(value);
9201             if(msg !== true){
9202                 return false;
9203             }
9204             if (typeof(msg) == 'string') {
9205                 this.invalidText = msg;
9206             }
9207         }
9208         
9209         if(this.regex && !this.regex.test(value)){
9210             return false;
9211         }
9212         
9213         return true;
9214     },
9215     
9216      // private
9217     fireKey : function(e){
9218         //Roo.log('field ' + e.getKey());
9219         if(e.isNavKeyPress()){
9220             this.fireEvent("specialkey", this, e);
9221         }
9222     },
9223     focus : function (selectText){
9224         if(this.rendered){
9225             this.inputEl().focus();
9226             if(selectText === true){
9227                 this.inputEl().dom.select();
9228             }
9229         }
9230         return this;
9231     } ,
9232     
9233     onFocus : function(){
9234         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9235            // this.el.addClass(this.focusClass);
9236         }
9237         if(!this.hasFocus){
9238             this.hasFocus = true;
9239             this.startValue = this.getValue();
9240             this.fireEvent("focus", this);
9241         }
9242     },
9243     
9244     beforeBlur : Roo.emptyFn,
9245
9246     
9247     // private
9248     onBlur : function(){
9249         this.beforeBlur();
9250         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9251             //this.el.removeClass(this.focusClass);
9252         }
9253         this.hasFocus = false;
9254         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9255             this.validate();
9256         }
9257         var v = this.getValue();
9258         if(String(v) !== String(this.startValue)){
9259             this.fireEvent('change', this, v, this.startValue);
9260         }
9261         this.fireEvent("blur", this);
9262     },
9263     
9264     onChange : function(e)
9265     {
9266         var v = this.getValue();
9267         if(String(v) !== String(this.startValue)){
9268             this.fireEvent('change', this, v, this.startValue);
9269         }
9270         
9271     },
9272     
9273     /**
9274      * Resets the current field value to the originally loaded value and clears any validation messages
9275      */
9276     reset : function(){
9277         this.setValue(this.originalValue);
9278         this.validate();
9279     },
9280      /**
9281      * Returns the name of the field
9282      * @return {Mixed} name The name field
9283      */
9284     getName: function(){
9285         return this.name;
9286     },
9287      /**
9288      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9289      * @return {Mixed} value The field value
9290      */
9291     getValue : function(){
9292         
9293         var v = this.inputEl().getValue();
9294         
9295         return v;
9296     },
9297     /**
9298      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9299      * @return {Mixed} value The field value
9300      */
9301     getRawValue : function(){
9302         var v = this.inputEl().getValue();
9303         
9304         return v;
9305     },
9306     
9307     /**
9308      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9309      * @param {Mixed} value The value to set
9310      */
9311     setRawValue : function(v){
9312         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9313     },
9314     
9315     selectText : function(start, end){
9316         var v = this.getRawValue();
9317         if(v.length > 0){
9318             start = start === undefined ? 0 : start;
9319             end = end === undefined ? v.length : end;
9320             var d = this.inputEl().dom;
9321             if(d.setSelectionRange){
9322                 d.setSelectionRange(start, end);
9323             }else if(d.createTextRange){
9324                 var range = d.createTextRange();
9325                 range.moveStart("character", start);
9326                 range.moveEnd("character", v.length-end);
9327                 range.select();
9328             }
9329         }
9330     },
9331     
9332     /**
9333      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9334      * @param {Mixed} value The value to set
9335      */
9336     setValue : function(v){
9337         this.value = v;
9338         if(this.rendered){
9339             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9340             this.validate();
9341         }
9342     },
9343     
9344     /*
9345     processValue : function(value){
9346         if(this.stripCharsRe){
9347             var newValue = value.replace(this.stripCharsRe, '');
9348             if(newValue !== value){
9349                 this.setRawValue(newValue);
9350                 return newValue;
9351             }
9352         }
9353         return value;
9354     },
9355   */
9356     preFocus : function(){
9357         
9358         if(this.selectOnFocus){
9359             this.inputEl().dom.select();
9360         }
9361     },
9362     filterKeys : function(e){
9363         var k = e.getKey();
9364         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9365             return;
9366         }
9367         var c = e.getCharCode(), cc = String.fromCharCode(c);
9368         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9369             return;
9370         }
9371         if(!this.maskRe.test(cc)){
9372             e.stopEvent();
9373         }
9374     },
9375      /**
9376      * Clear any invalid styles/messages for this field
9377      */
9378     clearInvalid : function(){
9379         
9380         if(!this.el || this.preventMark){ // not rendered
9381             return;
9382         }
9383         
9384      
9385         this.el.removeClass(this.invalidClass);
9386         
9387         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9388             
9389             var feedback = this.el.select('.form-control-feedback', true).first();
9390             
9391             if(feedback){
9392                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9393             }
9394             
9395         }
9396         
9397         this.fireEvent('valid', this);
9398     },
9399     
9400      /**
9401      * Mark this field as valid
9402      */
9403     markValid : function()
9404     {
9405         if(!this.el  || this.preventMark){ // not rendered...
9406             return;
9407         }
9408         
9409         this.el.removeClass([this.invalidClass, this.validClass]);
9410         
9411         var feedback = this.el.select('.form-control-feedback', true).first();
9412             
9413         if(feedback){
9414             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9415         }
9416         
9417         if(this.indicator){
9418             this.indicator.removeClass('visible');
9419             this.indicator.addClass('invisible');
9420         }
9421         
9422         if(this.disabled){
9423             return;
9424         }
9425         
9426         if(this.allowBlank && !this.getRawValue().length){
9427             return;
9428         }
9429         
9430         this.el.addClass(this.validClass);
9431         
9432         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9433             
9434             var feedback = this.el.select('.form-control-feedback', true).first();
9435             
9436             if(feedback){
9437                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9438                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9439             }
9440             
9441         }
9442         
9443         this.fireEvent('valid', this);
9444     },
9445     
9446      /**
9447      * Mark this field as invalid
9448      * @param {String} msg The validation message
9449      */
9450     markInvalid : function(msg)
9451     {
9452         if(!this.el  || this.preventMark){ // not rendered
9453             return;
9454         }
9455         
9456         this.el.removeClass([this.invalidClass, this.validClass]);
9457         
9458         var feedback = this.el.select('.form-control-feedback', true).first();
9459             
9460         if(feedback){
9461             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9462         }
9463
9464         if(this.disabled){
9465             return;
9466         }
9467         
9468         if(this.allowBlank && !this.getRawValue().length){
9469             return;
9470         }
9471         
9472         if(this.indicator){
9473             this.indicator.removeClass('invisible');
9474             this.indicator.addClass('visible');
9475         }
9476         
9477         this.el.addClass(this.invalidClass);
9478         
9479         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9480             
9481             var feedback = this.el.select('.form-control-feedback', true).first();
9482             
9483             if(feedback){
9484                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9485                 
9486                 if(this.getValue().length || this.forceFeedback){
9487                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9488                 }
9489                 
9490             }
9491             
9492         }
9493         
9494         this.fireEvent('invalid', this, msg);
9495     },
9496     // private
9497     SafariOnKeyDown : function(event)
9498     {
9499         // this is a workaround for a password hang bug on chrome/ webkit.
9500         if (this.inputEl().dom.type != 'password') {
9501             return;
9502         }
9503         
9504         var isSelectAll = false;
9505         
9506         if(this.inputEl().dom.selectionEnd > 0){
9507             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9508         }
9509         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9510             event.preventDefault();
9511             this.setValue('');
9512             return;
9513         }
9514         
9515         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9516             
9517             event.preventDefault();
9518             // this is very hacky as keydown always get's upper case.
9519             //
9520             var cc = String.fromCharCode(event.getCharCode());
9521             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9522             
9523         }
9524     },
9525     adjustWidth : function(tag, w){
9526         tag = tag.toLowerCase();
9527         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9528             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9529                 if(tag == 'input'){
9530                     return w + 2;
9531                 }
9532                 if(tag == 'textarea'){
9533                     return w-2;
9534                 }
9535             }else if(Roo.isOpera){
9536                 if(tag == 'input'){
9537                     return w + 2;
9538                 }
9539                 if(tag == 'textarea'){
9540                     return w-2;
9541                 }
9542             }
9543         }
9544         return w;
9545     },
9546     
9547     setFieldLabel : function(v)
9548     {
9549         if(!this.rendered){
9550             return;
9551         }
9552         
9553         if(this.indicator){
9554             var ar = this.el.select('label > span',true);
9555             
9556             if (ar.elements.length) {
9557                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9558                 this.fieldLabel = v;
9559                 return;
9560             }
9561             
9562             var br = this.el.select('label',true);
9563             
9564             if(br.elements.length) {
9565                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9566                 this.fieldLabel = v;
9567                 return;
9568             }
9569             
9570             Roo.log('Cannot Found any of label > span || label in input');
9571             return;
9572         }
9573         
9574         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9575         this.fieldLabel = v;
9576         
9577         
9578     }
9579 });
9580
9581  
9582 /*
9583  * - LGPL
9584  *
9585  * Input
9586  * 
9587  */
9588
9589 /**
9590  * @class Roo.bootstrap.TextArea
9591  * @extends Roo.bootstrap.Input
9592  * Bootstrap TextArea class
9593  * @cfg {Number} cols Specifies the visible width of a text area
9594  * @cfg {Number} rows Specifies the visible number of lines in a text area
9595  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9596  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9597  * @cfg {string} html text
9598  * 
9599  * @constructor
9600  * Create a new TextArea
9601  * @param {Object} config The config object
9602  */
9603
9604 Roo.bootstrap.TextArea = function(config){
9605     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9606    
9607 };
9608
9609 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9610      
9611     cols : false,
9612     rows : 5,
9613     readOnly : false,
9614     warp : 'soft',
9615     resize : false,
9616     value: false,
9617     html: false,
9618     
9619     getAutoCreate : function(){
9620         
9621         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9622         
9623         var id = Roo.id();
9624         
9625         var cfg = {};
9626         
9627         if(this.inputType != 'hidden'){
9628             cfg.cls = 'form-group' //input-group
9629         }
9630         
9631         var input =  {
9632             tag: 'textarea',
9633             id : id,
9634             warp : this.warp,
9635             rows : this.rows,
9636             value : this.value || '',
9637             html: this.html || '',
9638             cls : 'form-control',
9639             placeholder : this.placeholder || '' 
9640             
9641         };
9642         
9643         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9644             input.maxLength = this.maxLength;
9645         }
9646         
9647         if(this.resize){
9648             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9649         }
9650         
9651         if(this.cols){
9652             input.cols = this.cols;
9653         }
9654         
9655         if (this.readOnly) {
9656             input.readonly = true;
9657         }
9658         
9659         if (this.name) {
9660             input.name = this.name;
9661         }
9662         
9663         if (this.size) {
9664             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9665         }
9666         
9667         var settings=this;
9668         ['xs','sm','md','lg'].map(function(size){
9669             if (settings[size]) {
9670                 cfg.cls += ' col-' + size + '-' + settings[size];
9671             }
9672         });
9673         
9674         var inputblock = input;
9675         
9676         if(this.hasFeedback && !this.allowBlank){
9677             
9678             var feedback = {
9679                 tag: 'span',
9680                 cls: 'glyphicon form-control-feedback'
9681             };
9682
9683             inputblock = {
9684                 cls : 'has-feedback',
9685                 cn :  [
9686                     input,
9687                     feedback
9688                 ] 
9689             };  
9690         }
9691         
9692         
9693         if (this.before || this.after) {
9694             
9695             inputblock = {
9696                 cls : 'input-group',
9697                 cn :  [] 
9698             };
9699             if (this.before) {
9700                 inputblock.cn.push({
9701                     tag :'span',
9702                     cls : 'input-group-addon',
9703                     html : this.before
9704                 });
9705             }
9706             
9707             inputblock.cn.push(input);
9708             
9709             if(this.hasFeedback && !this.allowBlank){
9710                 inputblock.cls += ' has-feedback';
9711                 inputblock.cn.push(feedback);
9712             }
9713             
9714             if (this.after) {
9715                 inputblock.cn.push({
9716                     tag :'span',
9717                     cls : 'input-group-addon',
9718                     html : this.after
9719                 });
9720             }
9721             
9722         }
9723         
9724         if (align ==='left' && this.fieldLabel.length) {
9725             cfg.cn = [
9726                 {
9727                     tag: 'label',
9728                     'for' :  id,
9729                     cls : 'control-label',
9730                     html : this.fieldLabel
9731                 },
9732                 {
9733                     cls : "",
9734                     cn: [
9735                         inputblock
9736                     ]
9737                 }
9738
9739             ];
9740             
9741             if(this.labelWidth > 12){
9742                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9743             }
9744
9745             if(this.labelWidth < 13 && this.labelmd == 0){
9746                 this.labelmd = this.labelWidth;
9747             }
9748
9749             if(this.labellg > 0){
9750                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9751                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9752             }
9753
9754             if(this.labelmd > 0){
9755                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9756                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9757             }
9758
9759             if(this.labelsm > 0){
9760                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9761                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9762             }
9763
9764             if(this.labelxs > 0){
9765                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9766                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9767             }
9768             
9769         } else if ( this.fieldLabel.length) {
9770             cfg.cn = [
9771
9772                {
9773                    tag: 'label',
9774                    //cls : 'input-group-addon',
9775                    html : this.fieldLabel
9776
9777                },
9778
9779                inputblock
9780
9781            ];
9782
9783         } else {
9784
9785             cfg.cn = [
9786
9787                 inputblock
9788
9789             ];
9790                 
9791         }
9792         
9793         if (this.disabled) {
9794             input.disabled=true;
9795         }
9796         
9797         return cfg;
9798         
9799     },
9800     /**
9801      * return the real textarea element.
9802      */
9803     inputEl: function ()
9804     {
9805         return this.el.select('textarea.form-control',true).first();
9806     },
9807     
9808     /**
9809      * Clear any invalid styles/messages for this field
9810      */
9811     clearInvalid : function()
9812     {
9813         
9814         if(!this.el || this.preventMark){ // not rendered
9815             return;
9816         }
9817         
9818         var label = this.el.select('label', true).first();
9819         var icon = this.el.select('i.fa-star', true).first();
9820         
9821         if(label && icon){
9822             icon.remove();
9823         }
9824         
9825         this.el.removeClass(this.invalidClass);
9826         
9827         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9828             
9829             var feedback = this.el.select('.form-control-feedback', true).first();
9830             
9831             if(feedback){
9832                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9833             }
9834             
9835         }
9836         
9837         this.fireEvent('valid', this);
9838     },
9839     
9840      /**
9841      * Mark this field as valid
9842      */
9843     markValid : function()
9844     {
9845         if(!this.el  || this.preventMark){ // not rendered
9846             return;
9847         }
9848         
9849         this.el.removeClass([this.invalidClass, this.validClass]);
9850         
9851         var feedback = this.el.select('.form-control-feedback', true).first();
9852             
9853         if(feedback){
9854             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9855         }
9856
9857         if(this.disabled || this.allowBlank){
9858             return;
9859         }
9860         
9861         var label = this.el.select('label', true).first();
9862         var icon = this.el.select('i.fa-star', true).first();
9863         
9864         if(label && icon){
9865             icon.remove();
9866         }
9867         
9868         this.el.addClass(this.validClass);
9869         
9870         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9871             
9872             var feedback = this.el.select('.form-control-feedback', true).first();
9873             
9874             if(feedback){
9875                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9876                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9877             }
9878             
9879         }
9880         
9881         this.fireEvent('valid', this);
9882     },
9883     
9884      /**
9885      * Mark this field as invalid
9886      * @param {String} msg The validation message
9887      */
9888     markInvalid : function(msg)
9889     {
9890         if(!this.el  || this.preventMark){ // not rendered
9891             return;
9892         }
9893         
9894         this.el.removeClass([this.invalidClass, this.validClass]);
9895         
9896         var feedback = this.el.select('.form-control-feedback', true).first();
9897             
9898         if(feedback){
9899             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9900         }
9901
9902         if(this.disabled || this.allowBlank){
9903             return;
9904         }
9905         
9906         var label = this.el.select('label', true).first();
9907         var icon = this.el.select('i.fa-star', true).first();
9908         
9909         if(!this.getValue().length && label && !icon){
9910             this.el.createChild({
9911                 tag : 'i',
9912                 cls : 'text-danger fa fa-lg fa-star',
9913                 tooltip : 'This field is required',
9914                 style : 'margin-right:5px;'
9915             }, label, true);
9916         }
9917
9918         this.el.addClass(this.invalidClass);
9919         
9920         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9921             
9922             var feedback = this.el.select('.form-control-feedback', true).first();
9923             
9924             if(feedback){
9925                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9926                 
9927                 if(this.getValue().length || this.forceFeedback){
9928                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9929                 }
9930                 
9931             }
9932             
9933         }
9934         
9935         this.fireEvent('invalid', this, msg);
9936     }
9937 });
9938
9939  
9940 /*
9941  * - LGPL
9942  *
9943  * trigger field - base class for combo..
9944  * 
9945  */
9946  
9947 /**
9948  * @class Roo.bootstrap.TriggerField
9949  * @extends Roo.bootstrap.Input
9950  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9951  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9952  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9953  * for which you can provide a custom implementation.  For example:
9954  * <pre><code>
9955 var trigger = new Roo.bootstrap.TriggerField();
9956 trigger.onTriggerClick = myTriggerFn;
9957 trigger.applyTo('my-field');
9958 </code></pre>
9959  *
9960  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9961  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9962  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9963  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9964  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9965
9966  * @constructor
9967  * Create a new TriggerField.
9968  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9969  * to the base TextField)
9970  */
9971 Roo.bootstrap.TriggerField = function(config){
9972     this.mimicing = false;
9973     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9974 };
9975
9976 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9977     /**
9978      * @cfg {String} triggerClass A CSS class to apply to the trigger
9979      */
9980      /**
9981      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9982      */
9983     hideTrigger:false,
9984
9985     /**
9986      * @cfg {Boolean} removable (true|false) special filter default false
9987      */
9988     removable : false,
9989     
9990     /** @cfg {Boolean} grow @hide */
9991     /** @cfg {Number} growMin @hide */
9992     /** @cfg {Number} growMax @hide */
9993
9994     /**
9995      * @hide 
9996      * @method
9997      */
9998     autoSize: Roo.emptyFn,
9999     // private
10000     monitorTab : true,
10001     // private
10002     deferHeight : true,
10003
10004     
10005     actionMode : 'wrap',
10006     
10007     caret : false,
10008     
10009     
10010     getAutoCreate : function(){
10011        
10012         var align = this.labelAlign || this.parentLabelAlign();
10013         
10014         var id = Roo.id();
10015         
10016         var cfg = {
10017             cls: 'form-group' //input-group
10018         };
10019         
10020         
10021         var input =  {
10022             tag: 'input',
10023             id : id,
10024             type : this.inputType,
10025             cls : 'form-control',
10026             autocomplete: 'new-password',
10027             placeholder : this.placeholder || '' 
10028             
10029         };
10030         if (this.name) {
10031             input.name = this.name;
10032         }
10033         if (this.size) {
10034             input.cls += ' input-' + this.size;
10035         }
10036         
10037         if (this.disabled) {
10038             input.disabled=true;
10039         }
10040         
10041         var inputblock = input;
10042         
10043         if(this.hasFeedback && !this.allowBlank){
10044             
10045             var feedback = {
10046                 tag: 'span',
10047                 cls: 'glyphicon form-control-feedback'
10048             };
10049             
10050             if(this.removable && !this.editable && !this.tickable){
10051                 inputblock = {
10052                     cls : 'has-feedback',
10053                     cn :  [
10054                         inputblock,
10055                         {
10056                             tag: 'button',
10057                             html : 'x',
10058                             cls : 'roo-combo-removable-btn close'
10059                         },
10060                         feedback
10061                     ] 
10062                 };
10063             } else {
10064                 inputblock = {
10065                     cls : 'has-feedback',
10066                     cn :  [
10067                         inputblock,
10068                         feedback
10069                     ] 
10070                 };
10071             }
10072
10073         } else {
10074             if(this.removable && !this.editable && !this.tickable){
10075                 inputblock = {
10076                     cls : 'roo-removable',
10077                     cn :  [
10078                         inputblock,
10079                         {
10080                             tag: 'button',
10081                             html : 'x',
10082                             cls : 'roo-combo-removable-btn close'
10083                         }
10084                     ] 
10085                 };
10086             }
10087         }
10088         
10089         if (this.before || this.after) {
10090             
10091             inputblock = {
10092                 cls : 'input-group',
10093                 cn :  [] 
10094             };
10095             if (this.before) {
10096                 inputblock.cn.push({
10097                     tag :'span',
10098                     cls : 'input-group-addon',
10099                     html : this.before
10100                 });
10101             }
10102             
10103             inputblock.cn.push(input);
10104             
10105             if(this.hasFeedback && !this.allowBlank){
10106                 inputblock.cls += ' has-feedback';
10107                 inputblock.cn.push(feedback);
10108             }
10109             
10110             if (this.after) {
10111                 inputblock.cn.push({
10112                     tag :'span',
10113                     cls : 'input-group-addon',
10114                     html : this.after
10115                 });
10116             }
10117             
10118         };
10119         
10120         var box = {
10121             tag: 'div',
10122             cn: [
10123                 {
10124                     tag: 'input',
10125                     type : 'hidden',
10126                     cls: 'form-hidden-field'
10127                 },
10128                 inputblock
10129             ]
10130             
10131         };
10132         
10133         if(this.multiple){
10134             box = {
10135                 tag: 'div',
10136                 cn: [
10137                     {
10138                         tag: 'input',
10139                         type : 'hidden',
10140                         cls: 'form-hidden-field'
10141                     },
10142                     {
10143                         tag: 'ul',
10144                         cls: 'roo-select2-choices',
10145                         cn:[
10146                             {
10147                                 tag: 'li',
10148                                 cls: 'roo-select2-search-field',
10149                                 cn: [
10150
10151                                     inputblock
10152                                 ]
10153                             }
10154                         ]
10155                     }
10156                 ]
10157             }
10158         };
10159         
10160         var combobox = {
10161             cls: 'roo-select2-container input-group',
10162             cn: [
10163                 box
10164 //                {
10165 //                    tag: 'ul',
10166 //                    cls: 'typeahead typeahead-long dropdown-menu',
10167 //                    style: 'display:none'
10168 //                }
10169             ]
10170         };
10171         
10172         if(!this.multiple && this.showToggleBtn){
10173             
10174             var caret = {
10175                         tag: 'span',
10176                         cls: 'caret'
10177              };
10178             if (this.caret != false) {
10179                 caret = {
10180                      tag: 'i',
10181                      cls: 'fa fa-' + this.caret
10182                 };
10183                 
10184             }
10185             
10186             combobox.cn.push({
10187                 tag :'span',
10188                 cls : 'input-group-addon btn dropdown-toggle',
10189                 cn : [
10190                     caret,
10191                     {
10192                         tag: 'span',
10193                         cls: 'combobox-clear',
10194                         cn  : [
10195                             {
10196                                 tag : 'i',
10197                                 cls: 'icon-remove'
10198                             }
10199                         ]
10200                     }
10201                 ]
10202
10203             })
10204         }
10205         
10206         if(this.multiple){
10207             combobox.cls += ' roo-select2-container-multi';
10208         }
10209         
10210         if (align ==='left' && this.fieldLabel.length) {
10211             
10212             cfg.cls += ' roo-form-group-label-left';
10213
10214             cfg.cn = [
10215                 {
10216                     tag : 'i',
10217                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10218                     tooltip : 'This field is required'
10219                 },
10220                 {
10221                     tag: 'label',
10222                     'for' :  id,
10223                     cls : 'control-label',
10224                     html : this.fieldLabel
10225
10226                 },
10227                 {
10228                     cls : "", 
10229                     cn: [
10230                         combobox
10231                     ]
10232                 }
10233
10234             ];
10235             
10236             var labelCfg = cfg.cn[1];
10237             var contentCfg = cfg.cn[2];
10238             
10239             if(this.indicatorpos == 'right'){
10240                 cfg.cn = [
10241                     {
10242                         tag: 'label',
10243                         'for' :  id,
10244                         cls : 'control-label',
10245                         cn : [
10246                             {
10247                                 tag : 'span',
10248                                 html : this.fieldLabel
10249                             },
10250                             {
10251                                 tag : 'i',
10252                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10253                                 tooltip : 'This field is required'
10254                             }
10255                         ]
10256                     },
10257                     {
10258                         cls : "", 
10259                         cn: [
10260                             combobox
10261                         ]
10262                     }
10263
10264                 ];
10265                 
10266                 labelCfg = cfg.cn[0];
10267                 contentCfg = cfg.cn[1];
10268             }
10269             
10270             if(this.labelWidth > 12){
10271                 labelCfg.style = "width: " + this.labelWidth + 'px';
10272             }
10273             
10274             if(this.labelWidth < 13 && this.labelmd == 0){
10275                 this.labelmd = this.labelWidth;
10276             }
10277             
10278             if(this.labellg > 0){
10279                 labelCfg.cls += ' col-lg-' + this.labellg;
10280                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10281             }
10282             
10283             if(this.labelmd > 0){
10284                 labelCfg.cls += ' col-md-' + this.labelmd;
10285                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10286             }
10287             
10288             if(this.labelsm > 0){
10289                 labelCfg.cls += ' col-sm-' + this.labelsm;
10290                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10291             }
10292             
10293             if(this.labelxs > 0){
10294                 labelCfg.cls += ' col-xs-' + this.labelxs;
10295                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10296             }
10297             
10298         } else if ( this.fieldLabel.length) {
10299 //                Roo.log(" label");
10300             cfg.cn = [
10301                 {
10302                    tag : 'i',
10303                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10304                    tooltip : 'This field is required'
10305                },
10306                {
10307                    tag: 'label',
10308                    //cls : 'input-group-addon',
10309                    html : this.fieldLabel
10310
10311                },
10312
10313                combobox
10314
10315             ];
10316             
10317             if(this.indicatorpos == 'right'){
10318                 
10319                 cfg.cn = [
10320                     {
10321                        tag: 'label',
10322                        cn : [
10323                            {
10324                                tag : 'span',
10325                                html : this.fieldLabel
10326                            },
10327                            {
10328                               tag : 'i',
10329                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10330                               tooltip : 'This field is required'
10331                            }
10332                        ]
10333
10334                     },
10335                     combobox
10336
10337                 ];
10338
10339             }
10340
10341         } else {
10342             
10343 //                Roo.log(" no label && no align");
10344                 cfg = combobox
10345                      
10346                 
10347         }
10348         
10349         var settings=this;
10350         ['xs','sm','md','lg'].map(function(size){
10351             if (settings[size]) {
10352                 cfg.cls += ' col-' + size + '-' + settings[size];
10353             }
10354         });
10355         
10356         return cfg;
10357         
10358     },
10359     
10360     
10361     
10362     // private
10363     onResize : function(w, h){
10364 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10365 //        if(typeof w == 'number'){
10366 //            var x = w - this.trigger.getWidth();
10367 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10368 //            this.trigger.setStyle('left', x+'px');
10369 //        }
10370     },
10371
10372     // private
10373     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10374
10375     // private
10376     getResizeEl : function(){
10377         return this.inputEl();
10378     },
10379
10380     // private
10381     getPositionEl : function(){
10382         return this.inputEl();
10383     },
10384
10385     // private
10386     alignErrorIcon : function(){
10387         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10388     },
10389
10390     // private
10391     initEvents : function(){
10392         
10393         this.createList();
10394         
10395         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10396         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10397         if(!this.multiple && this.showToggleBtn){
10398             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10399             if(this.hideTrigger){
10400                 this.trigger.setDisplayed(false);
10401             }
10402             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10403         }
10404         
10405         if(this.multiple){
10406             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10407         }
10408         
10409         if(this.removable && !this.editable && !this.tickable){
10410             var close = this.closeTriggerEl();
10411             
10412             if(close){
10413                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10414                 close.on('click', this.removeBtnClick, this, close);
10415             }
10416         }
10417         
10418         //this.trigger.addClassOnOver('x-form-trigger-over');
10419         //this.trigger.addClassOnClick('x-form-trigger-click');
10420         
10421         //if(!this.width){
10422         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10423         //}
10424     },
10425     
10426     closeTriggerEl : function()
10427     {
10428         var close = this.el.select('.roo-combo-removable-btn', true).first();
10429         return close ? close : false;
10430     },
10431     
10432     removeBtnClick : function(e, h, el)
10433     {
10434         e.preventDefault();
10435         
10436         if(this.fireEvent("remove", this) !== false){
10437             this.reset();
10438             this.fireEvent("afterremove", this)
10439         }
10440     },
10441     
10442     createList : function()
10443     {
10444         this.list = Roo.get(document.body).createChild({
10445             tag: 'ul',
10446             cls: 'typeahead typeahead-long dropdown-menu',
10447             style: 'display:none'
10448         });
10449         
10450         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10451         
10452     },
10453
10454     // private
10455     initTrigger : function(){
10456        
10457     },
10458
10459     // private
10460     onDestroy : function(){
10461         if(this.trigger){
10462             this.trigger.removeAllListeners();
10463           //  this.trigger.remove();
10464         }
10465         //if(this.wrap){
10466         //    this.wrap.remove();
10467         //}
10468         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10469     },
10470
10471     // private
10472     onFocus : function(){
10473         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10474         /*
10475         if(!this.mimicing){
10476             this.wrap.addClass('x-trigger-wrap-focus');
10477             this.mimicing = true;
10478             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10479             if(this.monitorTab){
10480                 this.el.on("keydown", this.checkTab, this);
10481             }
10482         }
10483         */
10484     },
10485
10486     // private
10487     checkTab : function(e){
10488         if(e.getKey() == e.TAB){
10489             this.triggerBlur();
10490         }
10491     },
10492
10493     // private
10494     onBlur : function(){
10495         // do nothing
10496     },
10497
10498     // private
10499     mimicBlur : function(e, t){
10500         /*
10501         if(!this.wrap.contains(t) && this.validateBlur()){
10502             this.triggerBlur();
10503         }
10504         */
10505     },
10506
10507     // private
10508     triggerBlur : function(){
10509         this.mimicing = false;
10510         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10511         if(this.monitorTab){
10512             this.el.un("keydown", this.checkTab, this);
10513         }
10514         //this.wrap.removeClass('x-trigger-wrap-focus');
10515         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10516     },
10517
10518     // private
10519     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10520     validateBlur : function(e, t){
10521         return true;
10522     },
10523
10524     // private
10525     onDisable : function(){
10526         this.inputEl().dom.disabled = true;
10527         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10528         //if(this.wrap){
10529         //    this.wrap.addClass('x-item-disabled');
10530         //}
10531     },
10532
10533     // private
10534     onEnable : function(){
10535         this.inputEl().dom.disabled = false;
10536         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10537         //if(this.wrap){
10538         //    this.el.removeClass('x-item-disabled');
10539         //}
10540     },
10541
10542     // private
10543     onShow : function(){
10544         var ae = this.getActionEl();
10545         
10546         if(ae){
10547             ae.dom.style.display = '';
10548             ae.dom.style.visibility = 'visible';
10549         }
10550     },
10551
10552     // private
10553     
10554     onHide : function(){
10555         var ae = this.getActionEl();
10556         ae.dom.style.display = 'none';
10557     },
10558
10559     /**
10560      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10561      * by an implementing function.
10562      * @method
10563      * @param {EventObject} e
10564      */
10565     onTriggerClick : Roo.emptyFn
10566 });
10567  /*
10568  * Based on:
10569  * Ext JS Library 1.1.1
10570  * Copyright(c) 2006-2007, Ext JS, LLC.
10571  *
10572  * Originally Released Under LGPL - original licence link has changed is not relivant.
10573  *
10574  * Fork - LGPL
10575  * <script type="text/javascript">
10576  */
10577
10578
10579 /**
10580  * @class Roo.data.SortTypes
10581  * @singleton
10582  * Defines the default sorting (casting?) comparison functions used when sorting data.
10583  */
10584 Roo.data.SortTypes = {
10585     /**
10586      * Default sort that does nothing
10587      * @param {Mixed} s The value being converted
10588      * @return {Mixed} The comparison value
10589      */
10590     none : function(s){
10591         return s;
10592     },
10593     
10594     /**
10595      * The regular expression used to strip tags
10596      * @type {RegExp}
10597      * @property
10598      */
10599     stripTagsRE : /<\/?[^>]+>/gi,
10600     
10601     /**
10602      * Strips all HTML tags to sort on text only
10603      * @param {Mixed} s The value being converted
10604      * @return {String} The comparison value
10605      */
10606     asText : function(s){
10607         return String(s).replace(this.stripTagsRE, "");
10608     },
10609     
10610     /**
10611      * Strips all HTML tags to sort on text only - Case insensitive
10612      * @param {Mixed} s The value being converted
10613      * @return {String} The comparison value
10614      */
10615     asUCText : function(s){
10616         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10617     },
10618     
10619     /**
10620      * Case insensitive string
10621      * @param {Mixed} s The value being converted
10622      * @return {String} The comparison value
10623      */
10624     asUCString : function(s) {
10625         return String(s).toUpperCase();
10626     },
10627     
10628     /**
10629      * Date sorting
10630      * @param {Mixed} s The value being converted
10631      * @return {Number} The comparison value
10632      */
10633     asDate : function(s) {
10634         if(!s){
10635             return 0;
10636         }
10637         if(s instanceof Date){
10638             return s.getTime();
10639         }
10640         return Date.parse(String(s));
10641     },
10642     
10643     /**
10644      * Float sorting
10645      * @param {Mixed} s The value being converted
10646      * @return {Float} The comparison value
10647      */
10648     asFloat : function(s) {
10649         var val = parseFloat(String(s).replace(/,/g, ""));
10650         if(isNaN(val)) {
10651             val = 0;
10652         }
10653         return val;
10654     },
10655     
10656     /**
10657      * Integer sorting
10658      * @param {Mixed} s The value being converted
10659      * @return {Number} The comparison value
10660      */
10661     asInt : function(s) {
10662         var val = parseInt(String(s).replace(/,/g, ""));
10663         if(isNaN(val)) {
10664             val = 0;
10665         }
10666         return val;
10667     }
10668 };/*
10669  * Based on:
10670  * Ext JS Library 1.1.1
10671  * Copyright(c) 2006-2007, Ext JS, LLC.
10672  *
10673  * Originally Released Under LGPL - original licence link has changed is not relivant.
10674  *
10675  * Fork - LGPL
10676  * <script type="text/javascript">
10677  */
10678
10679 /**
10680 * @class Roo.data.Record
10681  * Instances of this class encapsulate both record <em>definition</em> information, and record
10682  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10683  * to access Records cached in an {@link Roo.data.Store} object.<br>
10684  * <p>
10685  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10686  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10687  * objects.<br>
10688  * <p>
10689  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10690  * @constructor
10691  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10692  * {@link #create}. The parameters are the same.
10693  * @param {Array} data An associative Array of data values keyed by the field name.
10694  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10695  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10696  * not specified an integer id is generated.
10697  */
10698 Roo.data.Record = function(data, id){
10699     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10700     this.data = data;
10701 };
10702
10703 /**
10704  * Generate a constructor for a specific record layout.
10705  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10706  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10707  * Each field definition object may contain the following properties: <ul>
10708  * <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,
10709  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10710  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10711  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10712  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10713  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10714  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10715  * this may be omitted.</p></li>
10716  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10717  * <ul><li>auto (Default, implies no conversion)</li>
10718  * <li>string</li>
10719  * <li>int</li>
10720  * <li>float</li>
10721  * <li>boolean</li>
10722  * <li>date</li></ul></p></li>
10723  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10724  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10725  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10726  * by the Reader into an object that will be stored in the Record. It is passed the
10727  * following parameters:<ul>
10728  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10729  * </ul></p></li>
10730  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10731  * </ul>
10732  * <br>usage:<br><pre><code>
10733 var TopicRecord = Roo.data.Record.create(
10734     {name: 'title', mapping: 'topic_title'},
10735     {name: 'author', mapping: 'username'},
10736     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10737     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10738     {name: 'lastPoster', mapping: 'user2'},
10739     {name: 'excerpt', mapping: 'post_text'}
10740 );
10741
10742 var myNewRecord = new TopicRecord({
10743     title: 'Do my job please',
10744     author: 'noobie',
10745     totalPosts: 1,
10746     lastPost: new Date(),
10747     lastPoster: 'Animal',
10748     excerpt: 'No way dude!'
10749 });
10750 myStore.add(myNewRecord);
10751 </code></pre>
10752  * @method create
10753  * @static
10754  */
10755 Roo.data.Record.create = function(o){
10756     var f = function(){
10757         f.superclass.constructor.apply(this, arguments);
10758     };
10759     Roo.extend(f, Roo.data.Record);
10760     var p = f.prototype;
10761     p.fields = new Roo.util.MixedCollection(false, function(field){
10762         return field.name;
10763     });
10764     for(var i = 0, len = o.length; i < len; i++){
10765         p.fields.add(new Roo.data.Field(o[i]));
10766     }
10767     f.getField = function(name){
10768         return p.fields.get(name);  
10769     };
10770     return f;
10771 };
10772
10773 Roo.data.Record.AUTO_ID = 1000;
10774 Roo.data.Record.EDIT = 'edit';
10775 Roo.data.Record.REJECT = 'reject';
10776 Roo.data.Record.COMMIT = 'commit';
10777
10778 Roo.data.Record.prototype = {
10779     /**
10780      * Readonly flag - true if this record has been modified.
10781      * @type Boolean
10782      */
10783     dirty : false,
10784     editing : false,
10785     error: null,
10786     modified: null,
10787
10788     // private
10789     join : function(store){
10790         this.store = store;
10791     },
10792
10793     /**
10794      * Set the named field to the specified value.
10795      * @param {String} name The name of the field to set.
10796      * @param {Object} value The value to set the field to.
10797      */
10798     set : function(name, value){
10799         if(this.data[name] == value){
10800             return;
10801         }
10802         this.dirty = true;
10803         if(!this.modified){
10804             this.modified = {};
10805         }
10806         if(typeof this.modified[name] == 'undefined'){
10807             this.modified[name] = this.data[name];
10808         }
10809         this.data[name] = value;
10810         if(!this.editing && this.store){
10811             this.store.afterEdit(this);
10812         }       
10813     },
10814
10815     /**
10816      * Get the value of the named field.
10817      * @param {String} name The name of the field to get the value of.
10818      * @return {Object} The value of the field.
10819      */
10820     get : function(name){
10821         return this.data[name]; 
10822     },
10823
10824     // private
10825     beginEdit : function(){
10826         this.editing = true;
10827         this.modified = {}; 
10828     },
10829
10830     // private
10831     cancelEdit : function(){
10832         this.editing = false;
10833         delete this.modified;
10834     },
10835
10836     // private
10837     endEdit : function(){
10838         this.editing = false;
10839         if(this.dirty && this.store){
10840             this.store.afterEdit(this);
10841         }
10842     },
10843
10844     /**
10845      * Usually called by the {@link Roo.data.Store} which owns the Record.
10846      * Rejects all changes made to the Record since either creation, or the last commit operation.
10847      * Modified fields are reverted to their original values.
10848      * <p>
10849      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10850      * of reject operations.
10851      */
10852     reject : function(){
10853         var m = this.modified;
10854         for(var n in m){
10855             if(typeof m[n] != "function"){
10856                 this.data[n] = m[n];
10857             }
10858         }
10859         this.dirty = false;
10860         delete this.modified;
10861         this.editing = false;
10862         if(this.store){
10863             this.store.afterReject(this);
10864         }
10865     },
10866
10867     /**
10868      * Usually called by the {@link Roo.data.Store} which owns the Record.
10869      * Commits all changes made to the Record since either creation, or the last commit operation.
10870      * <p>
10871      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10872      * of commit operations.
10873      */
10874     commit : function(){
10875         this.dirty = false;
10876         delete this.modified;
10877         this.editing = false;
10878         if(this.store){
10879             this.store.afterCommit(this);
10880         }
10881     },
10882
10883     // private
10884     hasError : function(){
10885         return this.error != null;
10886     },
10887
10888     // private
10889     clearError : function(){
10890         this.error = null;
10891     },
10892
10893     /**
10894      * Creates a copy of this record.
10895      * @param {String} id (optional) A new record id if you don't want to use this record's id
10896      * @return {Record}
10897      */
10898     copy : function(newId) {
10899         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10900     }
10901 };/*
10902  * Based on:
10903  * Ext JS Library 1.1.1
10904  * Copyright(c) 2006-2007, Ext JS, LLC.
10905  *
10906  * Originally Released Under LGPL - original licence link has changed is not relivant.
10907  *
10908  * Fork - LGPL
10909  * <script type="text/javascript">
10910  */
10911
10912
10913
10914 /**
10915  * @class Roo.data.Store
10916  * @extends Roo.util.Observable
10917  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10918  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10919  * <p>
10920  * 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
10921  * has no knowledge of the format of the data returned by the Proxy.<br>
10922  * <p>
10923  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10924  * instances from the data object. These records are cached and made available through accessor functions.
10925  * @constructor
10926  * Creates a new Store.
10927  * @param {Object} config A config object containing the objects needed for the Store to access data,
10928  * and read the data into Records.
10929  */
10930 Roo.data.Store = function(config){
10931     this.data = new Roo.util.MixedCollection(false);
10932     this.data.getKey = function(o){
10933         return o.id;
10934     };
10935     this.baseParams = {};
10936     // private
10937     this.paramNames = {
10938         "start" : "start",
10939         "limit" : "limit",
10940         "sort" : "sort",
10941         "dir" : "dir",
10942         "multisort" : "_multisort"
10943     };
10944
10945     if(config && config.data){
10946         this.inlineData = config.data;
10947         delete config.data;
10948     }
10949
10950     Roo.apply(this, config);
10951     
10952     if(this.reader){ // reader passed
10953         this.reader = Roo.factory(this.reader, Roo.data);
10954         this.reader.xmodule = this.xmodule || false;
10955         if(!this.recordType){
10956             this.recordType = this.reader.recordType;
10957         }
10958         if(this.reader.onMetaChange){
10959             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10960         }
10961     }
10962
10963     if(this.recordType){
10964         this.fields = this.recordType.prototype.fields;
10965     }
10966     this.modified = [];
10967
10968     this.addEvents({
10969         /**
10970          * @event datachanged
10971          * Fires when the data cache has changed, and a widget which is using this Store
10972          * as a Record cache should refresh its view.
10973          * @param {Store} this
10974          */
10975         datachanged : true,
10976         /**
10977          * @event metachange
10978          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10979          * @param {Store} this
10980          * @param {Object} meta The JSON metadata
10981          */
10982         metachange : true,
10983         /**
10984          * @event add
10985          * Fires when Records have been added to the Store
10986          * @param {Store} this
10987          * @param {Roo.data.Record[]} records The array of Records added
10988          * @param {Number} index The index at which the record(s) were added
10989          */
10990         add : true,
10991         /**
10992          * @event remove
10993          * Fires when a Record has been removed from the Store
10994          * @param {Store} this
10995          * @param {Roo.data.Record} record The Record that was removed
10996          * @param {Number} index The index at which the record was removed
10997          */
10998         remove : true,
10999         /**
11000          * @event update
11001          * Fires when a Record has been updated
11002          * @param {Store} this
11003          * @param {Roo.data.Record} record The Record that was updated
11004          * @param {String} operation The update operation being performed.  Value may be one of:
11005          * <pre><code>
11006  Roo.data.Record.EDIT
11007  Roo.data.Record.REJECT
11008  Roo.data.Record.COMMIT
11009          * </code></pre>
11010          */
11011         update : true,
11012         /**
11013          * @event clear
11014          * Fires when the data cache has been cleared.
11015          * @param {Store} this
11016          */
11017         clear : true,
11018         /**
11019          * @event beforeload
11020          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11021          * the load action will be canceled.
11022          * @param {Store} this
11023          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11024          */
11025         beforeload : true,
11026         /**
11027          * @event beforeloadadd
11028          * Fires after a new set of Records has been loaded.
11029          * @param {Store} this
11030          * @param {Roo.data.Record[]} records The Records that were loaded
11031          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11032          */
11033         beforeloadadd : true,
11034         /**
11035          * @event load
11036          * Fires after a new set of Records has been loaded, before they are added to the store.
11037          * @param {Store} this
11038          * @param {Roo.data.Record[]} records The Records that were loaded
11039          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11040          * @params {Object} return from reader
11041          */
11042         load : true,
11043         /**
11044          * @event loadexception
11045          * Fires if an exception occurs in the Proxy during loading.
11046          * Called with the signature of the Proxy's "loadexception" event.
11047          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11048          * 
11049          * @param {Proxy} 
11050          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11051          * @param {Object} load options 
11052          * @param {Object} jsonData from your request (normally this contains the Exception)
11053          */
11054         loadexception : true
11055     });
11056     
11057     if(this.proxy){
11058         this.proxy = Roo.factory(this.proxy, Roo.data);
11059         this.proxy.xmodule = this.xmodule || false;
11060         this.relayEvents(this.proxy,  ["loadexception"]);
11061     }
11062     this.sortToggle = {};
11063     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11064
11065     Roo.data.Store.superclass.constructor.call(this);
11066
11067     if(this.inlineData){
11068         this.loadData(this.inlineData);
11069         delete this.inlineData;
11070     }
11071 };
11072
11073 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11074      /**
11075     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11076     * without a remote query - used by combo/forms at present.
11077     */
11078     
11079     /**
11080     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11081     */
11082     /**
11083     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11084     */
11085     /**
11086     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11087     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11088     */
11089     /**
11090     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11091     * on any HTTP request
11092     */
11093     /**
11094     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11095     */
11096     /**
11097     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11098     */
11099     multiSort: false,
11100     /**
11101     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11102     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11103     */
11104     remoteSort : false,
11105
11106     /**
11107     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11108      * loaded or when a record is removed. (defaults to false).
11109     */
11110     pruneModifiedRecords : false,
11111
11112     // private
11113     lastOptions : null,
11114
11115     /**
11116      * Add Records to the Store and fires the add event.
11117      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11118      */
11119     add : function(records){
11120         records = [].concat(records);
11121         for(var i = 0, len = records.length; i < len; i++){
11122             records[i].join(this);
11123         }
11124         var index = this.data.length;
11125         this.data.addAll(records);
11126         this.fireEvent("add", this, records, index);
11127     },
11128
11129     /**
11130      * Remove a Record from the Store and fires the remove event.
11131      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11132      */
11133     remove : function(record){
11134         var index = this.data.indexOf(record);
11135         this.data.removeAt(index);
11136         if(this.pruneModifiedRecords){
11137             this.modified.remove(record);
11138         }
11139         this.fireEvent("remove", this, record, index);
11140     },
11141
11142     /**
11143      * Remove all Records from the Store and fires the clear event.
11144      */
11145     removeAll : function(){
11146         this.data.clear();
11147         if(this.pruneModifiedRecords){
11148             this.modified = [];
11149         }
11150         this.fireEvent("clear", this);
11151     },
11152
11153     /**
11154      * Inserts Records to the Store at the given index and fires the add event.
11155      * @param {Number} index The start index at which to insert the passed Records.
11156      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11157      */
11158     insert : function(index, records){
11159         records = [].concat(records);
11160         for(var i = 0, len = records.length; i < len; i++){
11161             this.data.insert(index, records[i]);
11162             records[i].join(this);
11163         }
11164         this.fireEvent("add", this, records, index);
11165     },
11166
11167     /**
11168      * Get the index within the cache of the passed Record.
11169      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11170      * @return {Number} The index of the passed Record. Returns -1 if not found.
11171      */
11172     indexOf : function(record){
11173         return this.data.indexOf(record);
11174     },
11175
11176     /**
11177      * Get the index within the cache of the Record with the passed id.
11178      * @param {String} id The id of the Record to find.
11179      * @return {Number} The index of the Record. Returns -1 if not found.
11180      */
11181     indexOfId : function(id){
11182         return this.data.indexOfKey(id);
11183     },
11184
11185     /**
11186      * Get the Record with the specified id.
11187      * @param {String} id The id of the Record to find.
11188      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11189      */
11190     getById : function(id){
11191         return this.data.key(id);
11192     },
11193
11194     /**
11195      * Get the Record at the specified index.
11196      * @param {Number} index The index of the Record to find.
11197      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11198      */
11199     getAt : function(index){
11200         return this.data.itemAt(index);
11201     },
11202
11203     /**
11204      * Returns a range of Records between specified indices.
11205      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11206      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11207      * @return {Roo.data.Record[]} An array of Records
11208      */
11209     getRange : function(start, end){
11210         return this.data.getRange(start, end);
11211     },
11212
11213     // private
11214     storeOptions : function(o){
11215         o = Roo.apply({}, o);
11216         delete o.callback;
11217         delete o.scope;
11218         this.lastOptions = o;
11219     },
11220
11221     /**
11222      * Loads the Record cache from the configured Proxy using the configured Reader.
11223      * <p>
11224      * If using remote paging, then the first load call must specify the <em>start</em>
11225      * and <em>limit</em> properties in the options.params property to establish the initial
11226      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11227      * <p>
11228      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11229      * and this call will return before the new data has been loaded. Perform any post-processing
11230      * in a callback function, or in a "load" event handler.</strong>
11231      * <p>
11232      * @param {Object} options An object containing properties which control loading options:<ul>
11233      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11234      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11235      * passed the following arguments:<ul>
11236      * <li>r : Roo.data.Record[]</li>
11237      * <li>options: Options object from the load call</li>
11238      * <li>success: Boolean success indicator</li></ul></li>
11239      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11240      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11241      * </ul>
11242      */
11243     load : function(options){
11244         options = options || {};
11245         if(this.fireEvent("beforeload", this, options) !== false){
11246             this.storeOptions(options);
11247             var p = Roo.apply(options.params || {}, this.baseParams);
11248             // if meta was not loaded from remote source.. try requesting it.
11249             if (!this.reader.metaFromRemote) {
11250                 p._requestMeta = 1;
11251             }
11252             if(this.sortInfo && this.remoteSort){
11253                 var pn = this.paramNames;
11254                 p[pn["sort"]] = this.sortInfo.field;
11255                 p[pn["dir"]] = this.sortInfo.direction;
11256             }
11257             if (this.multiSort) {
11258                 var pn = this.paramNames;
11259                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11260             }
11261             
11262             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11263         }
11264     },
11265
11266     /**
11267      * Reloads the Record cache from the configured Proxy using the configured Reader and
11268      * the options from the last load operation performed.
11269      * @param {Object} options (optional) An object containing properties which may override the options
11270      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11271      * the most recently used options are reused).
11272      */
11273     reload : function(options){
11274         this.load(Roo.applyIf(options||{}, this.lastOptions));
11275     },
11276
11277     // private
11278     // Called as a callback by the Reader during a load operation.
11279     loadRecords : function(o, options, success){
11280         if(!o || success === false){
11281             if(success !== false){
11282                 this.fireEvent("load", this, [], options, o);
11283             }
11284             if(options.callback){
11285                 options.callback.call(options.scope || this, [], options, false);
11286             }
11287             return;
11288         }
11289         // if data returned failure - throw an exception.
11290         if (o.success === false) {
11291             // show a message if no listener is registered.
11292             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11293                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11294             }
11295             // loadmask wil be hooked into this..
11296             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11297             return;
11298         }
11299         var r = o.records, t = o.totalRecords || r.length;
11300         
11301         this.fireEvent("beforeloadadd", this, r, options, o);
11302         
11303         if(!options || options.add !== true){
11304             if(this.pruneModifiedRecords){
11305                 this.modified = [];
11306             }
11307             for(var i = 0, len = r.length; i < len; i++){
11308                 r[i].join(this);
11309             }
11310             if(this.snapshot){
11311                 this.data = this.snapshot;
11312                 delete this.snapshot;
11313             }
11314             this.data.clear();
11315             this.data.addAll(r);
11316             this.totalLength = t;
11317             this.applySort();
11318             this.fireEvent("datachanged", this);
11319         }else{
11320             this.totalLength = Math.max(t, this.data.length+r.length);
11321             this.add(r);
11322         }
11323         
11324         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11325                 
11326             var e = new Roo.data.Record({});
11327
11328             e.set(this.parent.displayField, this.parent.emptyTitle);
11329             e.set(this.parent.valueField, '');
11330
11331             this.insert(0, e);
11332         }
11333             
11334         this.fireEvent("load", this, r, options, o);
11335         if(options.callback){
11336             options.callback.call(options.scope || this, r, options, true);
11337         }
11338     },
11339
11340
11341     /**
11342      * Loads data from a passed data block. A Reader which understands the format of the data
11343      * must have been configured in the constructor.
11344      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11345      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11346      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11347      */
11348     loadData : function(o, append){
11349         var r = this.reader.readRecords(o);
11350         this.loadRecords(r, {add: append}, true);
11351     },
11352
11353     /**
11354      * Gets the number of cached records.
11355      * <p>
11356      * <em>If using paging, this may not be the total size of the dataset. If the data object
11357      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11358      * the data set size</em>
11359      */
11360     getCount : function(){
11361         return this.data.length || 0;
11362     },
11363
11364     /**
11365      * Gets the total number of records in the dataset as returned by the server.
11366      * <p>
11367      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11368      * the dataset size</em>
11369      */
11370     getTotalCount : function(){
11371         return this.totalLength || 0;
11372     },
11373
11374     /**
11375      * Returns the sort state of the Store as an object with two properties:
11376      * <pre><code>
11377  field {String} The name of the field by which the Records are sorted
11378  direction {String} The sort order, "ASC" or "DESC"
11379      * </code></pre>
11380      */
11381     getSortState : function(){
11382         return this.sortInfo;
11383     },
11384
11385     // private
11386     applySort : function(){
11387         if(this.sortInfo && !this.remoteSort){
11388             var s = this.sortInfo, f = s.field;
11389             var st = this.fields.get(f).sortType;
11390             var fn = function(r1, r2){
11391                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11392                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11393             };
11394             this.data.sort(s.direction, fn);
11395             if(this.snapshot && this.snapshot != this.data){
11396                 this.snapshot.sort(s.direction, fn);
11397             }
11398         }
11399     },
11400
11401     /**
11402      * Sets the default sort column and order to be used by the next load operation.
11403      * @param {String} fieldName The name of the field to sort by.
11404      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11405      */
11406     setDefaultSort : function(field, dir){
11407         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11408     },
11409
11410     /**
11411      * Sort the Records.
11412      * If remote sorting is used, the sort is performed on the server, and the cache is
11413      * reloaded. If local sorting is used, the cache is sorted internally.
11414      * @param {String} fieldName The name of the field to sort by.
11415      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11416      */
11417     sort : function(fieldName, dir){
11418         var f = this.fields.get(fieldName);
11419         if(!dir){
11420             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11421             
11422             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11423                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11424             }else{
11425                 dir = f.sortDir;
11426             }
11427         }
11428         this.sortToggle[f.name] = dir;
11429         this.sortInfo = {field: f.name, direction: dir};
11430         if(!this.remoteSort){
11431             this.applySort();
11432             this.fireEvent("datachanged", this);
11433         }else{
11434             this.load(this.lastOptions);
11435         }
11436     },
11437
11438     /**
11439      * Calls the specified function for each of the Records in the cache.
11440      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11441      * Returning <em>false</em> aborts and exits the iteration.
11442      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11443      */
11444     each : function(fn, scope){
11445         this.data.each(fn, scope);
11446     },
11447
11448     /**
11449      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11450      * (e.g., during paging).
11451      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11452      */
11453     getModifiedRecords : function(){
11454         return this.modified;
11455     },
11456
11457     // private
11458     createFilterFn : function(property, value, anyMatch){
11459         if(!value.exec){ // not a regex
11460             value = String(value);
11461             if(value.length == 0){
11462                 return false;
11463             }
11464             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11465         }
11466         return function(r){
11467             return value.test(r.data[property]);
11468         };
11469     },
11470
11471     /**
11472      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11473      * @param {String} property A field on your records
11474      * @param {Number} start The record index to start at (defaults to 0)
11475      * @param {Number} end The last record index to include (defaults to length - 1)
11476      * @return {Number} The sum
11477      */
11478     sum : function(property, start, end){
11479         var rs = this.data.items, v = 0;
11480         start = start || 0;
11481         end = (end || end === 0) ? end : rs.length-1;
11482
11483         for(var i = start; i <= end; i++){
11484             v += (rs[i].data[property] || 0);
11485         }
11486         return v;
11487     },
11488
11489     /**
11490      * Filter the records by a specified property.
11491      * @param {String} field A field on your records
11492      * @param {String/RegExp} value Either a string that the field
11493      * should start with or a RegExp to test against the field
11494      * @param {Boolean} anyMatch True to match any part not just the beginning
11495      */
11496     filter : function(property, value, anyMatch){
11497         var fn = this.createFilterFn(property, value, anyMatch);
11498         return fn ? this.filterBy(fn) : this.clearFilter();
11499     },
11500
11501     /**
11502      * Filter by a function. The specified function will be called with each
11503      * record in this data source. If the function returns true the record is included,
11504      * otherwise it is filtered.
11505      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11506      * @param {Object} scope (optional) The scope of the function (defaults to this)
11507      */
11508     filterBy : function(fn, scope){
11509         this.snapshot = this.snapshot || this.data;
11510         this.data = this.queryBy(fn, scope||this);
11511         this.fireEvent("datachanged", this);
11512     },
11513
11514     /**
11515      * Query the records by a specified property.
11516      * @param {String} field A field on your records
11517      * @param {String/RegExp} value Either a string that the field
11518      * should start with or a RegExp to test against the field
11519      * @param {Boolean} anyMatch True to match any part not just the beginning
11520      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11521      */
11522     query : function(property, value, anyMatch){
11523         var fn = this.createFilterFn(property, value, anyMatch);
11524         return fn ? this.queryBy(fn) : this.data.clone();
11525     },
11526
11527     /**
11528      * Query by a function. The specified function will be called with each
11529      * record in this data source. If the function returns true the record is included
11530      * in the results.
11531      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11532      * @param {Object} scope (optional) The scope of the function (defaults to this)
11533       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11534      **/
11535     queryBy : function(fn, scope){
11536         var data = this.snapshot || this.data;
11537         return data.filterBy(fn, scope||this);
11538     },
11539
11540     /**
11541      * Collects unique values for a particular dataIndex from this store.
11542      * @param {String} dataIndex The property to collect
11543      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11544      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11545      * @return {Array} An array of the unique values
11546      **/
11547     collect : function(dataIndex, allowNull, bypassFilter){
11548         var d = (bypassFilter === true && this.snapshot) ?
11549                 this.snapshot.items : this.data.items;
11550         var v, sv, r = [], l = {};
11551         for(var i = 0, len = d.length; i < len; i++){
11552             v = d[i].data[dataIndex];
11553             sv = String(v);
11554             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11555                 l[sv] = true;
11556                 r[r.length] = v;
11557             }
11558         }
11559         return r;
11560     },
11561
11562     /**
11563      * Revert to a view of the Record cache with no filtering applied.
11564      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11565      */
11566     clearFilter : function(suppressEvent){
11567         if(this.snapshot && this.snapshot != this.data){
11568             this.data = this.snapshot;
11569             delete this.snapshot;
11570             if(suppressEvent !== true){
11571                 this.fireEvent("datachanged", this);
11572             }
11573         }
11574     },
11575
11576     // private
11577     afterEdit : function(record){
11578         if(this.modified.indexOf(record) == -1){
11579             this.modified.push(record);
11580         }
11581         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11582     },
11583     
11584     // private
11585     afterReject : function(record){
11586         this.modified.remove(record);
11587         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11588     },
11589
11590     // private
11591     afterCommit : function(record){
11592         this.modified.remove(record);
11593         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11594     },
11595
11596     /**
11597      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11598      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11599      */
11600     commitChanges : function(){
11601         var m = this.modified.slice(0);
11602         this.modified = [];
11603         for(var i = 0, len = m.length; i < len; i++){
11604             m[i].commit();
11605         }
11606     },
11607
11608     /**
11609      * Cancel outstanding changes on all changed records.
11610      */
11611     rejectChanges : function(){
11612         var m = this.modified.slice(0);
11613         this.modified = [];
11614         for(var i = 0, len = m.length; i < len; i++){
11615             m[i].reject();
11616         }
11617     },
11618
11619     onMetaChange : function(meta, rtype, o){
11620         this.recordType = rtype;
11621         this.fields = rtype.prototype.fields;
11622         delete this.snapshot;
11623         this.sortInfo = meta.sortInfo || this.sortInfo;
11624         this.modified = [];
11625         this.fireEvent('metachange', this, this.reader.meta);
11626     },
11627     
11628     moveIndex : function(data, type)
11629     {
11630         var index = this.indexOf(data);
11631         
11632         var newIndex = index + type;
11633         
11634         this.remove(data);
11635         
11636         this.insert(newIndex, data);
11637         
11638     }
11639 });/*
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  * @class Roo.data.SimpleStore
11652  * @extends Roo.data.Store
11653  * Small helper class to make creating Stores from Array data easier.
11654  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11655  * @cfg {Array} fields An array of field definition objects, or field name strings.
11656  * @cfg {Array} data The multi-dimensional array of data
11657  * @constructor
11658  * @param {Object} config
11659  */
11660 Roo.data.SimpleStore = function(config){
11661     Roo.data.SimpleStore.superclass.constructor.call(this, {
11662         isLocal : true,
11663         reader: new Roo.data.ArrayReader({
11664                 id: config.id
11665             },
11666             Roo.data.Record.create(config.fields)
11667         ),
11668         proxy : new Roo.data.MemoryProxy(config.data)
11669     });
11670     this.load();
11671 };
11672 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11673  * Based on:
11674  * Ext JS Library 1.1.1
11675  * Copyright(c) 2006-2007, Ext JS, LLC.
11676  *
11677  * Originally Released Under LGPL - original licence link has changed is not relivant.
11678  *
11679  * Fork - LGPL
11680  * <script type="text/javascript">
11681  */
11682
11683 /**
11684 /**
11685  * @extends Roo.data.Store
11686  * @class Roo.data.JsonStore
11687  * Small helper class to make creating Stores for JSON data easier. <br/>
11688 <pre><code>
11689 var store = new Roo.data.JsonStore({
11690     url: 'get-images.php',
11691     root: 'images',
11692     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11693 });
11694 </code></pre>
11695  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11696  * JsonReader and HttpProxy (unless inline data is provided).</b>
11697  * @cfg {Array} fields An array of field definition objects, or field name strings.
11698  * @constructor
11699  * @param {Object} config
11700  */
11701 Roo.data.JsonStore = function(c){
11702     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11703         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11704         reader: new Roo.data.JsonReader(c, c.fields)
11705     }));
11706 };
11707 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11708  * Based on:
11709  * Ext JS Library 1.1.1
11710  * Copyright(c) 2006-2007, Ext JS, LLC.
11711  *
11712  * Originally Released Under LGPL - original licence link has changed is not relivant.
11713  *
11714  * Fork - LGPL
11715  * <script type="text/javascript">
11716  */
11717
11718  
11719 Roo.data.Field = function(config){
11720     if(typeof config == "string"){
11721         config = {name: config};
11722     }
11723     Roo.apply(this, config);
11724     
11725     if(!this.type){
11726         this.type = "auto";
11727     }
11728     
11729     var st = Roo.data.SortTypes;
11730     // named sortTypes are supported, here we look them up
11731     if(typeof this.sortType == "string"){
11732         this.sortType = st[this.sortType];
11733     }
11734     
11735     // set default sortType for strings and dates
11736     if(!this.sortType){
11737         switch(this.type){
11738             case "string":
11739                 this.sortType = st.asUCString;
11740                 break;
11741             case "date":
11742                 this.sortType = st.asDate;
11743                 break;
11744             default:
11745                 this.sortType = st.none;
11746         }
11747     }
11748
11749     // define once
11750     var stripRe = /[\$,%]/g;
11751
11752     // prebuilt conversion function for this field, instead of
11753     // switching every time we're reading a value
11754     if(!this.convert){
11755         var cv, dateFormat = this.dateFormat;
11756         switch(this.type){
11757             case "":
11758             case "auto":
11759             case undefined:
11760                 cv = function(v){ return v; };
11761                 break;
11762             case "string":
11763                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11764                 break;
11765             case "int":
11766                 cv = function(v){
11767                     return v !== undefined && v !== null && v !== '' ?
11768                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11769                     };
11770                 break;
11771             case "float":
11772                 cv = function(v){
11773                     return v !== undefined && v !== null && v !== '' ?
11774                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11775                     };
11776                 break;
11777             case "bool":
11778             case "boolean":
11779                 cv = function(v){ return v === true || v === "true" || v == 1; };
11780                 break;
11781             case "date":
11782                 cv = function(v){
11783                     if(!v){
11784                         return '';
11785                     }
11786                     if(v instanceof Date){
11787                         return v;
11788                     }
11789                     if(dateFormat){
11790                         if(dateFormat == "timestamp"){
11791                             return new Date(v*1000);
11792                         }
11793                         return Date.parseDate(v, dateFormat);
11794                     }
11795                     var parsed = Date.parse(v);
11796                     return parsed ? new Date(parsed) : null;
11797                 };
11798              break;
11799             
11800         }
11801         this.convert = cv;
11802     }
11803 };
11804
11805 Roo.data.Field.prototype = {
11806     dateFormat: null,
11807     defaultValue: "",
11808     mapping: null,
11809     sortType : null,
11810     sortDir : "ASC"
11811 };/*
11812  * Based on:
11813  * Ext JS Library 1.1.1
11814  * Copyright(c) 2006-2007, Ext JS, LLC.
11815  *
11816  * Originally Released Under LGPL - original licence link has changed is not relivant.
11817  *
11818  * Fork - LGPL
11819  * <script type="text/javascript">
11820  */
11821  
11822 // Base class for reading structured data from a data source.  This class is intended to be
11823 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11824
11825 /**
11826  * @class Roo.data.DataReader
11827  * Base class for reading structured data from a data source.  This class is intended to be
11828  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11829  */
11830
11831 Roo.data.DataReader = function(meta, recordType){
11832     
11833     this.meta = meta;
11834     
11835     this.recordType = recordType instanceof Array ? 
11836         Roo.data.Record.create(recordType) : recordType;
11837 };
11838
11839 Roo.data.DataReader.prototype = {
11840      /**
11841      * Create an empty record
11842      * @param {Object} data (optional) - overlay some values
11843      * @return {Roo.data.Record} record created.
11844      */
11845     newRow :  function(d) {
11846         var da =  {};
11847         this.recordType.prototype.fields.each(function(c) {
11848             switch( c.type) {
11849                 case 'int' : da[c.name] = 0; break;
11850                 case 'date' : da[c.name] = new Date(); break;
11851                 case 'float' : da[c.name] = 0.0; break;
11852                 case 'boolean' : da[c.name] = false; break;
11853                 default : da[c.name] = ""; break;
11854             }
11855             
11856         });
11857         return new this.recordType(Roo.apply(da, d));
11858     }
11859     
11860 };/*
11861  * Based on:
11862  * Ext JS Library 1.1.1
11863  * Copyright(c) 2006-2007, Ext JS, LLC.
11864  *
11865  * Originally Released Under LGPL - original licence link has changed is not relivant.
11866  *
11867  * Fork - LGPL
11868  * <script type="text/javascript">
11869  */
11870
11871 /**
11872  * @class Roo.data.DataProxy
11873  * @extends Roo.data.Observable
11874  * This class is an abstract base class for implementations which provide retrieval of
11875  * unformatted data objects.<br>
11876  * <p>
11877  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11878  * (of the appropriate type which knows how to parse the data object) to provide a block of
11879  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11880  * <p>
11881  * Custom implementations must implement the load method as described in
11882  * {@link Roo.data.HttpProxy#load}.
11883  */
11884 Roo.data.DataProxy = function(){
11885     this.addEvents({
11886         /**
11887          * @event beforeload
11888          * Fires before a network request is made to retrieve a data object.
11889          * @param {Object} This DataProxy object.
11890          * @param {Object} params The params parameter to the load function.
11891          */
11892         beforeload : true,
11893         /**
11894          * @event load
11895          * Fires before the load method's callback is called.
11896          * @param {Object} This DataProxy object.
11897          * @param {Object} o The data object.
11898          * @param {Object} arg The callback argument object passed to the load function.
11899          */
11900         load : true,
11901         /**
11902          * @event loadexception
11903          * Fires if an Exception occurs during data retrieval.
11904          * @param {Object} This DataProxy object.
11905          * @param {Object} o The data object.
11906          * @param {Object} arg The callback argument object passed to the load function.
11907          * @param {Object} e The Exception.
11908          */
11909         loadexception : true
11910     });
11911     Roo.data.DataProxy.superclass.constructor.call(this);
11912 };
11913
11914 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11915
11916     /**
11917      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11918      */
11919 /*
11920  * Based on:
11921  * Ext JS Library 1.1.1
11922  * Copyright(c) 2006-2007, Ext JS, LLC.
11923  *
11924  * Originally Released Under LGPL - original licence link has changed is not relivant.
11925  *
11926  * Fork - LGPL
11927  * <script type="text/javascript">
11928  */
11929 /**
11930  * @class Roo.data.MemoryProxy
11931  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11932  * to the Reader when its load method is called.
11933  * @constructor
11934  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11935  */
11936 Roo.data.MemoryProxy = function(data){
11937     if (data.data) {
11938         data = data.data;
11939     }
11940     Roo.data.MemoryProxy.superclass.constructor.call(this);
11941     this.data = data;
11942 };
11943
11944 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11945     
11946     /**
11947      * Load data from the requested source (in this case an in-memory
11948      * data object passed to the constructor), read the data object into
11949      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11950      * process that block using the passed callback.
11951      * @param {Object} params This parameter is not used by the MemoryProxy class.
11952      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11953      * object into a block of Roo.data.Records.
11954      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11955      * The function must be passed <ul>
11956      * <li>The Record block object</li>
11957      * <li>The "arg" argument from the load function</li>
11958      * <li>A boolean success indicator</li>
11959      * </ul>
11960      * @param {Object} scope The scope in which to call the callback
11961      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11962      */
11963     load : function(params, reader, callback, scope, arg){
11964         params = params || {};
11965         var result;
11966         try {
11967             result = reader.readRecords(this.data);
11968         }catch(e){
11969             this.fireEvent("loadexception", this, arg, null, e);
11970             callback.call(scope, null, arg, false);
11971             return;
11972         }
11973         callback.call(scope, result, arg, true);
11974     },
11975     
11976     // private
11977     update : function(params, records){
11978         
11979     }
11980 });/*
11981  * Based on:
11982  * Ext JS Library 1.1.1
11983  * Copyright(c) 2006-2007, Ext JS, LLC.
11984  *
11985  * Originally Released Under LGPL - original licence link has changed is not relivant.
11986  *
11987  * Fork - LGPL
11988  * <script type="text/javascript">
11989  */
11990 /**
11991  * @class Roo.data.HttpProxy
11992  * @extends Roo.data.DataProxy
11993  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11994  * configured to reference a certain URL.<br><br>
11995  * <p>
11996  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11997  * from which the running page was served.<br><br>
11998  * <p>
11999  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12000  * <p>
12001  * Be aware that to enable the browser to parse an XML document, the server must set
12002  * the Content-Type header in the HTTP response to "text/xml".
12003  * @constructor
12004  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12005  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12006  * will be used to make the request.
12007  */
12008 Roo.data.HttpProxy = function(conn){
12009     Roo.data.HttpProxy.superclass.constructor.call(this);
12010     // is conn a conn config or a real conn?
12011     this.conn = conn;
12012     this.useAjax = !conn || !conn.events;
12013   
12014 };
12015
12016 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12017     // thse are take from connection...
12018     
12019     /**
12020      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12021      */
12022     /**
12023      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12024      * extra parameters to each request made by this object. (defaults to undefined)
12025      */
12026     /**
12027      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12028      *  to each request made by this object. (defaults to undefined)
12029      */
12030     /**
12031      * @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)
12032      */
12033     /**
12034      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12035      */
12036      /**
12037      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12038      * @type Boolean
12039      */
12040   
12041
12042     /**
12043      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12044      * @type Boolean
12045      */
12046     /**
12047      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12048      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12049      * a finer-grained basis than the DataProxy events.
12050      */
12051     getConnection : function(){
12052         return this.useAjax ? Roo.Ajax : this.conn;
12053     },
12054
12055     /**
12056      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12057      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12058      * process that block using the passed callback.
12059      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12060      * for the request to the remote server.
12061      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12062      * object into a block of Roo.data.Records.
12063      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12064      * The function must be passed <ul>
12065      * <li>The Record block object</li>
12066      * <li>The "arg" argument from the load function</li>
12067      * <li>A boolean success indicator</li>
12068      * </ul>
12069      * @param {Object} scope The scope in which to call the callback
12070      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12071      */
12072     load : function(params, reader, callback, scope, arg){
12073         if(this.fireEvent("beforeload", this, params) !== false){
12074             var  o = {
12075                 params : params || {},
12076                 request: {
12077                     callback : callback,
12078                     scope : scope,
12079                     arg : arg
12080                 },
12081                 reader: reader,
12082                 callback : this.loadResponse,
12083                 scope: this
12084             };
12085             if(this.useAjax){
12086                 Roo.applyIf(o, this.conn);
12087                 if(this.activeRequest){
12088                     Roo.Ajax.abort(this.activeRequest);
12089                 }
12090                 this.activeRequest = Roo.Ajax.request(o);
12091             }else{
12092                 this.conn.request(o);
12093             }
12094         }else{
12095             callback.call(scope||this, null, arg, false);
12096         }
12097     },
12098
12099     // private
12100     loadResponse : function(o, success, response){
12101         delete this.activeRequest;
12102         if(!success){
12103             this.fireEvent("loadexception", this, o, response);
12104             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12105             return;
12106         }
12107         var result;
12108         try {
12109             result = o.reader.read(response);
12110         }catch(e){
12111             this.fireEvent("loadexception", this, o, response, e);
12112             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12113             return;
12114         }
12115         
12116         this.fireEvent("load", this, o, o.request.arg);
12117         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12118     },
12119
12120     // private
12121     update : function(dataSet){
12122
12123     },
12124
12125     // private
12126     updateResponse : function(dataSet){
12127
12128     }
12129 });/*
12130  * Based on:
12131  * Ext JS Library 1.1.1
12132  * Copyright(c) 2006-2007, Ext JS, LLC.
12133  *
12134  * Originally Released Under LGPL - original licence link has changed is not relivant.
12135  *
12136  * Fork - LGPL
12137  * <script type="text/javascript">
12138  */
12139
12140 /**
12141  * @class Roo.data.ScriptTagProxy
12142  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12143  * other than the originating domain of the running page.<br><br>
12144  * <p>
12145  * <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
12146  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12147  * <p>
12148  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12149  * source code that is used as the source inside a &lt;script> tag.<br><br>
12150  * <p>
12151  * In order for the browser to process the returned data, the server must wrap the data object
12152  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12153  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12154  * depending on whether the callback name was passed:
12155  * <p>
12156  * <pre><code>
12157 boolean scriptTag = false;
12158 String cb = request.getParameter("callback");
12159 if (cb != null) {
12160     scriptTag = true;
12161     response.setContentType("text/javascript");
12162 } else {
12163     response.setContentType("application/x-json");
12164 }
12165 Writer out = response.getWriter();
12166 if (scriptTag) {
12167     out.write(cb + "(");
12168 }
12169 out.print(dataBlock.toJsonString());
12170 if (scriptTag) {
12171     out.write(");");
12172 }
12173 </pre></code>
12174  *
12175  * @constructor
12176  * @param {Object} config A configuration object.
12177  */
12178 Roo.data.ScriptTagProxy = function(config){
12179     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12180     Roo.apply(this, config);
12181     this.head = document.getElementsByTagName("head")[0];
12182 };
12183
12184 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12185
12186 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12187     /**
12188      * @cfg {String} url The URL from which to request the data object.
12189      */
12190     /**
12191      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12192      */
12193     timeout : 30000,
12194     /**
12195      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12196      * the server the name of the callback function set up by the load call to process the returned data object.
12197      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12198      * javascript output which calls this named function passing the data object as its only parameter.
12199      */
12200     callbackParam : "callback",
12201     /**
12202      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12203      * name to the request.
12204      */
12205     nocache : true,
12206
12207     /**
12208      * Load data from the configured URL, read the data object into
12209      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12210      * process that block using the passed callback.
12211      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12212      * for the request to the remote server.
12213      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12214      * object into a block of Roo.data.Records.
12215      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12216      * The function must be passed <ul>
12217      * <li>The Record block object</li>
12218      * <li>The "arg" argument from the load function</li>
12219      * <li>A boolean success indicator</li>
12220      * </ul>
12221      * @param {Object} scope The scope in which to call the callback
12222      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12223      */
12224     load : function(params, reader, callback, scope, arg){
12225         if(this.fireEvent("beforeload", this, params) !== false){
12226
12227             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12228
12229             var url = this.url;
12230             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12231             if(this.nocache){
12232                 url += "&_dc=" + (new Date().getTime());
12233             }
12234             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12235             var trans = {
12236                 id : transId,
12237                 cb : "stcCallback"+transId,
12238                 scriptId : "stcScript"+transId,
12239                 params : params,
12240                 arg : arg,
12241                 url : url,
12242                 callback : callback,
12243                 scope : scope,
12244                 reader : reader
12245             };
12246             var conn = this;
12247
12248             window[trans.cb] = function(o){
12249                 conn.handleResponse(o, trans);
12250             };
12251
12252             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12253
12254             if(this.autoAbort !== false){
12255                 this.abort();
12256             }
12257
12258             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12259
12260             var script = document.createElement("script");
12261             script.setAttribute("src", url);
12262             script.setAttribute("type", "text/javascript");
12263             script.setAttribute("id", trans.scriptId);
12264             this.head.appendChild(script);
12265
12266             this.trans = trans;
12267         }else{
12268             callback.call(scope||this, null, arg, false);
12269         }
12270     },
12271
12272     // private
12273     isLoading : function(){
12274         return this.trans ? true : false;
12275     },
12276
12277     /**
12278      * Abort the current server request.
12279      */
12280     abort : function(){
12281         if(this.isLoading()){
12282             this.destroyTrans(this.trans);
12283         }
12284     },
12285
12286     // private
12287     destroyTrans : function(trans, isLoaded){
12288         this.head.removeChild(document.getElementById(trans.scriptId));
12289         clearTimeout(trans.timeoutId);
12290         if(isLoaded){
12291             window[trans.cb] = undefined;
12292             try{
12293                 delete window[trans.cb];
12294             }catch(e){}
12295         }else{
12296             // if hasn't been loaded, wait for load to remove it to prevent script error
12297             window[trans.cb] = function(){
12298                 window[trans.cb] = undefined;
12299                 try{
12300                     delete window[trans.cb];
12301                 }catch(e){}
12302             };
12303         }
12304     },
12305
12306     // private
12307     handleResponse : function(o, trans){
12308         this.trans = false;
12309         this.destroyTrans(trans, true);
12310         var result;
12311         try {
12312             result = trans.reader.readRecords(o);
12313         }catch(e){
12314             this.fireEvent("loadexception", this, o, trans.arg, e);
12315             trans.callback.call(trans.scope||window, null, trans.arg, false);
12316             return;
12317         }
12318         this.fireEvent("load", this, o, trans.arg);
12319         trans.callback.call(trans.scope||window, result, trans.arg, true);
12320     },
12321
12322     // private
12323     handleFailure : function(trans){
12324         this.trans = false;
12325         this.destroyTrans(trans, false);
12326         this.fireEvent("loadexception", this, null, trans.arg);
12327         trans.callback.call(trans.scope||window, null, trans.arg, false);
12328     }
12329 });/*
12330  * Based on:
12331  * Ext JS Library 1.1.1
12332  * Copyright(c) 2006-2007, Ext JS, LLC.
12333  *
12334  * Originally Released Under LGPL - original licence link has changed is not relivant.
12335  *
12336  * Fork - LGPL
12337  * <script type="text/javascript">
12338  */
12339
12340 /**
12341  * @class Roo.data.JsonReader
12342  * @extends Roo.data.DataReader
12343  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12344  * based on mappings in a provided Roo.data.Record constructor.
12345  * 
12346  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12347  * in the reply previously. 
12348  * 
12349  * <p>
12350  * Example code:
12351  * <pre><code>
12352 var RecordDef = Roo.data.Record.create([
12353     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12354     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12355 ]);
12356 var myReader = new Roo.data.JsonReader({
12357     totalProperty: "results",    // The property which contains the total dataset size (optional)
12358     root: "rows",                // The property which contains an Array of row objects
12359     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12360 }, RecordDef);
12361 </code></pre>
12362  * <p>
12363  * This would consume a JSON file like this:
12364  * <pre><code>
12365 { 'results': 2, 'rows': [
12366     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12367     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12368 }
12369 </code></pre>
12370  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12371  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12372  * paged from the remote server.
12373  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12374  * @cfg {String} root name of the property which contains the Array of row objects.
12375  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12376  * @cfg {Array} fields Array of field definition objects
12377  * @constructor
12378  * Create a new JsonReader
12379  * @param {Object} meta Metadata configuration options
12380  * @param {Object} recordType Either an Array of field definition objects,
12381  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12382  */
12383 Roo.data.JsonReader = function(meta, recordType){
12384     
12385     meta = meta || {};
12386     // set some defaults:
12387     Roo.applyIf(meta, {
12388         totalProperty: 'total',
12389         successProperty : 'success',
12390         root : 'data',
12391         id : 'id'
12392     });
12393     
12394     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12395 };
12396 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12397     
12398     /**
12399      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12400      * Used by Store query builder to append _requestMeta to params.
12401      * 
12402      */
12403     metaFromRemote : false,
12404     /**
12405      * This method is only used by a DataProxy which has retrieved data from a remote server.
12406      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12407      * @return {Object} data A data block which is used by an Roo.data.Store object as
12408      * a cache of Roo.data.Records.
12409      */
12410     read : function(response){
12411         var json = response.responseText;
12412        
12413         var o = /* eval:var:o */ eval("("+json+")");
12414         if(!o) {
12415             throw {message: "JsonReader.read: Json object not found"};
12416         }
12417         
12418         if(o.metaData){
12419             
12420             delete this.ef;
12421             this.metaFromRemote = true;
12422             this.meta = o.metaData;
12423             this.recordType = Roo.data.Record.create(o.metaData.fields);
12424             this.onMetaChange(this.meta, this.recordType, o);
12425         }
12426         return this.readRecords(o);
12427     },
12428
12429     // private function a store will implement
12430     onMetaChange : function(meta, recordType, o){
12431
12432     },
12433
12434     /**
12435          * @ignore
12436          */
12437     simpleAccess: function(obj, subsc) {
12438         return obj[subsc];
12439     },
12440
12441         /**
12442          * @ignore
12443          */
12444     getJsonAccessor: function(){
12445         var re = /[\[\.]/;
12446         return function(expr) {
12447             try {
12448                 return(re.test(expr))
12449                     ? new Function("obj", "return obj." + expr)
12450                     : function(obj){
12451                         return obj[expr];
12452                     };
12453             } catch(e){}
12454             return Roo.emptyFn;
12455         };
12456     }(),
12457
12458     /**
12459      * Create a data block containing Roo.data.Records from an XML document.
12460      * @param {Object} o An object which contains an Array of row objects in the property specified
12461      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12462      * which contains the total size of the dataset.
12463      * @return {Object} data A data block which is used by an Roo.data.Store object as
12464      * a cache of Roo.data.Records.
12465      */
12466     readRecords : function(o){
12467         /**
12468          * After any data loads, the raw JSON data is available for further custom processing.
12469          * @type Object
12470          */
12471         this.o = o;
12472         var s = this.meta, Record = this.recordType,
12473             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12474
12475 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12476         if (!this.ef) {
12477             if(s.totalProperty) {
12478                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12479                 }
12480                 if(s.successProperty) {
12481                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12482                 }
12483                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12484                 if (s.id) {
12485                         var g = this.getJsonAccessor(s.id);
12486                         this.getId = function(rec) {
12487                                 var r = g(rec);  
12488                                 return (r === undefined || r === "") ? null : r;
12489                         };
12490                 } else {
12491                         this.getId = function(){return null;};
12492                 }
12493             this.ef = [];
12494             for(var jj = 0; jj < fl; jj++){
12495                 f = fi[jj];
12496                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12497                 this.ef[jj] = this.getJsonAccessor(map);
12498             }
12499         }
12500
12501         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12502         if(s.totalProperty){
12503             var vt = parseInt(this.getTotal(o), 10);
12504             if(!isNaN(vt)){
12505                 totalRecords = vt;
12506             }
12507         }
12508         if(s.successProperty){
12509             var vs = this.getSuccess(o);
12510             if(vs === false || vs === 'false'){
12511                 success = false;
12512             }
12513         }
12514         var records = [];
12515         for(var i = 0; i < c; i++){
12516                 var n = root[i];
12517             var values = {};
12518             var id = this.getId(n);
12519             for(var j = 0; j < fl; j++){
12520                 f = fi[j];
12521             var v = this.ef[j](n);
12522             if (!f.convert) {
12523                 Roo.log('missing convert for ' + f.name);
12524                 Roo.log(f);
12525                 continue;
12526             }
12527             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12528             }
12529             var record = new Record(values, id);
12530             record.json = n;
12531             records[i] = record;
12532         }
12533         return {
12534             raw : o,
12535             success : success,
12536             records : records,
12537             totalRecords : totalRecords
12538         };
12539     }
12540 });/*
12541  * Based on:
12542  * Ext JS Library 1.1.1
12543  * Copyright(c) 2006-2007, Ext JS, LLC.
12544  *
12545  * Originally Released Under LGPL - original licence link has changed is not relivant.
12546  *
12547  * Fork - LGPL
12548  * <script type="text/javascript">
12549  */
12550
12551 /**
12552  * @class Roo.data.ArrayReader
12553  * @extends Roo.data.DataReader
12554  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12555  * Each element of that Array represents a row of data fields. The
12556  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12557  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12558  * <p>
12559  * Example code:.
12560  * <pre><code>
12561 var RecordDef = Roo.data.Record.create([
12562     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12563     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12564 ]);
12565 var myReader = new Roo.data.ArrayReader({
12566     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12567 }, RecordDef);
12568 </code></pre>
12569  * <p>
12570  * This would consume an Array like this:
12571  * <pre><code>
12572 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12573   </code></pre>
12574  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12575  * @constructor
12576  * Create a new JsonReader
12577  * @param {Object} meta Metadata configuration options.
12578  * @param {Object} recordType Either an Array of field definition objects
12579  * as specified to {@link Roo.data.Record#create},
12580  * or an {@link Roo.data.Record} object
12581  * created using {@link Roo.data.Record#create}.
12582  */
12583 Roo.data.ArrayReader = function(meta, recordType){
12584     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12585 };
12586
12587 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12588     /**
12589      * Create a data block containing Roo.data.Records from an XML document.
12590      * @param {Object} o An Array of row objects which represents the dataset.
12591      * @return {Object} data A data block which is used by an Roo.data.Store object as
12592      * a cache of Roo.data.Records.
12593      */
12594     readRecords : function(o){
12595         var sid = this.meta ? this.meta.id : null;
12596         var recordType = this.recordType, fields = recordType.prototype.fields;
12597         var records = [];
12598         var root = o;
12599             for(var i = 0; i < root.length; i++){
12600                     var n = root[i];
12601                 var values = {};
12602                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12603                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12604                 var f = fields.items[j];
12605                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12606                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12607                 v = f.convert(v);
12608                 values[f.name] = v;
12609             }
12610                 var record = new recordType(values, id);
12611                 record.json = n;
12612                 records[records.length] = record;
12613             }
12614             return {
12615                 records : records,
12616                 totalRecords : records.length
12617             };
12618     }
12619 });/*
12620  * - LGPL
12621  * * 
12622  */
12623
12624 /**
12625  * @class Roo.bootstrap.ComboBox
12626  * @extends Roo.bootstrap.TriggerField
12627  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12628  * @cfg {Boolean} append (true|false) default false
12629  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12630  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12631  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12632  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12633  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12634  * @cfg {Boolean} animate default true
12635  * @cfg {Boolean} emptyResultText only for touch device
12636  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12637  * @cfg {String} emptyTitle default ''
12638  * @constructor
12639  * Create a new ComboBox.
12640  * @param {Object} config Configuration options
12641  */
12642 Roo.bootstrap.ComboBox = function(config){
12643     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12644     this.addEvents({
12645         /**
12646          * @event expand
12647          * Fires when the dropdown list is expanded
12648         * @param {Roo.bootstrap.ComboBox} combo This combo box
12649         */
12650         'expand' : true,
12651         /**
12652          * @event collapse
12653          * Fires when the dropdown list is collapsed
12654         * @param {Roo.bootstrap.ComboBox} combo This combo box
12655         */
12656         'collapse' : true,
12657         /**
12658          * @event beforeselect
12659          * Fires before a list item is selected. Return false to cancel the selection.
12660         * @param {Roo.bootstrap.ComboBox} combo This combo box
12661         * @param {Roo.data.Record} record The data record returned from the underlying store
12662         * @param {Number} index The index of the selected item in the dropdown list
12663         */
12664         'beforeselect' : true,
12665         /**
12666          * @event select
12667          * Fires when a list item is selected
12668         * @param {Roo.bootstrap.ComboBox} combo This combo box
12669         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12670         * @param {Number} index The index of the selected item in the dropdown list
12671         */
12672         'select' : true,
12673         /**
12674          * @event beforequery
12675          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12676          * The event object passed has these properties:
12677         * @param {Roo.bootstrap.ComboBox} combo This combo box
12678         * @param {String} query The query
12679         * @param {Boolean} forceAll true to force "all" query
12680         * @param {Boolean} cancel true to cancel the query
12681         * @param {Object} e The query event object
12682         */
12683         'beforequery': true,
12684          /**
12685          * @event add
12686          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12687         * @param {Roo.bootstrap.ComboBox} combo This combo box
12688         */
12689         'add' : true,
12690         /**
12691          * @event edit
12692          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12693         * @param {Roo.bootstrap.ComboBox} combo This combo box
12694         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12695         */
12696         'edit' : true,
12697         /**
12698          * @event remove
12699          * Fires when the remove value from the combobox array
12700         * @param {Roo.bootstrap.ComboBox} combo This combo box
12701         */
12702         'remove' : true,
12703         /**
12704          * @event afterremove
12705          * Fires when the remove value from the combobox array
12706         * @param {Roo.bootstrap.ComboBox} combo This combo box
12707         */
12708         'afterremove' : true,
12709         /**
12710          * @event specialfilter
12711          * Fires when specialfilter
12712             * @param {Roo.bootstrap.ComboBox} combo This combo box
12713             */
12714         'specialfilter' : true,
12715         /**
12716          * @event tick
12717          * Fires when tick the element
12718             * @param {Roo.bootstrap.ComboBox} combo This combo box
12719             */
12720         'tick' : true,
12721         /**
12722          * @event touchviewdisplay
12723          * Fires when touch view require special display (default is using displayField)
12724             * @param {Roo.bootstrap.ComboBox} combo This combo box
12725             * @param {Object} cfg set html .
12726             */
12727         'touchviewdisplay' : true
12728         
12729     });
12730     
12731     this.item = [];
12732     this.tickItems = [];
12733     
12734     this.selectedIndex = -1;
12735     if(this.mode == 'local'){
12736         if(config.queryDelay === undefined){
12737             this.queryDelay = 10;
12738         }
12739         if(config.minChars === undefined){
12740             this.minChars = 0;
12741         }
12742     }
12743 };
12744
12745 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12746      
12747     /**
12748      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12749      * rendering into an Roo.Editor, defaults to false)
12750      */
12751     /**
12752      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12753      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12754      */
12755     /**
12756      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12757      */
12758     /**
12759      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12760      * the dropdown list (defaults to undefined, with no header element)
12761      */
12762
12763      /**
12764      * @cfg {String/Roo.Template} tpl The template to use to render the output
12765      */
12766      
12767      /**
12768      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12769      */
12770     listWidth: undefined,
12771     /**
12772      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12773      * mode = 'remote' or 'text' if mode = 'local')
12774      */
12775     displayField: undefined,
12776     
12777     /**
12778      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12779      * mode = 'remote' or 'value' if mode = 'local'). 
12780      * Note: use of a valueField requires the user make a selection
12781      * in order for a value to be mapped.
12782      */
12783     valueField: undefined,
12784     /**
12785      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12786      */
12787     modalTitle : '',
12788     
12789     /**
12790      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12791      * field's data value (defaults to the underlying DOM element's name)
12792      */
12793     hiddenName: undefined,
12794     /**
12795      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12796      */
12797     listClass: '',
12798     /**
12799      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12800      */
12801     selectedClass: 'active',
12802     
12803     /**
12804      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12805      */
12806     shadow:'sides',
12807     /**
12808      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12809      * anchor positions (defaults to 'tl-bl')
12810      */
12811     listAlign: 'tl-bl?',
12812     /**
12813      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12814      */
12815     maxHeight: 300,
12816     /**
12817      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12818      * query specified by the allQuery config option (defaults to 'query')
12819      */
12820     triggerAction: 'query',
12821     /**
12822      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12823      * (defaults to 4, does not apply if editable = false)
12824      */
12825     minChars : 4,
12826     /**
12827      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12828      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12829      */
12830     typeAhead: false,
12831     /**
12832      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12833      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12834      */
12835     queryDelay: 500,
12836     /**
12837      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12838      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12839      */
12840     pageSize: 0,
12841     /**
12842      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12843      * when editable = true (defaults to false)
12844      */
12845     selectOnFocus:false,
12846     /**
12847      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12848      */
12849     queryParam: 'query',
12850     /**
12851      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12852      * when mode = 'remote' (defaults to 'Loading...')
12853      */
12854     loadingText: 'Loading...',
12855     /**
12856      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12857      */
12858     resizable: false,
12859     /**
12860      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12861      */
12862     handleHeight : 8,
12863     /**
12864      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12865      * traditional select (defaults to true)
12866      */
12867     editable: true,
12868     /**
12869      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12870      */
12871     allQuery: '',
12872     /**
12873      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12874      */
12875     mode: 'remote',
12876     /**
12877      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12878      * listWidth has a higher value)
12879      */
12880     minListWidth : 70,
12881     /**
12882      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12883      * allow the user to set arbitrary text into the field (defaults to false)
12884      */
12885     forceSelection:false,
12886     /**
12887      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12888      * if typeAhead = true (defaults to 250)
12889      */
12890     typeAheadDelay : 250,
12891     /**
12892      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12893      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12894      */
12895     valueNotFoundText : undefined,
12896     /**
12897      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12898      */
12899     blockFocus : false,
12900     
12901     /**
12902      * @cfg {Boolean} disableClear Disable showing of clear button.
12903      */
12904     disableClear : false,
12905     /**
12906      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12907      */
12908     alwaysQuery : false,
12909     
12910     /**
12911      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12912      */
12913     multiple : false,
12914     
12915     /**
12916      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12917      */
12918     invalidClass : "has-warning",
12919     
12920     /**
12921      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12922      */
12923     validClass : "has-success",
12924     
12925     /**
12926      * @cfg {Boolean} specialFilter (true|false) special filter default false
12927      */
12928     specialFilter : false,
12929     
12930     /**
12931      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12932      */
12933     mobileTouchView : true,
12934     
12935     /**
12936      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12937      */
12938     useNativeIOS : false,
12939     
12940     ios_options : false,
12941     
12942     //private
12943     addicon : false,
12944     editicon: false,
12945     
12946     page: 0,
12947     hasQuery: false,
12948     append: false,
12949     loadNext: false,
12950     autoFocus : true,
12951     tickable : false,
12952     btnPosition : 'right',
12953     triggerList : true,
12954     showToggleBtn : true,
12955     animate : true,
12956     emptyResultText: 'Empty',
12957     triggerText : 'Select',
12958     emptyTitle : '',
12959     
12960     // element that contains real text value.. (when hidden is used..)
12961     
12962     getAutoCreate : function()
12963     {   
12964         var cfg = false;
12965         //render
12966         /*
12967          * Render classic select for iso
12968          */
12969         
12970         if(Roo.isIOS && this.useNativeIOS){
12971             cfg = this.getAutoCreateNativeIOS();
12972             return cfg;
12973         }
12974         
12975         /*
12976          * Touch Devices
12977          */
12978         
12979         if(Roo.isTouch && this.mobileTouchView){
12980             cfg = this.getAutoCreateTouchView();
12981             return cfg;;
12982         }
12983         
12984         /*
12985          *  Normal ComboBox
12986          */
12987         if(!this.tickable){
12988             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12989             return cfg;
12990         }
12991         
12992         /*
12993          *  ComboBox with tickable selections
12994          */
12995              
12996         var align = this.labelAlign || this.parentLabelAlign();
12997         
12998         cfg = {
12999             cls : 'form-group roo-combobox-tickable' //input-group
13000         };
13001         
13002         var btn_text_select = '';
13003         var btn_text_done = '';
13004         var btn_text_cancel = '';
13005         
13006         if (this.btn_text_show) {
13007             btn_text_select = 'Select';
13008             btn_text_done = 'Done';
13009             btn_text_cancel = 'Cancel'; 
13010         }
13011         
13012         var buttons = {
13013             tag : 'div',
13014             cls : 'tickable-buttons',
13015             cn : [
13016                 {
13017                     tag : 'button',
13018                     type : 'button',
13019                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13020                     //html : this.triggerText
13021                     html: btn_text_select
13022                 },
13023                 {
13024                     tag : 'button',
13025                     type : 'button',
13026                     name : 'ok',
13027                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13028                     //html : 'Done'
13029                     html: btn_text_done
13030                 },
13031                 {
13032                     tag : 'button',
13033                     type : 'button',
13034                     name : 'cancel',
13035                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13036                     //html : 'Cancel'
13037                     html: btn_text_cancel
13038                 }
13039             ]
13040         };
13041         
13042         if(this.editable){
13043             buttons.cn.unshift({
13044                 tag: 'input',
13045                 cls: 'roo-select2-search-field-input'
13046             });
13047         }
13048         
13049         var _this = this;
13050         
13051         Roo.each(buttons.cn, function(c){
13052             if (_this.size) {
13053                 c.cls += ' btn-' + _this.size;
13054             }
13055
13056             if (_this.disabled) {
13057                 c.disabled = true;
13058             }
13059         });
13060         
13061         var box = {
13062             tag: 'div',
13063             cn: [
13064                 {
13065                     tag: 'input',
13066                     type : 'hidden',
13067                     cls: 'form-hidden-field'
13068                 },
13069                 {
13070                     tag: 'ul',
13071                     cls: 'roo-select2-choices',
13072                     cn:[
13073                         {
13074                             tag: 'li',
13075                             cls: 'roo-select2-search-field',
13076                             cn: [
13077                                 buttons
13078                             ]
13079                         }
13080                     ]
13081                 }
13082             ]
13083         };
13084         
13085         var combobox = {
13086             cls: 'roo-select2-container input-group roo-select2-container-multi',
13087             cn: [
13088                 box
13089 //                {
13090 //                    tag: 'ul',
13091 //                    cls: 'typeahead typeahead-long dropdown-menu',
13092 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13093 //                }
13094             ]
13095         };
13096         
13097         if(this.hasFeedback && !this.allowBlank){
13098             
13099             var feedback = {
13100                 tag: 'span',
13101                 cls: 'glyphicon form-control-feedback'
13102             };
13103
13104             combobox.cn.push(feedback);
13105         }
13106         
13107         
13108         if (align ==='left' && this.fieldLabel.length) {
13109             
13110             cfg.cls += ' roo-form-group-label-left';
13111             
13112             cfg.cn = [
13113                 {
13114                     tag : 'i',
13115                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13116                     tooltip : 'This field is required'
13117                 },
13118                 {
13119                     tag: 'label',
13120                     'for' :  id,
13121                     cls : 'control-label',
13122                     html : this.fieldLabel
13123
13124                 },
13125                 {
13126                     cls : "", 
13127                     cn: [
13128                         combobox
13129                     ]
13130                 }
13131
13132             ];
13133             
13134             var labelCfg = cfg.cn[1];
13135             var contentCfg = cfg.cn[2];
13136             
13137
13138             if(this.indicatorpos == 'right'){
13139                 
13140                 cfg.cn = [
13141                     {
13142                         tag: 'label',
13143                         'for' :  id,
13144                         cls : 'control-label',
13145                         cn : [
13146                             {
13147                                 tag : 'span',
13148                                 html : this.fieldLabel
13149                             },
13150                             {
13151                                 tag : 'i',
13152                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13153                                 tooltip : 'This field is required'
13154                             }
13155                         ]
13156                     },
13157                     {
13158                         cls : "",
13159                         cn: [
13160                             combobox
13161                         ]
13162                     }
13163
13164                 ];
13165                 
13166                 
13167                 
13168                 labelCfg = cfg.cn[0];
13169                 contentCfg = cfg.cn[1];
13170             
13171             }
13172             
13173             if(this.labelWidth > 12){
13174                 labelCfg.style = "width: " + this.labelWidth + 'px';
13175             }
13176             
13177             if(this.labelWidth < 13 && this.labelmd == 0){
13178                 this.labelmd = this.labelWidth;
13179             }
13180             
13181             if(this.labellg > 0){
13182                 labelCfg.cls += ' col-lg-' + this.labellg;
13183                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13184             }
13185             
13186             if(this.labelmd > 0){
13187                 labelCfg.cls += ' col-md-' + this.labelmd;
13188                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13189             }
13190             
13191             if(this.labelsm > 0){
13192                 labelCfg.cls += ' col-sm-' + this.labelsm;
13193                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13194             }
13195             
13196             if(this.labelxs > 0){
13197                 labelCfg.cls += ' col-xs-' + this.labelxs;
13198                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13199             }
13200                 
13201                 
13202         } else if ( this.fieldLabel.length) {
13203 //                Roo.log(" label");
13204                  cfg.cn = [
13205                     {
13206                         tag : 'i',
13207                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13208                         tooltip : 'This field is required'
13209                     },
13210                     {
13211                         tag: 'label',
13212                         //cls : 'input-group-addon',
13213                         html : this.fieldLabel
13214                     },
13215                     combobox
13216                 ];
13217                 
13218                 if(this.indicatorpos == 'right'){
13219                     cfg.cn = [
13220                         {
13221                             tag: 'label',
13222                             //cls : 'input-group-addon',
13223                             html : this.fieldLabel
13224                         },
13225                         {
13226                             tag : 'i',
13227                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13228                             tooltip : 'This field is required'
13229                         },
13230                         combobox
13231                     ];
13232                     
13233                 }
13234
13235         } else {
13236             
13237 //                Roo.log(" no label && no align");
13238                 cfg = combobox
13239                      
13240                 
13241         }
13242          
13243         var settings=this;
13244         ['xs','sm','md','lg'].map(function(size){
13245             if (settings[size]) {
13246                 cfg.cls += ' col-' + size + '-' + settings[size];
13247             }
13248         });
13249         
13250         return cfg;
13251         
13252     },
13253     
13254     _initEventsCalled : false,
13255     
13256     // private
13257     initEvents: function()
13258     {   
13259         if (this._initEventsCalled) { // as we call render... prevent looping...
13260             return;
13261         }
13262         this._initEventsCalled = true;
13263         
13264         if (!this.store) {
13265             throw "can not find store for combo";
13266         }
13267         
13268         this.indicator = this.indicatorEl();
13269         
13270         this.store = Roo.factory(this.store, Roo.data);
13271         this.store.parent = this;
13272         
13273         // if we are building from html. then this element is so complex, that we can not really
13274         // use the rendered HTML.
13275         // so we have to trash and replace the previous code.
13276         if (Roo.XComponent.build_from_html) {
13277             // remove this element....
13278             var e = this.el.dom, k=0;
13279             while (e ) { e = e.previousSibling;  ++k;}
13280
13281             this.el.remove();
13282             
13283             this.el=false;
13284             this.rendered = false;
13285             
13286             this.render(this.parent().getChildContainer(true), k);
13287         }
13288         
13289         if(Roo.isIOS && this.useNativeIOS){
13290             this.initIOSView();
13291             return;
13292         }
13293         
13294         /*
13295          * Touch Devices
13296          */
13297         
13298         if(Roo.isTouch && this.mobileTouchView){
13299             this.initTouchView();
13300             return;
13301         }
13302         
13303         if(this.tickable){
13304             this.initTickableEvents();
13305             return;
13306         }
13307         
13308         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13309         
13310         if(this.hiddenName){
13311             
13312             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13313             
13314             this.hiddenField.dom.value =
13315                 this.hiddenValue !== undefined ? this.hiddenValue :
13316                 this.value !== undefined ? this.value : '';
13317
13318             // prevent input submission
13319             this.el.dom.removeAttribute('name');
13320             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13321              
13322              
13323         }
13324         //if(Roo.isGecko){
13325         //    this.el.dom.setAttribute('autocomplete', 'off');
13326         //}
13327         
13328         var cls = 'x-combo-list';
13329         
13330         //this.list = new Roo.Layer({
13331         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13332         //});
13333         
13334         var _this = this;
13335         
13336         (function(){
13337             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13338             _this.list.setWidth(lw);
13339         }).defer(100);
13340         
13341         this.list.on('mouseover', this.onViewOver, this);
13342         this.list.on('mousemove', this.onViewMove, this);
13343         this.list.on('scroll', this.onViewScroll, this);
13344         
13345         /*
13346         this.list.swallowEvent('mousewheel');
13347         this.assetHeight = 0;
13348
13349         if(this.title){
13350             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13351             this.assetHeight += this.header.getHeight();
13352         }
13353
13354         this.innerList = this.list.createChild({cls:cls+'-inner'});
13355         this.innerList.on('mouseover', this.onViewOver, this);
13356         this.innerList.on('mousemove', this.onViewMove, this);
13357         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13358         
13359         if(this.allowBlank && !this.pageSize && !this.disableClear){
13360             this.footer = this.list.createChild({cls:cls+'-ft'});
13361             this.pageTb = new Roo.Toolbar(this.footer);
13362            
13363         }
13364         if(this.pageSize){
13365             this.footer = this.list.createChild({cls:cls+'-ft'});
13366             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13367                     {pageSize: this.pageSize});
13368             
13369         }
13370         
13371         if (this.pageTb && this.allowBlank && !this.disableClear) {
13372             var _this = this;
13373             this.pageTb.add(new Roo.Toolbar.Fill(), {
13374                 cls: 'x-btn-icon x-btn-clear',
13375                 text: '&#160;',
13376                 handler: function()
13377                 {
13378                     _this.collapse();
13379                     _this.clearValue();
13380                     _this.onSelect(false, -1);
13381                 }
13382             });
13383         }
13384         if (this.footer) {
13385             this.assetHeight += this.footer.getHeight();
13386         }
13387         */
13388             
13389         if(!this.tpl){
13390             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13391         }
13392
13393         this.view = new Roo.View(this.list, this.tpl, {
13394             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13395         });
13396         //this.view.wrapEl.setDisplayed(false);
13397         this.view.on('click', this.onViewClick, this);
13398         
13399         
13400         this.store.on('beforeload', this.onBeforeLoad, this);
13401         this.store.on('load', this.onLoad, this);
13402         this.store.on('loadexception', this.onLoadException, this);
13403         /*
13404         if(this.resizable){
13405             this.resizer = new Roo.Resizable(this.list,  {
13406                pinned:true, handles:'se'
13407             });
13408             this.resizer.on('resize', function(r, w, h){
13409                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13410                 this.listWidth = w;
13411                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13412                 this.restrictHeight();
13413             }, this);
13414             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13415         }
13416         */
13417         if(!this.editable){
13418             this.editable = true;
13419             this.setEditable(false);
13420         }
13421         
13422         /*
13423         
13424         if (typeof(this.events.add.listeners) != 'undefined') {
13425             
13426             this.addicon = this.wrap.createChild(
13427                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13428        
13429             this.addicon.on('click', function(e) {
13430                 this.fireEvent('add', this);
13431             }, this);
13432         }
13433         if (typeof(this.events.edit.listeners) != 'undefined') {
13434             
13435             this.editicon = this.wrap.createChild(
13436                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13437             if (this.addicon) {
13438                 this.editicon.setStyle('margin-left', '40px');
13439             }
13440             this.editicon.on('click', function(e) {
13441                 
13442                 // we fire even  if inothing is selected..
13443                 this.fireEvent('edit', this, this.lastData );
13444                 
13445             }, this);
13446         }
13447         */
13448         
13449         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13450             "up" : function(e){
13451                 this.inKeyMode = true;
13452                 this.selectPrev();
13453             },
13454
13455             "down" : function(e){
13456                 if(!this.isExpanded()){
13457                     this.onTriggerClick();
13458                 }else{
13459                     this.inKeyMode = true;
13460                     this.selectNext();
13461                 }
13462             },
13463
13464             "enter" : function(e){
13465 //                this.onViewClick();
13466                 //return true;
13467                 this.collapse();
13468                 
13469                 if(this.fireEvent("specialkey", this, e)){
13470                     this.onViewClick(false);
13471                 }
13472                 
13473                 return true;
13474             },
13475
13476             "esc" : function(e){
13477                 this.collapse();
13478             },
13479
13480             "tab" : function(e){
13481                 this.collapse();
13482                 
13483                 if(this.fireEvent("specialkey", this, e)){
13484                     this.onViewClick(false);
13485                 }
13486                 
13487                 return true;
13488             },
13489
13490             scope : this,
13491
13492             doRelay : function(foo, bar, hname){
13493                 if(hname == 'down' || this.scope.isExpanded()){
13494                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13495                 }
13496                 return true;
13497             },
13498
13499             forceKeyDown: true
13500         });
13501         
13502         
13503         this.queryDelay = Math.max(this.queryDelay || 10,
13504                 this.mode == 'local' ? 10 : 250);
13505         
13506         
13507         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13508         
13509         if(this.typeAhead){
13510             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13511         }
13512         if(this.editable !== false){
13513             this.inputEl().on("keyup", this.onKeyUp, this);
13514         }
13515         if(this.forceSelection){
13516             this.inputEl().on('blur', this.doForce, this);
13517         }
13518         
13519         if(this.multiple){
13520             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13521             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13522         }
13523     },
13524     
13525     initTickableEvents: function()
13526     {   
13527         this.createList();
13528         
13529         if(this.hiddenName){
13530             
13531             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13532             
13533             this.hiddenField.dom.value =
13534                 this.hiddenValue !== undefined ? this.hiddenValue :
13535                 this.value !== undefined ? this.value : '';
13536
13537             // prevent input submission
13538             this.el.dom.removeAttribute('name');
13539             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13540              
13541              
13542         }
13543         
13544 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13545         
13546         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13547         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13548         if(this.triggerList){
13549             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13550         }
13551          
13552         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13553         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13554         
13555         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13556         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13557         
13558         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13559         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13560         
13561         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13562         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13563         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13564         
13565         this.okBtn.hide();
13566         this.cancelBtn.hide();
13567         
13568         var _this = this;
13569         
13570         (function(){
13571             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13572             _this.list.setWidth(lw);
13573         }).defer(100);
13574         
13575         this.list.on('mouseover', this.onViewOver, this);
13576         this.list.on('mousemove', this.onViewMove, this);
13577         
13578         this.list.on('scroll', this.onViewScroll, this);
13579         
13580         if(!this.tpl){
13581             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>';
13582         }
13583
13584         this.view = new Roo.View(this.list, this.tpl, {
13585             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13586         });
13587         
13588         //this.view.wrapEl.setDisplayed(false);
13589         this.view.on('click', this.onViewClick, this);
13590         
13591         
13592         
13593         this.store.on('beforeload', this.onBeforeLoad, this);
13594         this.store.on('load', this.onLoad, this);
13595         this.store.on('loadexception', this.onLoadException, this);
13596         
13597         if(this.editable){
13598             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13599                 "up" : function(e){
13600                     this.inKeyMode = true;
13601                     this.selectPrev();
13602                 },
13603
13604                 "down" : function(e){
13605                     this.inKeyMode = true;
13606                     this.selectNext();
13607                 },
13608
13609                 "enter" : function(e){
13610                     if(this.fireEvent("specialkey", this, e)){
13611                         this.onViewClick(false);
13612                     }
13613                     
13614                     return true;
13615                 },
13616
13617                 "esc" : function(e){
13618                     this.onTickableFooterButtonClick(e, false, false);
13619                 },
13620
13621                 "tab" : function(e){
13622                     this.fireEvent("specialkey", this, e);
13623                     
13624                     this.onTickableFooterButtonClick(e, false, false);
13625                     
13626                     return true;
13627                 },
13628
13629                 scope : this,
13630
13631                 doRelay : function(e, fn, key){
13632                     if(this.scope.isExpanded()){
13633                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13634                     }
13635                     return true;
13636                 },
13637
13638                 forceKeyDown: true
13639             });
13640         }
13641         
13642         this.queryDelay = Math.max(this.queryDelay || 10,
13643                 this.mode == 'local' ? 10 : 250);
13644         
13645         
13646         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13647         
13648         if(this.typeAhead){
13649             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13650         }
13651         
13652         if(this.editable !== false){
13653             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13654         }
13655         
13656         this.indicator = this.indicatorEl();
13657         
13658         if(this.indicator){
13659             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13660             this.indicator.hide();
13661         }
13662         
13663     },
13664
13665     onDestroy : function(){
13666         if(this.view){
13667             this.view.setStore(null);
13668             this.view.el.removeAllListeners();
13669             this.view.el.remove();
13670             this.view.purgeListeners();
13671         }
13672         if(this.list){
13673             this.list.dom.innerHTML  = '';
13674         }
13675         
13676         if(this.store){
13677             this.store.un('beforeload', this.onBeforeLoad, this);
13678             this.store.un('load', this.onLoad, this);
13679             this.store.un('loadexception', this.onLoadException, this);
13680         }
13681         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13682     },
13683
13684     // private
13685     fireKey : function(e){
13686         if(e.isNavKeyPress() && !this.list.isVisible()){
13687             this.fireEvent("specialkey", this, e);
13688         }
13689     },
13690
13691     // private
13692     onResize: function(w, h){
13693 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13694 //        
13695 //        if(typeof w != 'number'){
13696 //            // we do not handle it!?!?
13697 //            return;
13698 //        }
13699 //        var tw = this.trigger.getWidth();
13700 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13701 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13702 //        var x = w - tw;
13703 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13704 //            
13705 //        //this.trigger.setStyle('left', x+'px');
13706 //        
13707 //        if(this.list && this.listWidth === undefined){
13708 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13709 //            this.list.setWidth(lw);
13710 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13711 //        }
13712         
13713     
13714         
13715     },
13716
13717     /**
13718      * Allow or prevent the user from directly editing the field text.  If false is passed,
13719      * the user will only be able to select from the items defined in the dropdown list.  This method
13720      * is the runtime equivalent of setting the 'editable' config option at config time.
13721      * @param {Boolean} value True to allow the user to directly edit the field text
13722      */
13723     setEditable : function(value){
13724         if(value == this.editable){
13725             return;
13726         }
13727         this.editable = value;
13728         if(!value){
13729             this.inputEl().dom.setAttribute('readOnly', true);
13730             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13731             this.inputEl().addClass('x-combo-noedit');
13732         }else{
13733             this.inputEl().dom.setAttribute('readOnly', false);
13734             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13735             this.inputEl().removeClass('x-combo-noedit');
13736         }
13737     },
13738
13739     // private
13740     
13741     onBeforeLoad : function(combo,opts){
13742         if(!this.hasFocus){
13743             return;
13744         }
13745          if (!opts.add) {
13746             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13747          }
13748         this.restrictHeight();
13749         this.selectedIndex = -1;
13750     },
13751
13752     // private
13753     onLoad : function(){
13754         
13755         this.hasQuery = false;
13756         
13757         if(!this.hasFocus){
13758             return;
13759         }
13760         
13761         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13762             this.loading.hide();
13763         }
13764         
13765         if(this.store.getCount() > 0){
13766             
13767             this.expand();
13768             this.restrictHeight();
13769             if(this.lastQuery == this.allQuery){
13770                 if(this.editable && !this.tickable){
13771                     this.inputEl().dom.select();
13772                 }
13773                 
13774                 if(
13775                     !this.selectByValue(this.value, true) &&
13776                     this.autoFocus && 
13777                     (
13778                         !this.store.lastOptions ||
13779                         typeof(this.store.lastOptions.add) == 'undefined' || 
13780                         this.store.lastOptions.add != true
13781                     )
13782                 ){
13783                     this.select(0, true);
13784                 }
13785             }else{
13786                 if(this.autoFocus){
13787                     this.selectNext();
13788                 }
13789                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13790                     this.taTask.delay(this.typeAheadDelay);
13791                 }
13792             }
13793         }else{
13794             this.onEmptyResults();
13795         }
13796         
13797         //this.el.focus();
13798     },
13799     // private
13800     onLoadException : function()
13801     {
13802         this.hasQuery = false;
13803         
13804         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13805             this.loading.hide();
13806         }
13807         
13808         if(this.tickable && this.editable){
13809             return;
13810         }
13811         
13812         this.collapse();
13813         // only causes errors at present
13814         //Roo.log(this.store.reader.jsonData);
13815         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13816             // fixme
13817             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13818         //}
13819         
13820         
13821     },
13822     // private
13823     onTypeAhead : function(){
13824         if(this.store.getCount() > 0){
13825             var r = this.store.getAt(0);
13826             var newValue = r.data[this.displayField];
13827             var len = newValue.length;
13828             var selStart = this.getRawValue().length;
13829             
13830             if(selStart != len){
13831                 this.setRawValue(newValue);
13832                 this.selectText(selStart, newValue.length);
13833             }
13834         }
13835     },
13836
13837     // private
13838     onSelect : function(record, index){
13839         
13840         if(this.fireEvent('beforeselect', this, record, index) !== false){
13841         
13842             this.setFromData(index > -1 ? record.data : false);
13843             
13844             this.collapse();
13845             this.fireEvent('select', this, record, index);
13846         }
13847     },
13848
13849     /**
13850      * Returns the currently selected field value or empty string if no value is set.
13851      * @return {String} value The selected value
13852      */
13853     getValue : function()
13854     {
13855         if(Roo.isIOS && this.useNativeIOS){
13856             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13857         }
13858         
13859         if(this.multiple){
13860             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13861         }
13862         
13863         if(this.valueField){
13864             return typeof this.value != 'undefined' ? this.value : '';
13865         }else{
13866             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13867         }
13868     },
13869     
13870     getRawValue : function()
13871     {
13872         if(Roo.isIOS && this.useNativeIOS){
13873             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13874         }
13875         
13876         var v = this.inputEl().getValue();
13877         
13878         return v;
13879     },
13880
13881     /**
13882      * Clears any text/value currently set in the field
13883      */
13884     clearValue : function(){
13885         
13886         if(this.hiddenField){
13887             this.hiddenField.dom.value = '';
13888         }
13889         this.value = '';
13890         this.setRawValue('');
13891         this.lastSelectionText = '';
13892         this.lastData = false;
13893         
13894         var close = this.closeTriggerEl();
13895         
13896         if(close){
13897             close.hide();
13898         }
13899         
13900         this.validate();
13901         
13902     },
13903
13904     /**
13905      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13906      * will be displayed in the field.  If the value does not match the data value of an existing item,
13907      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13908      * Otherwise the field will be blank (although the value will still be set).
13909      * @param {String} value The value to match
13910      */
13911     setValue : function(v)
13912     {
13913         if(Roo.isIOS && this.useNativeIOS){
13914             this.setIOSValue(v);
13915             return;
13916         }
13917         
13918         if(this.multiple){
13919             this.syncValue();
13920             return;
13921         }
13922         
13923         var text = v;
13924         if(this.valueField){
13925             var r = this.findRecord(this.valueField, v);
13926             if(r){
13927                 text = r.data[this.displayField];
13928             }else if(this.valueNotFoundText !== undefined){
13929                 text = this.valueNotFoundText;
13930             }
13931         }
13932         this.lastSelectionText = text;
13933         if(this.hiddenField){
13934             this.hiddenField.dom.value = v;
13935         }
13936         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13937         this.value = v;
13938         
13939         var close = this.closeTriggerEl();
13940         
13941         if(close){
13942             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13943         }
13944         
13945         this.validate();
13946     },
13947     /**
13948      * @property {Object} the last set data for the element
13949      */
13950     
13951     lastData : false,
13952     /**
13953      * Sets the value of the field based on a object which is related to the record format for the store.
13954      * @param {Object} value the value to set as. or false on reset?
13955      */
13956     setFromData : function(o){
13957         
13958         if(this.multiple){
13959             this.addItem(o);
13960             return;
13961         }
13962             
13963         var dv = ''; // display value
13964         var vv = ''; // value value..
13965         this.lastData = o;
13966         if (this.displayField) {
13967             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13968         } else {
13969             // this is an error condition!!!
13970             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13971         }
13972         
13973         if(this.valueField){
13974             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13975         }
13976         
13977         var close = this.closeTriggerEl();
13978         
13979         if(close){
13980             if(dv.length || vv * 1 > 0){
13981                 close.show() ;
13982                 this.blockFocus=true;
13983             } else {
13984                 close.hide();
13985             }             
13986         }
13987         
13988         if(this.hiddenField){
13989             this.hiddenField.dom.value = vv;
13990             
13991             this.lastSelectionText = dv;
13992             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13993             this.value = vv;
13994             return;
13995         }
13996         // no hidden field.. - we store the value in 'value', but still display
13997         // display field!!!!
13998         this.lastSelectionText = dv;
13999         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14000         this.value = vv;
14001         
14002         
14003         
14004     },
14005     // private
14006     reset : function(){
14007         // overridden so that last data is reset..
14008         
14009         if(this.multiple){
14010             this.clearItem();
14011             return;
14012         }
14013         
14014         this.setValue(this.originalValue);
14015         //this.clearInvalid();
14016         this.lastData = false;
14017         if (this.view) {
14018             this.view.clearSelections();
14019         }
14020         
14021         this.validate();
14022     },
14023     // private
14024     findRecord : function(prop, value){
14025         var record;
14026         if(this.store.getCount() > 0){
14027             this.store.each(function(r){
14028                 if(r.data[prop] == value){
14029                     record = r;
14030                     return false;
14031                 }
14032                 return true;
14033             });
14034         }
14035         return record;
14036     },
14037     
14038     getName: function()
14039     {
14040         // returns hidden if it's set..
14041         if (!this.rendered) {return ''};
14042         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14043         
14044     },
14045     // private
14046     onViewMove : function(e, t){
14047         this.inKeyMode = false;
14048     },
14049
14050     // private
14051     onViewOver : function(e, t){
14052         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14053             return;
14054         }
14055         var item = this.view.findItemFromChild(t);
14056         
14057         if(item){
14058             var index = this.view.indexOf(item);
14059             this.select(index, false);
14060         }
14061     },
14062
14063     // private
14064     onViewClick : function(view, doFocus, el, e)
14065     {
14066         var index = this.view.getSelectedIndexes()[0];
14067         
14068         var r = this.store.getAt(index);
14069         
14070         if(this.tickable){
14071             
14072             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14073                 return;
14074             }
14075             
14076             var rm = false;
14077             var _this = this;
14078             
14079             Roo.each(this.tickItems, function(v,k){
14080                 
14081                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14082                     Roo.log(v);
14083                     _this.tickItems.splice(k, 1);
14084                     
14085                     if(typeof(e) == 'undefined' && view == false){
14086                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14087                     }
14088                     
14089                     rm = true;
14090                     return;
14091                 }
14092             });
14093             
14094             if(rm){
14095                 return;
14096             }
14097             
14098             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14099                 this.tickItems.push(r.data);
14100             }
14101             
14102             if(typeof(e) == 'undefined' && view == false){
14103                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14104             }
14105                     
14106             return;
14107         }
14108         
14109         if(r){
14110             this.onSelect(r, index);
14111         }
14112         if(doFocus !== false && !this.blockFocus){
14113             this.inputEl().focus();
14114         }
14115     },
14116
14117     // private
14118     restrictHeight : function(){
14119         //this.innerList.dom.style.height = '';
14120         //var inner = this.innerList.dom;
14121         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14122         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14123         //this.list.beginUpdate();
14124         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14125         this.list.alignTo(this.inputEl(), this.listAlign);
14126         this.list.alignTo(this.inputEl(), this.listAlign);
14127         //this.list.endUpdate();
14128     },
14129
14130     // private
14131     onEmptyResults : function(){
14132         
14133         if(this.tickable && this.editable){
14134             this.hasFocus = false;
14135             this.restrictHeight();
14136             return;
14137         }
14138         
14139         this.collapse();
14140     },
14141
14142     /**
14143      * Returns true if the dropdown list is expanded, else false.
14144      */
14145     isExpanded : function(){
14146         return this.list.isVisible();
14147     },
14148
14149     /**
14150      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14151      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14152      * @param {String} value The data value of the item to select
14153      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14154      * selected item if it is not currently in view (defaults to true)
14155      * @return {Boolean} True if the value matched an item in the list, else false
14156      */
14157     selectByValue : function(v, scrollIntoView){
14158         if(v !== undefined && v !== null){
14159             var r = this.findRecord(this.valueField || this.displayField, v);
14160             if(r){
14161                 this.select(this.store.indexOf(r), scrollIntoView);
14162                 return true;
14163             }
14164         }
14165         return false;
14166     },
14167
14168     /**
14169      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14170      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14171      * @param {Number} index The zero-based index of the list item to select
14172      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14173      * selected item if it is not currently in view (defaults to true)
14174      */
14175     select : function(index, scrollIntoView){
14176         this.selectedIndex = index;
14177         this.view.select(index);
14178         if(scrollIntoView !== false){
14179             var el = this.view.getNode(index);
14180             /*
14181              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14182              */
14183             if(el){
14184                 this.list.scrollChildIntoView(el, false);
14185             }
14186         }
14187     },
14188
14189     // private
14190     selectNext : function(){
14191         var ct = this.store.getCount();
14192         if(ct > 0){
14193             if(this.selectedIndex == -1){
14194                 this.select(0);
14195             }else if(this.selectedIndex < ct-1){
14196                 this.select(this.selectedIndex+1);
14197             }
14198         }
14199     },
14200
14201     // private
14202     selectPrev : function(){
14203         var ct = this.store.getCount();
14204         if(ct > 0){
14205             if(this.selectedIndex == -1){
14206                 this.select(0);
14207             }else if(this.selectedIndex != 0){
14208                 this.select(this.selectedIndex-1);
14209             }
14210         }
14211     },
14212
14213     // private
14214     onKeyUp : function(e){
14215         if(this.editable !== false && !e.isSpecialKey()){
14216             this.lastKey = e.getKey();
14217             this.dqTask.delay(this.queryDelay);
14218         }
14219     },
14220
14221     // private
14222     validateBlur : function(){
14223         return !this.list || !this.list.isVisible();   
14224     },
14225
14226     // private
14227     initQuery : function(){
14228         
14229         var v = this.getRawValue();
14230         
14231         if(this.tickable && this.editable){
14232             v = this.tickableInputEl().getValue();
14233         }
14234         
14235         this.doQuery(v);
14236     },
14237
14238     // private
14239     doForce : function(){
14240         if(this.inputEl().dom.value.length > 0){
14241             this.inputEl().dom.value =
14242                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14243              
14244         }
14245     },
14246
14247     /**
14248      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14249      * query allowing the query action to be canceled if needed.
14250      * @param {String} query The SQL query to execute
14251      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14252      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14253      * saved in the current store (defaults to false)
14254      */
14255     doQuery : function(q, forceAll){
14256         
14257         if(q === undefined || q === null){
14258             q = '';
14259         }
14260         var qe = {
14261             query: q,
14262             forceAll: forceAll,
14263             combo: this,
14264             cancel:false
14265         };
14266         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14267             return false;
14268         }
14269         q = qe.query;
14270         
14271         forceAll = qe.forceAll;
14272         if(forceAll === true || (q.length >= this.minChars)){
14273             
14274             this.hasQuery = true;
14275             
14276             if(this.lastQuery != q || this.alwaysQuery){
14277                 this.lastQuery = q;
14278                 if(this.mode == 'local'){
14279                     this.selectedIndex = -1;
14280                     if(forceAll){
14281                         this.store.clearFilter();
14282                     }else{
14283                         
14284                         if(this.specialFilter){
14285                             this.fireEvent('specialfilter', this);
14286                             this.onLoad();
14287                             return;
14288                         }
14289                         
14290                         this.store.filter(this.displayField, q);
14291                     }
14292                     
14293                     this.store.fireEvent("datachanged", this.store);
14294                     
14295                     this.onLoad();
14296                     
14297                     
14298                 }else{
14299                     
14300                     this.store.baseParams[this.queryParam] = q;
14301                     
14302                     var options = {params : this.getParams(q)};
14303                     
14304                     if(this.loadNext){
14305                         options.add = true;
14306                         options.params.start = this.page * this.pageSize;
14307                     }
14308                     
14309                     this.store.load(options);
14310                     
14311                     /*
14312                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14313                      *  we should expand the list on onLoad
14314                      *  so command out it
14315                      */
14316 //                    this.expand();
14317                 }
14318             }else{
14319                 this.selectedIndex = -1;
14320                 this.onLoad();   
14321             }
14322         }
14323         
14324         this.loadNext = false;
14325     },
14326     
14327     // private
14328     getParams : function(q){
14329         var p = {};
14330         //p[this.queryParam] = q;
14331         
14332         if(this.pageSize){
14333             p.start = 0;
14334             p.limit = this.pageSize;
14335         }
14336         return p;
14337     },
14338
14339     /**
14340      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14341      */
14342     collapse : function(){
14343         if(!this.isExpanded()){
14344             return;
14345         }
14346         
14347         this.list.hide();
14348         
14349         this.hasFocus = false;
14350         
14351         if(this.tickable){
14352             this.okBtn.hide();
14353             this.cancelBtn.hide();
14354             this.trigger.show();
14355             
14356             if(this.editable){
14357                 this.tickableInputEl().dom.value = '';
14358                 this.tickableInputEl().blur();
14359             }
14360             
14361         }
14362         
14363         Roo.get(document).un('mousedown', this.collapseIf, this);
14364         Roo.get(document).un('mousewheel', this.collapseIf, this);
14365         if (!this.editable) {
14366             Roo.get(document).un('keydown', this.listKeyPress, this);
14367         }
14368         this.fireEvent('collapse', this);
14369         
14370         this.validate();
14371     },
14372
14373     // private
14374     collapseIf : function(e){
14375         var in_combo  = e.within(this.el);
14376         var in_list =  e.within(this.list);
14377         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14378         
14379         if (in_combo || in_list || is_list) {
14380             //e.stopPropagation();
14381             return;
14382         }
14383         
14384         if(this.tickable){
14385             this.onTickableFooterButtonClick(e, false, false);
14386         }
14387
14388         this.collapse();
14389         
14390     },
14391
14392     /**
14393      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14394      */
14395     expand : function(){
14396        
14397         if(this.isExpanded() || !this.hasFocus){
14398             return;
14399         }
14400         
14401         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14402         this.list.setWidth(lw);
14403         
14404         Roo.log('expand');
14405         
14406         this.list.show();
14407         
14408         this.restrictHeight();
14409         
14410         if(this.tickable){
14411             
14412             this.tickItems = Roo.apply([], this.item);
14413             
14414             this.okBtn.show();
14415             this.cancelBtn.show();
14416             this.trigger.hide();
14417             
14418             if(this.editable){
14419                 this.tickableInputEl().focus();
14420             }
14421             
14422         }
14423         
14424         Roo.get(document).on('mousedown', this.collapseIf, this);
14425         Roo.get(document).on('mousewheel', this.collapseIf, this);
14426         if (!this.editable) {
14427             Roo.get(document).on('keydown', this.listKeyPress, this);
14428         }
14429         
14430         this.fireEvent('expand', this);
14431     },
14432
14433     // private
14434     // Implements the default empty TriggerField.onTriggerClick function
14435     onTriggerClick : function(e)
14436     {
14437         Roo.log('trigger click');
14438         
14439         if(this.disabled || !this.triggerList){
14440             return;
14441         }
14442         
14443         this.page = 0;
14444         this.loadNext = false;
14445         
14446         if(this.isExpanded()){
14447             this.collapse();
14448             if (!this.blockFocus) {
14449                 this.inputEl().focus();
14450             }
14451             
14452         }else {
14453             this.hasFocus = true;
14454             if(this.triggerAction == 'all') {
14455                 this.doQuery(this.allQuery, true);
14456             } else {
14457                 this.doQuery(this.getRawValue());
14458             }
14459             if (!this.blockFocus) {
14460                 this.inputEl().focus();
14461             }
14462         }
14463     },
14464     
14465     onTickableTriggerClick : function(e)
14466     {
14467         if(this.disabled){
14468             return;
14469         }
14470         
14471         this.page = 0;
14472         this.loadNext = false;
14473         this.hasFocus = true;
14474         
14475         if(this.triggerAction == 'all') {
14476             this.doQuery(this.allQuery, true);
14477         } else {
14478             this.doQuery(this.getRawValue());
14479         }
14480     },
14481     
14482     onSearchFieldClick : function(e)
14483     {
14484         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14485             this.onTickableFooterButtonClick(e, false, false);
14486             return;
14487         }
14488         
14489         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14490             return;
14491         }
14492         
14493         this.page = 0;
14494         this.loadNext = false;
14495         this.hasFocus = true;
14496         
14497         if(this.triggerAction == 'all') {
14498             this.doQuery(this.allQuery, true);
14499         } else {
14500             this.doQuery(this.getRawValue());
14501         }
14502     },
14503     
14504     listKeyPress : function(e)
14505     {
14506         //Roo.log('listkeypress');
14507         // scroll to first matching element based on key pres..
14508         if (e.isSpecialKey()) {
14509             return false;
14510         }
14511         var k = String.fromCharCode(e.getKey()).toUpperCase();
14512         //Roo.log(k);
14513         var match  = false;
14514         var csel = this.view.getSelectedNodes();
14515         var cselitem = false;
14516         if (csel.length) {
14517             var ix = this.view.indexOf(csel[0]);
14518             cselitem  = this.store.getAt(ix);
14519             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14520                 cselitem = false;
14521             }
14522             
14523         }
14524         
14525         this.store.each(function(v) { 
14526             if (cselitem) {
14527                 // start at existing selection.
14528                 if (cselitem.id == v.id) {
14529                     cselitem = false;
14530                 }
14531                 return true;
14532             }
14533                 
14534             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14535                 match = this.store.indexOf(v);
14536                 return false;
14537             }
14538             return true;
14539         }, this);
14540         
14541         if (match === false) {
14542             return true; // no more action?
14543         }
14544         // scroll to?
14545         this.view.select(match);
14546         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14547         sn.scrollIntoView(sn.dom.parentNode, false);
14548     },
14549     
14550     onViewScroll : function(e, t){
14551         
14552         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){
14553             return;
14554         }
14555         
14556         this.hasQuery = true;
14557         
14558         this.loading = this.list.select('.loading', true).first();
14559         
14560         if(this.loading === null){
14561             this.list.createChild({
14562                 tag: 'div',
14563                 cls: 'loading roo-select2-more-results roo-select2-active',
14564                 html: 'Loading more results...'
14565             });
14566             
14567             this.loading = this.list.select('.loading', true).first();
14568             
14569             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14570             
14571             this.loading.hide();
14572         }
14573         
14574         this.loading.show();
14575         
14576         var _combo = this;
14577         
14578         this.page++;
14579         this.loadNext = true;
14580         
14581         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14582         
14583         return;
14584     },
14585     
14586     addItem : function(o)
14587     {   
14588         var dv = ''; // display value
14589         
14590         if (this.displayField) {
14591             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14592         } else {
14593             // this is an error condition!!!
14594             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14595         }
14596         
14597         if(!dv.length){
14598             return;
14599         }
14600         
14601         var choice = this.choices.createChild({
14602             tag: 'li',
14603             cls: 'roo-select2-search-choice',
14604             cn: [
14605                 {
14606                     tag: 'div',
14607                     html: dv
14608                 },
14609                 {
14610                     tag: 'a',
14611                     href: '#',
14612                     cls: 'roo-select2-search-choice-close fa fa-times',
14613                     tabindex: '-1'
14614                 }
14615             ]
14616             
14617         }, this.searchField);
14618         
14619         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14620         
14621         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14622         
14623         this.item.push(o);
14624         
14625         this.lastData = o;
14626         
14627         this.syncValue();
14628         
14629         this.inputEl().dom.value = '';
14630         
14631         this.validate();
14632     },
14633     
14634     onRemoveItem : function(e, _self, o)
14635     {
14636         e.preventDefault();
14637         
14638         this.lastItem = Roo.apply([], this.item);
14639         
14640         var index = this.item.indexOf(o.data) * 1;
14641         
14642         if( index < 0){
14643             Roo.log('not this item?!');
14644             return;
14645         }
14646         
14647         this.item.splice(index, 1);
14648         o.item.remove();
14649         
14650         this.syncValue();
14651         
14652         this.fireEvent('remove', this, e);
14653         
14654         this.validate();
14655         
14656     },
14657     
14658     syncValue : function()
14659     {
14660         if(!this.item.length){
14661             this.clearValue();
14662             return;
14663         }
14664             
14665         var value = [];
14666         var _this = this;
14667         Roo.each(this.item, function(i){
14668             if(_this.valueField){
14669                 value.push(i[_this.valueField]);
14670                 return;
14671             }
14672
14673             value.push(i);
14674         });
14675
14676         this.value = value.join(',');
14677
14678         if(this.hiddenField){
14679             this.hiddenField.dom.value = this.value;
14680         }
14681         
14682         this.store.fireEvent("datachanged", this.store);
14683         
14684         this.validate();
14685     },
14686     
14687     clearItem : function()
14688     {
14689         if(!this.multiple){
14690             return;
14691         }
14692         
14693         this.item = [];
14694         
14695         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14696            c.remove();
14697         });
14698         
14699         this.syncValue();
14700         
14701         this.validate();
14702         
14703         if(this.tickable && !Roo.isTouch){
14704             this.view.refresh();
14705         }
14706     },
14707     
14708     inputEl: function ()
14709     {
14710         if(Roo.isIOS && this.useNativeIOS){
14711             return this.el.select('select.roo-ios-select', true).first();
14712         }
14713         
14714         if(Roo.isTouch && this.mobileTouchView){
14715             return this.el.select('input.form-control',true).first();
14716         }
14717         
14718         if(this.tickable){
14719             return this.searchField;
14720         }
14721         
14722         return this.el.select('input.form-control',true).first();
14723     },
14724     
14725     onTickableFooterButtonClick : function(e, btn, el)
14726     {
14727         e.preventDefault();
14728         
14729         this.lastItem = Roo.apply([], this.item);
14730         
14731         if(btn && btn.name == 'cancel'){
14732             this.tickItems = Roo.apply([], this.item);
14733             this.collapse();
14734             return;
14735         }
14736         
14737         this.clearItem();
14738         
14739         var _this = this;
14740         
14741         Roo.each(this.tickItems, function(o){
14742             _this.addItem(o);
14743         });
14744         
14745         this.collapse();
14746         
14747     },
14748     
14749     validate : function()
14750     {
14751         if(this.getVisibilityEl().hasClass('hidden')){
14752             return true;
14753         }
14754         
14755         var v = this.getRawValue();
14756         
14757         if(this.multiple){
14758             v = this.getValue();
14759         }
14760         
14761         if(this.disabled || this.allowBlank || v.length){
14762             this.markValid();
14763             return true;
14764         }
14765         
14766         this.markInvalid();
14767         return false;
14768     },
14769     
14770     tickableInputEl : function()
14771     {
14772         if(!this.tickable || !this.editable){
14773             return this.inputEl();
14774         }
14775         
14776         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14777     },
14778     
14779     
14780     getAutoCreateTouchView : function()
14781     {
14782         var id = Roo.id();
14783         
14784         var cfg = {
14785             cls: 'form-group' //input-group
14786         };
14787         
14788         var input =  {
14789             tag: 'input',
14790             id : id,
14791             type : this.inputType,
14792             cls : 'form-control x-combo-noedit',
14793             autocomplete: 'new-password',
14794             placeholder : this.placeholder || '',
14795             readonly : true
14796         };
14797         
14798         if (this.name) {
14799             input.name = this.name;
14800         }
14801         
14802         if (this.size) {
14803             input.cls += ' input-' + this.size;
14804         }
14805         
14806         if (this.disabled) {
14807             input.disabled = true;
14808         }
14809         
14810         var inputblock = {
14811             cls : '',
14812             cn : [
14813                 input
14814             ]
14815         };
14816         
14817         if(this.before){
14818             inputblock.cls += ' input-group';
14819             
14820             inputblock.cn.unshift({
14821                 tag :'span',
14822                 cls : 'input-group-addon',
14823                 html : this.before
14824             });
14825         }
14826         
14827         if(this.removable && !this.multiple){
14828             inputblock.cls += ' roo-removable';
14829             
14830             inputblock.cn.push({
14831                 tag: 'button',
14832                 html : 'x',
14833                 cls : 'roo-combo-removable-btn close'
14834             });
14835         }
14836
14837         if(this.hasFeedback && !this.allowBlank){
14838             
14839             inputblock.cls += ' has-feedback';
14840             
14841             inputblock.cn.push({
14842                 tag: 'span',
14843                 cls: 'glyphicon form-control-feedback'
14844             });
14845             
14846         }
14847         
14848         if (this.after) {
14849             
14850             inputblock.cls += (this.before) ? '' : ' input-group';
14851             
14852             inputblock.cn.push({
14853                 tag :'span',
14854                 cls : 'input-group-addon',
14855                 html : this.after
14856             });
14857         }
14858
14859         var box = {
14860             tag: 'div',
14861             cn: [
14862                 {
14863                     tag: 'input',
14864                     type : 'hidden',
14865                     cls: 'form-hidden-field'
14866                 },
14867                 inputblock
14868             ]
14869             
14870         };
14871         
14872         if(this.multiple){
14873             box = {
14874                 tag: 'div',
14875                 cn: [
14876                     {
14877                         tag: 'input',
14878                         type : 'hidden',
14879                         cls: 'form-hidden-field'
14880                     },
14881                     {
14882                         tag: 'ul',
14883                         cls: 'roo-select2-choices',
14884                         cn:[
14885                             {
14886                                 tag: 'li',
14887                                 cls: 'roo-select2-search-field',
14888                                 cn: [
14889
14890                                     inputblock
14891                                 ]
14892                             }
14893                         ]
14894                     }
14895                 ]
14896             }
14897         };
14898         
14899         var combobox = {
14900             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14901             cn: [
14902                 box
14903             ]
14904         };
14905         
14906         if(!this.multiple && this.showToggleBtn){
14907             
14908             var caret = {
14909                         tag: 'span',
14910                         cls: 'caret'
14911             };
14912             
14913             if (this.caret != false) {
14914                 caret = {
14915                      tag: 'i',
14916                      cls: 'fa fa-' + this.caret
14917                 };
14918                 
14919             }
14920             
14921             combobox.cn.push({
14922                 tag :'span',
14923                 cls : 'input-group-addon btn dropdown-toggle',
14924                 cn : [
14925                     caret,
14926                     {
14927                         tag: 'span',
14928                         cls: 'combobox-clear',
14929                         cn  : [
14930                             {
14931                                 tag : 'i',
14932                                 cls: 'icon-remove'
14933                             }
14934                         ]
14935                     }
14936                 ]
14937
14938             })
14939         }
14940         
14941         if(this.multiple){
14942             combobox.cls += ' roo-select2-container-multi';
14943         }
14944         
14945         var align = this.labelAlign || this.parentLabelAlign();
14946         
14947         if (align ==='left' && this.fieldLabel.length) {
14948
14949             cfg.cn = [
14950                 {
14951                    tag : 'i',
14952                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14953                    tooltip : 'This field is required'
14954                 },
14955                 {
14956                     tag: 'label',
14957                     cls : 'control-label',
14958                     html : this.fieldLabel
14959
14960                 },
14961                 {
14962                     cls : '', 
14963                     cn: [
14964                         combobox
14965                     ]
14966                 }
14967             ];
14968             
14969             var labelCfg = cfg.cn[1];
14970             var contentCfg = cfg.cn[2];
14971             
14972
14973             if(this.indicatorpos == 'right'){
14974                 cfg.cn = [
14975                     {
14976                         tag: 'label',
14977                         'for' :  id,
14978                         cls : 'control-label',
14979                         cn : [
14980                             {
14981                                 tag : 'span',
14982                                 html : this.fieldLabel
14983                             },
14984                             {
14985                                 tag : 'i',
14986                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14987                                 tooltip : 'This field is required'
14988                             }
14989                         ]
14990                     },
14991                     {
14992                         cls : "",
14993                         cn: [
14994                             combobox
14995                         ]
14996                     }
14997
14998                 ];
14999                 
15000                 labelCfg = cfg.cn[0];
15001                 contentCfg = cfg.cn[1];
15002             }
15003             
15004            
15005             
15006             if(this.labelWidth > 12){
15007                 labelCfg.style = "width: " + this.labelWidth + 'px';
15008             }
15009             
15010             if(this.labelWidth < 13 && this.labelmd == 0){
15011                 this.labelmd = this.labelWidth;
15012             }
15013             
15014             if(this.labellg > 0){
15015                 labelCfg.cls += ' col-lg-' + this.labellg;
15016                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15017             }
15018             
15019             if(this.labelmd > 0){
15020                 labelCfg.cls += ' col-md-' + this.labelmd;
15021                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15022             }
15023             
15024             if(this.labelsm > 0){
15025                 labelCfg.cls += ' col-sm-' + this.labelsm;
15026                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15027             }
15028             
15029             if(this.labelxs > 0){
15030                 labelCfg.cls += ' col-xs-' + this.labelxs;
15031                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15032             }
15033                 
15034                 
15035         } else if ( this.fieldLabel.length) {
15036             cfg.cn = [
15037                 {
15038                    tag : 'i',
15039                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15040                    tooltip : 'This field is required'
15041                 },
15042                 {
15043                     tag: 'label',
15044                     cls : 'control-label',
15045                     html : this.fieldLabel
15046
15047                 },
15048                 {
15049                     cls : '', 
15050                     cn: [
15051                         combobox
15052                     ]
15053                 }
15054             ];
15055             
15056             if(this.indicatorpos == 'right'){
15057                 cfg.cn = [
15058                     {
15059                         tag: 'label',
15060                         cls : 'control-label',
15061                         html : this.fieldLabel,
15062                         cn : [
15063                             {
15064                                tag : 'i',
15065                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15066                                tooltip : 'This field is required'
15067                             }
15068                         ]
15069                     },
15070                     {
15071                         cls : '', 
15072                         cn: [
15073                             combobox
15074                         ]
15075                     }
15076                 ];
15077             }
15078         } else {
15079             cfg.cn = combobox;    
15080         }
15081         
15082         
15083         var settings = this;
15084         
15085         ['xs','sm','md','lg'].map(function(size){
15086             if (settings[size]) {
15087                 cfg.cls += ' col-' + size + '-' + settings[size];
15088             }
15089         });
15090         
15091         return cfg;
15092     },
15093     
15094     initTouchView : function()
15095     {
15096         this.renderTouchView();
15097         
15098         this.touchViewEl.on('scroll', function(){
15099             this.el.dom.scrollTop = 0;
15100         }, this);
15101         
15102         this.originalValue = this.getValue();
15103         
15104         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15105         
15106         this.inputEl().on("click", this.showTouchView, this);
15107         if (this.triggerEl) {
15108             this.triggerEl.on("click", this.showTouchView, this);
15109         }
15110         
15111         
15112         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15113         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15114         
15115         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15116         
15117         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15118         this.store.on('load', this.onTouchViewLoad, this);
15119         this.store.on('loadexception', this.onTouchViewLoadException, this);
15120         
15121         if(this.hiddenName){
15122             
15123             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15124             
15125             this.hiddenField.dom.value =
15126                 this.hiddenValue !== undefined ? this.hiddenValue :
15127                 this.value !== undefined ? this.value : '';
15128         
15129             this.el.dom.removeAttribute('name');
15130             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15131         }
15132         
15133         if(this.multiple){
15134             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15135             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15136         }
15137         
15138         if(this.removable && !this.multiple){
15139             var close = this.closeTriggerEl();
15140             if(close){
15141                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15142                 close.on('click', this.removeBtnClick, this, close);
15143             }
15144         }
15145         /*
15146          * fix the bug in Safari iOS8
15147          */
15148         this.inputEl().on("focus", function(e){
15149             document.activeElement.blur();
15150         }, this);
15151         
15152         return;
15153         
15154         
15155     },
15156     
15157     renderTouchView : function()
15158     {
15159         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15160         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15161         
15162         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15163         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15164         
15165         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15166         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15167         this.touchViewBodyEl.setStyle('overflow', 'auto');
15168         
15169         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15170         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15171         
15172         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15173         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15174         
15175     },
15176     
15177     showTouchView : function()
15178     {
15179         if(this.disabled){
15180             return;
15181         }
15182         
15183         this.touchViewHeaderEl.hide();
15184
15185         if(this.modalTitle.length){
15186             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15187             this.touchViewHeaderEl.show();
15188         }
15189
15190         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15191         this.touchViewEl.show();
15192
15193         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15194         
15195         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15196         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15197
15198         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15199
15200         if(this.modalTitle.length){
15201             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15202         }
15203         
15204         this.touchViewBodyEl.setHeight(bodyHeight);
15205
15206         if(this.animate){
15207             var _this = this;
15208             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15209         }else{
15210             this.touchViewEl.addClass('in');
15211         }
15212
15213         this.doTouchViewQuery();
15214         
15215     },
15216     
15217     hideTouchView : function()
15218     {
15219         this.touchViewEl.removeClass('in');
15220
15221         if(this.animate){
15222             var _this = this;
15223             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15224         }else{
15225             this.touchViewEl.setStyle('display', 'none');
15226         }
15227         
15228     },
15229     
15230     setTouchViewValue : function()
15231     {
15232         if(this.multiple){
15233             this.clearItem();
15234         
15235             var _this = this;
15236
15237             Roo.each(this.tickItems, function(o){
15238                 this.addItem(o);
15239             }, this);
15240         }
15241         
15242         this.hideTouchView();
15243     },
15244     
15245     doTouchViewQuery : function()
15246     {
15247         var qe = {
15248             query: '',
15249             forceAll: true,
15250             combo: this,
15251             cancel:false
15252         };
15253         
15254         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15255             return false;
15256         }
15257         
15258         if(!this.alwaysQuery || this.mode == 'local'){
15259             this.onTouchViewLoad();
15260             return;
15261         }
15262         
15263         this.store.load();
15264     },
15265     
15266     onTouchViewBeforeLoad : function(combo,opts)
15267     {
15268         return;
15269     },
15270
15271     // private
15272     onTouchViewLoad : function()
15273     {
15274         if(this.store.getCount() < 1){
15275             this.onTouchViewEmptyResults();
15276             return;
15277         }
15278         
15279         this.clearTouchView();
15280         
15281         var rawValue = this.getRawValue();
15282         
15283         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15284         
15285         this.tickItems = [];
15286         
15287         this.store.data.each(function(d, rowIndex){
15288             var row = this.touchViewListGroup.createChild(template);
15289             
15290             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15291                 row.addClass(d.data.cls);
15292             }
15293             
15294             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15295                 var cfg = {
15296                     data : d.data,
15297                     html : d.data[this.displayField]
15298                 };
15299                 
15300                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15301                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15302                 }
15303             }
15304             row.removeClass('selected');
15305             if(!this.multiple && this.valueField &&
15306                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15307             {
15308                 // radio buttons..
15309                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15310                 row.addClass('selected');
15311             }
15312             
15313             if(this.multiple && this.valueField &&
15314                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15315             {
15316                 
15317                 // checkboxes...
15318                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15319                 this.tickItems.push(d.data);
15320             }
15321             
15322             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15323             
15324         }, this);
15325         
15326         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15327         
15328         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15329
15330         if(this.modalTitle.length){
15331             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15332         }
15333
15334         var listHeight = this.touchViewListGroup.getHeight();
15335         
15336         var _this = this;
15337         
15338         if(firstChecked && listHeight > bodyHeight){
15339             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15340         }
15341         
15342     },
15343     
15344     onTouchViewLoadException : function()
15345     {
15346         this.hideTouchView();
15347     },
15348     
15349     onTouchViewEmptyResults : function()
15350     {
15351         this.clearTouchView();
15352         
15353         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15354         
15355         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15356         
15357     },
15358     
15359     clearTouchView : function()
15360     {
15361         this.touchViewListGroup.dom.innerHTML = '';
15362     },
15363     
15364     onTouchViewClick : function(e, el, o)
15365     {
15366         e.preventDefault();
15367         
15368         var row = o.row;
15369         var rowIndex = o.rowIndex;
15370         
15371         var r = this.store.getAt(rowIndex);
15372         
15373         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15374             
15375             if(!this.multiple){
15376                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15377                     c.dom.removeAttribute('checked');
15378                 }, this);
15379
15380                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15381
15382                 this.setFromData(r.data);
15383
15384                 var close = this.closeTriggerEl();
15385
15386                 if(close){
15387                     close.show();
15388                 }
15389
15390                 this.hideTouchView();
15391
15392                 this.fireEvent('select', this, r, rowIndex);
15393
15394                 return;
15395             }
15396
15397             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15398                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15399                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15400                 return;
15401             }
15402
15403             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15404             this.addItem(r.data);
15405             this.tickItems.push(r.data);
15406         }
15407     },
15408     
15409     getAutoCreateNativeIOS : function()
15410     {
15411         var cfg = {
15412             cls: 'form-group' //input-group,
15413         };
15414         
15415         var combobox =  {
15416             tag: 'select',
15417             cls : 'roo-ios-select'
15418         };
15419         
15420         if (this.name) {
15421             combobox.name = this.name;
15422         }
15423         
15424         if (this.disabled) {
15425             combobox.disabled = true;
15426         }
15427         
15428         var settings = this;
15429         
15430         ['xs','sm','md','lg'].map(function(size){
15431             if (settings[size]) {
15432                 cfg.cls += ' col-' + size + '-' + settings[size];
15433             }
15434         });
15435         
15436         cfg.cn = combobox;
15437         
15438         return cfg;
15439         
15440     },
15441     
15442     initIOSView : function()
15443     {
15444         this.store.on('load', this.onIOSViewLoad, this);
15445         
15446         return;
15447     },
15448     
15449     onIOSViewLoad : function()
15450     {
15451         if(this.store.getCount() < 1){
15452             return;
15453         }
15454         
15455         this.clearIOSView();
15456         
15457         if(this.allowBlank) {
15458             
15459             var default_text = '-- SELECT --';
15460             
15461             if(this.placeholder.length){
15462                 default_text = this.placeholder;
15463             }
15464             
15465             if(this.emptyTitle.length){
15466                 default_text += ' - ' + this.emptyTitle + ' -';
15467             }
15468             
15469             var opt = this.inputEl().createChild({
15470                 tag: 'option',
15471                 value : 0,
15472                 html : default_text
15473             });
15474             
15475             var o = {};
15476             o[this.valueField] = 0;
15477             o[this.displayField] = default_text;
15478             
15479             this.ios_options.push({
15480                 data : o,
15481                 el : opt
15482             });
15483             
15484         }
15485         
15486         this.store.data.each(function(d, rowIndex){
15487             
15488             var html = '';
15489             
15490             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15491                 html = d.data[this.displayField];
15492             }
15493             
15494             var value = '';
15495             
15496             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15497                 value = d.data[this.valueField];
15498             }
15499             
15500             var option = {
15501                 tag: 'option',
15502                 value : value,
15503                 html : html
15504             };
15505             
15506             if(this.value == d.data[this.valueField]){
15507                 option['selected'] = true;
15508             }
15509             
15510             var opt = this.inputEl().createChild(option);
15511             
15512             this.ios_options.push({
15513                 data : d.data,
15514                 el : opt
15515             });
15516             
15517         }, this);
15518         
15519         this.inputEl().on('change', function(){
15520            this.fireEvent('select', this);
15521         }, this);
15522         
15523     },
15524     
15525     clearIOSView: function()
15526     {
15527         this.inputEl().dom.innerHTML = '';
15528         
15529         this.ios_options = [];
15530     },
15531     
15532     setIOSValue: function(v)
15533     {
15534         this.value = v;
15535         
15536         if(!this.ios_options){
15537             return;
15538         }
15539         
15540         Roo.each(this.ios_options, function(opts){
15541            
15542            opts.el.dom.removeAttribute('selected');
15543            
15544            if(opts.data[this.valueField] != v){
15545                return;
15546            }
15547            
15548            opts.el.dom.setAttribute('selected', true);
15549            
15550         }, this);
15551     }
15552
15553     /** 
15554     * @cfg {Boolean} grow 
15555     * @hide 
15556     */
15557     /** 
15558     * @cfg {Number} growMin 
15559     * @hide 
15560     */
15561     /** 
15562     * @cfg {Number} growMax 
15563     * @hide 
15564     */
15565     /**
15566      * @hide
15567      * @method autoSize
15568      */
15569 });
15570
15571 Roo.apply(Roo.bootstrap.ComboBox,  {
15572     
15573     header : {
15574         tag: 'div',
15575         cls: 'modal-header',
15576         cn: [
15577             {
15578                 tag: 'h4',
15579                 cls: 'modal-title'
15580             }
15581         ]
15582     },
15583     
15584     body : {
15585         tag: 'div',
15586         cls: 'modal-body',
15587         cn: [
15588             {
15589                 tag: 'ul',
15590                 cls: 'list-group'
15591             }
15592         ]
15593     },
15594     
15595     listItemRadio : {
15596         tag: 'li',
15597         cls: 'list-group-item',
15598         cn: [
15599             {
15600                 tag: 'span',
15601                 cls: 'roo-combobox-list-group-item-value'
15602             },
15603             {
15604                 tag: 'div',
15605                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15606                 cn: [
15607                     {
15608                         tag: 'input',
15609                         type: 'radio'
15610                     },
15611                     {
15612                         tag: 'label'
15613                     }
15614                 ]
15615             }
15616         ]
15617     },
15618     
15619     listItemCheckbox : {
15620         tag: 'li',
15621         cls: 'list-group-item',
15622         cn: [
15623             {
15624                 tag: 'span',
15625                 cls: 'roo-combobox-list-group-item-value'
15626             },
15627             {
15628                 tag: 'div',
15629                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15630                 cn: [
15631                     {
15632                         tag: 'input',
15633                         type: 'checkbox'
15634                     },
15635                     {
15636                         tag: 'label'
15637                     }
15638                 ]
15639             }
15640         ]
15641     },
15642     
15643     emptyResult : {
15644         tag: 'div',
15645         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15646     },
15647     
15648     footer : {
15649         tag: 'div',
15650         cls: 'modal-footer',
15651         cn: [
15652             {
15653                 tag: 'div',
15654                 cls: 'row',
15655                 cn: [
15656                     {
15657                         tag: 'div',
15658                         cls: 'col-xs-6 text-left',
15659                         cn: {
15660                             tag: 'button',
15661                             cls: 'btn btn-danger roo-touch-view-cancel',
15662                             html: 'Cancel'
15663                         }
15664                     },
15665                     {
15666                         tag: 'div',
15667                         cls: 'col-xs-6 text-right',
15668                         cn: {
15669                             tag: 'button',
15670                             cls: 'btn btn-success roo-touch-view-ok',
15671                             html: 'OK'
15672                         }
15673                     }
15674                 ]
15675             }
15676         ]
15677         
15678     }
15679 });
15680
15681 Roo.apply(Roo.bootstrap.ComboBox,  {
15682     
15683     touchViewTemplate : {
15684         tag: 'div',
15685         cls: 'modal fade roo-combobox-touch-view',
15686         cn: [
15687             {
15688                 tag: 'div',
15689                 cls: 'modal-dialog',
15690                 style : 'position:fixed', // we have to fix position....
15691                 cn: [
15692                     {
15693                         tag: 'div',
15694                         cls: 'modal-content',
15695                         cn: [
15696                             Roo.bootstrap.ComboBox.header,
15697                             Roo.bootstrap.ComboBox.body,
15698                             Roo.bootstrap.ComboBox.footer
15699                         ]
15700                     }
15701                 ]
15702             }
15703         ]
15704     }
15705 });/*
15706  * Based on:
15707  * Ext JS Library 1.1.1
15708  * Copyright(c) 2006-2007, Ext JS, LLC.
15709  *
15710  * Originally Released Under LGPL - original licence link has changed is not relivant.
15711  *
15712  * Fork - LGPL
15713  * <script type="text/javascript">
15714  */
15715
15716 /**
15717  * @class Roo.View
15718  * @extends Roo.util.Observable
15719  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15720  * This class also supports single and multi selection modes. <br>
15721  * Create a data model bound view:
15722  <pre><code>
15723  var store = new Roo.data.Store(...);
15724
15725  var view = new Roo.View({
15726     el : "my-element",
15727     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15728  
15729     singleSelect: true,
15730     selectedClass: "ydataview-selected",
15731     store: store
15732  });
15733
15734  // listen for node click?
15735  view.on("click", function(vw, index, node, e){
15736  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15737  });
15738
15739  // load XML data
15740  dataModel.load("foobar.xml");
15741  </code></pre>
15742  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15743  * <br><br>
15744  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15745  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15746  * 
15747  * Note: old style constructor is still suported (container, template, config)
15748  * 
15749  * @constructor
15750  * Create a new View
15751  * @param {Object} config The config object
15752  * 
15753  */
15754 Roo.View = function(config, depreciated_tpl, depreciated_config){
15755     
15756     this.parent = false;
15757     
15758     if (typeof(depreciated_tpl) == 'undefined') {
15759         // new way.. - universal constructor.
15760         Roo.apply(this, config);
15761         this.el  = Roo.get(this.el);
15762     } else {
15763         // old format..
15764         this.el  = Roo.get(config);
15765         this.tpl = depreciated_tpl;
15766         Roo.apply(this, depreciated_config);
15767     }
15768     this.wrapEl  = this.el.wrap().wrap();
15769     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15770     
15771     
15772     if(typeof(this.tpl) == "string"){
15773         this.tpl = new Roo.Template(this.tpl);
15774     } else {
15775         // support xtype ctors..
15776         this.tpl = new Roo.factory(this.tpl, Roo);
15777     }
15778     
15779     
15780     this.tpl.compile();
15781     
15782     /** @private */
15783     this.addEvents({
15784         /**
15785          * @event beforeclick
15786          * Fires before a click is processed. Returns false to cancel the default action.
15787          * @param {Roo.View} this
15788          * @param {Number} index The index of the target node
15789          * @param {HTMLElement} node The target node
15790          * @param {Roo.EventObject} e The raw event object
15791          */
15792             "beforeclick" : true,
15793         /**
15794          * @event click
15795          * Fires when a template node is clicked.
15796          * @param {Roo.View} this
15797          * @param {Number} index The index of the target node
15798          * @param {HTMLElement} node The target node
15799          * @param {Roo.EventObject} e The raw event object
15800          */
15801             "click" : true,
15802         /**
15803          * @event dblclick
15804          * Fires when a template node is double clicked.
15805          * @param {Roo.View} this
15806          * @param {Number} index The index of the target node
15807          * @param {HTMLElement} node The target node
15808          * @param {Roo.EventObject} e The raw event object
15809          */
15810             "dblclick" : true,
15811         /**
15812          * @event contextmenu
15813          * Fires when a template node is right clicked.
15814          * @param {Roo.View} this
15815          * @param {Number} index The index of the target node
15816          * @param {HTMLElement} node The target node
15817          * @param {Roo.EventObject} e The raw event object
15818          */
15819             "contextmenu" : true,
15820         /**
15821          * @event selectionchange
15822          * Fires when the selected nodes change.
15823          * @param {Roo.View} this
15824          * @param {Array} selections Array of the selected nodes
15825          */
15826             "selectionchange" : true,
15827     
15828         /**
15829          * @event beforeselect
15830          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15831          * @param {Roo.View} this
15832          * @param {HTMLElement} node The node to be selected
15833          * @param {Array} selections Array of currently selected nodes
15834          */
15835             "beforeselect" : true,
15836         /**
15837          * @event preparedata
15838          * Fires on every row to render, to allow you to change the data.
15839          * @param {Roo.View} this
15840          * @param {Object} data to be rendered (change this)
15841          */
15842           "preparedata" : true
15843           
15844           
15845         });
15846
15847
15848
15849     this.el.on({
15850         "click": this.onClick,
15851         "dblclick": this.onDblClick,
15852         "contextmenu": this.onContextMenu,
15853         scope:this
15854     });
15855
15856     this.selections = [];
15857     this.nodes = [];
15858     this.cmp = new Roo.CompositeElementLite([]);
15859     if(this.store){
15860         this.store = Roo.factory(this.store, Roo.data);
15861         this.setStore(this.store, true);
15862     }
15863     
15864     if ( this.footer && this.footer.xtype) {
15865            
15866          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15867         
15868         this.footer.dataSource = this.store;
15869         this.footer.container = fctr;
15870         this.footer = Roo.factory(this.footer, Roo);
15871         fctr.insertFirst(this.el);
15872         
15873         // this is a bit insane - as the paging toolbar seems to detach the el..
15874 //        dom.parentNode.parentNode.parentNode
15875          // they get detached?
15876     }
15877     
15878     
15879     Roo.View.superclass.constructor.call(this);
15880     
15881     
15882 };
15883
15884 Roo.extend(Roo.View, Roo.util.Observable, {
15885     
15886      /**
15887      * @cfg {Roo.data.Store} store Data store to load data from.
15888      */
15889     store : false,
15890     
15891     /**
15892      * @cfg {String|Roo.Element} el The container element.
15893      */
15894     el : '',
15895     
15896     /**
15897      * @cfg {String|Roo.Template} tpl The template used by this View 
15898      */
15899     tpl : false,
15900     /**
15901      * @cfg {String} dataName the named area of the template to use as the data area
15902      *                          Works with domtemplates roo-name="name"
15903      */
15904     dataName: false,
15905     /**
15906      * @cfg {String} selectedClass The css class to add to selected nodes
15907      */
15908     selectedClass : "x-view-selected",
15909      /**
15910      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15911      */
15912     emptyText : "",
15913     
15914     /**
15915      * @cfg {String} text to display on mask (default Loading)
15916      */
15917     mask : false,
15918     /**
15919      * @cfg {Boolean} multiSelect Allow multiple selection
15920      */
15921     multiSelect : false,
15922     /**
15923      * @cfg {Boolean} singleSelect Allow single selection
15924      */
15925     singleSelect:  false,
15926     
15927     /**
15928      * @cfg {Boolean} toggleSelect - selecting 
15929      */
15930     toggleSelect : false,
15931     
15932     /**
15933      * @cfg {Boolean} tickable - selecting 
15934      */
15935     tickable : false,
15936     
15937     /**
15938      * Returns the element this view is bound to.
15939      * @return {Roo.Element}
15940      */
15941     getEl : function(){
15942         return this.wrapEl;
15943     },
15944     
15945     
15946
15947     /**
15948      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15949      */
15950     refresh : function(){
15951         //Roo.log('refresh');
15952         var t = this.tpl;
15953         
15954         // if we are using something like 'domtemplate', then
15955         // the what gets used is:
15956         // t.applySubtemplate(NAME, data, wrapping data..)
15957         // the outer template then get' applied with
15958         //     the store 'extra data'
15959         // and the body get's added to the
15960         //      roo-name="data" node?
15961         //      <span class='roo-tpl-{name}'></span> ?????
15962         
15963         
15964         
15965         this.clearSelections();
15966         this.el.update("");
15967         var html = [];
15968         var records = this.store.getRange();
15969         if(records.length < 1) {
15970             
15971             // is this valid??  = should it render a template??
15972             
15973             this.el.update(this.emptyText);
15974             return;
15975         }
15976         var el = this.el;
15977         if (this.dataName) {
15978             this.el.update(t.apply(this.store.meta)); //????
15979             el = this.el.child('.roo-tpl-' + this.dataName);
15980         }
15981         
15982         for(var i = 0, len = records.length; i < len; i++){
15983             var data = this.prepareData(records[i].data, i, records[i]);
15984             this.fireEvent("preparedata", this, data, i, records[i]);
15985             
15986             var d = Roo.apply({}, data);
15987             
15988             if(this.tickable){
15989                 Roo.apply(d, {'roo-id' : Roo.id()});
15990                 
15991                 var _this = this;
15992             
15993                 Roo.each(this.parent.item, function(item){
15994                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15995                         return;
15996                     }
15997                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15998                 });
15999             }
16000             
16001             html[html.length] = Roo.util.Format.trim(
16002                 this.dataName ?
16003                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16004                     t.apply(d)
16005             );
16006         }
16007         
16008         
16009         
16010         el.update(html.join(""));
16011         this.nodes = el.dom.childNodes;
16012         this.updateIndexes(0);
16013     },
16014     
16015
16016     /**
16017      * Function to override to reformat the data that is sent to
16018      * the template for each node.
16019      * DEPRICATED - use the preparedata event handler.
16020      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16021      * a JSON object for an UpdateManager bound view).
16022      */
16023     prepareData : function(data, index, record)
16024     {
16025         this.fireEvent("preparedata", this, data, index, record);
16026         return data;
16027     },
16028
16029     onUpdate : function(ds, record){
16030         // Roo.log('on update');   
16031         this.clearSelections();
16032         var index = this.store.indexOf(record);
16033         var n = this.nodes[index];
16034         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16035         n.parentNode.removeChild(n);
16036         this.updateIndexes(index, index);
16037     },
16038
16039     
16040     
16041 // --------- FIXME     
16042     onAdd : function(ds, records, index)
16043     {
16044         //Roo.log(['on Add', ds, records, index] );        
16045         this.clearSelections();
16046         if(this.nodes.length == 0){
16047             this.refresh();
16048             return;
16049         }
16050         var n = this.nodes[index];
16051         for(var i = 0, len = records.length; i < len; i++){
16052             var d = this.prepareData(records[i].data, i, records[i]);
16053             if(n){
16054                 this.tpl.insertBefore(n, d);
16055             }else{
16056                 
16057                 this.tpl.append(this.el, d);
16058             }
16059         }
16060         this.updateIndexes(index);
16061     },
16062
16063     onRemove : function(ds, record, index){
16064        // Roo.log('onRemove');
16065         this.clearSelections();
16066         var el = this.dataName  ?
16067             this.el.child('.roo-tpl-' + this.dataName) :
16068             this.el; 
16069         
16070         el.dom.removeChild(this.nodes[index]);
16071         this.updateIndexes(index);
16072     },
16073
16074     /**
16075      * Refresh an individual node.
16076      * @param {Number} index
16077      */
16078     refreshNode : function(index){
16079         this.onUpdate(this.store, this.store.getAt(index));
16080     },
16081
16082     updateIndexes : function(startIndex, endIndex){
16083         var ns = this.nodes;
16084         startIndex = startIndex || 0;
16085         endIndex = endIndex || ns.length - 1;
16086         for(var i = startIndex; i <= endIndex; i++){
16087             ns[i].nodeIndex = i;
16088         }
16089     },
16090
16091     /**
16092      * Changes the data store this view uses and refresh the view.
16093      * @param {Store} store
16094      */
16095     setStore : function(store, initial){
16096         if(!initial && this.store){
16097             this.store.un("datachanged", this.refresh);
16098             this.store.un("add", this.onAdd);
16099             this.store.un("remove", this.onRemove);
16100             this.store.un("update", this.onUpdate);
16101             this.store.un("clear", this.refresh);
16102             this.store.un("beforeload", this.onBeforeLoad);
16103             this.store.un("load", this.onLoad);
16104             this.store.un("loadexception", this.onLoad);
16105         }
16106         if(store){
16107           
16108             store.on("datachanged", this.refresh, this);
16109             store.on("add", this.onAdd, this);
16110             store.on("remove", this.onRemove, this);
16111             store.on("update", this.onUpdate, this);
16112             store.on("clear", this.refresh, this);
16113             store.on("beforeload", this.onBeforeLoad, this);
16114             store.on("load", this.onLoad, this);
16115             store.on("loadexception", this.onLoad, this);
16116         }
16117         
16118         if(store){
16119             this.refresh();
16120         }
16121     },
16122     /**
16123      * onbeforeLoad - masks the loading area.
16124      *
16125      */
16126     onBeforeLoad : function(store,opts)
16127     {
16128          //Roo.log('onBeforeLoad');   
16129         if (!opts.add) {
16130             this.el.update("");
16131         }
16132         this.el.mask(this.mask ? this.mask : "Loading" ); 
16133     },
16134     onLoad : function ()
16135     {
16136         this.el.unmask();
16137     },
16138     
16139
16140     /**
16141      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16142      * @param {HTMLElement} node
16143      * @return {HTMLElement} The template node
16144      */
16145     findItemFromChild : function(node){
16146         var el = this.dataName  ?
16147             this.el.child('.roo-tpl-' + this.dataName,true) :
16148             this.el.dom; 
16149         
16150         if(!node || node.parentNode == el){
16151                     return node;
16152             }
16153             var p = node.parentNode;
16154             while(p && p != el){
16155             if(p.parentNode == el){
16156                 return p;
16157             }
16158             p = p.parentNode;
16159         }
16160             return null;
16161     },
16162
16163     /** @ignore */
16164     onClick : function(e){
16165         var item = this.findItemFromChild(e.getTarget());
16166         if(item){
16167             var index = this.indexOf(item);
16168             if(this.onItemClick(item, index, e) !== false){
16169                 this.fireEvent("click", this, index, item, e);
16170             }
16171         }else{
16172             this.clearSelections();
16173         }
16174     },
16175
16176     /** @ignore */
16177     onContextMenu : function(e){
16178         var item = this.findItemFromChild(e.getTarget());
16179         if(item){
16180             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16181         }
16182     },
16183
16184     /** @ignore */
16185     onDblClick : function(e){
16186         var item = this.findItemFromChild(e.getTarget());
16187         if(item){
16188             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16189         }
16190     },
16191
16192     onItemClick : function(item, index, e)
16193     {
16194         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16195             return false;
16196         }
16197         if (this.toggleSelect) {
16198             var m = this.isSelected(item) ? 'unselect' : 'select';
16199             //Roo.log(m);
16200             var _t = this;
16201             _t[m](item, true, false);
16202             return true;
16203         }
16204         if(this.multiSelect || this.singleSelect){
16205             if(this.multiSelect && e.shiftKey && this.lastSelection){
16206                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16207             }else{
16208                 this.select(item, this.multiSelect && e.ctrlKey);
16209                 this.lastSelection = item;
16210             }
16211             
16212             if(!this.tickable){
16213                 e.preventDefault();
16214             }
16215             
16216         }
16217         return true;
16218     },
16219
16220     /**
16221      * Get the number of selected nodes.
16222      * @return {Number}
16223      */
16224     getSelectionCount : function(){
16225         return this.selections.length;
16226     },
16227
16228     /**
16229      * Get the currently selected nodes.
16230      * @return {Array} An array of HTMLElements
16231      */
16232     getSelectedNodes : function(){
16233         return this.selections;
16234     },
16235
16236     /**
16237      * Get the indexes of the selected nodes.
16238      * @return {Array}
16239      */
16240     getSelectedIndexes : function(){
16241         var indexes = [], s = this.selections;
16242         for(var i = 0, len = s.length; i < len; i++){
16243             indexes.push(s[i].nodeIndex);
16244         }
16245         return indexes;
16246     },
16247
16248     /**
16249      * Clear all selections
16250      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16251      */
16252     clearSelections : function(suppressEvent){
16253         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16254             this.cmp.elements = this.selections;
16255             this.cmp.removeClass(this.selectedClass);
16256             this.selections = [];
16257             if(!suppressEvent){
16258                 this.fireEvent("selectionchange", this, this.selections);
16259             }
16260         }
16261     },
16262
16263     /**
16264      * Returns true if the passed node is selected
16265      * @param {HTMLElement/Number} node The node or node index
16266      * @return {Boolean}
16267      */
16268     isSelected : function(node){
16269         var s = this.selections;
16270         if(s.length < 1){
16271             return false;
16272         }
16273         node = this.getNode(node);
16274         return s.indexOf(node) !== -1;
16275     },
16276
16277     /**
16278      * Selects nodes.
16279      * @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
16280      * @param {Boolean} keepExisting (optional) true to keep existing selections
16281      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16282      */
16283     select : function(nodeInfo, keepExisting, suppressEvent){
16284         if(nodeInfo instanceof Array){
16285             if(!keepExisting){
16286                 this.clearSelections(true);
16287             }
16288             for(var i = 0, len = nodeInfo.length; i < len; i++){
16289                 this.select(nodeInfo[i], true, true);
16290             }
16291             return;
16292         } 
16293         var node = this.getNode(nodeInfo);
16294         if(!node || this.isSelected(node)){
16295             return; // already selected.
16296         }
16297         if(!keepExisting){
16298             this.clearSelections(true);
16299         }
16300         
16301         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16302             Roo.fly(node).addClass(this.selectedClass);
16303             this.selections.push(node);
16304             if(!suppressEvent){
16305                 this.fireEvent("selectionchange", this, this.selections);
16306             }
16307         }
16308         
16309         
16310     },
16311       /**
16312      * Unselects nodes.
16313      * @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
16314      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16315      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16316      */
16317     unselect : function(nodeInfo, keepExisting, suppressEvent)
16318     {
16319         if(nodeInfo instanceof Array){
16320             Roo.each(this.selections, function(s) {
16321                 this.unselect(s, nodeInfo);
16322             }, this);
16323             return;
16324         }
16325         var node = this.getNode(nodeInfo);
16326         if(!node || !this.isSelected(node)){
16327             //Roo.log("not selected");
16328             return; // not selected.
16329         }
16330         // fireevent???
16331         var ns = [];
16332         Roo.each(this.selections, function(s) {
16333             if (s == node ) {
16334                 Roo.fly(node).removeClass(this.selectedClass);
16335
16336                 return;
16337             }
16338             ns.push(s);
16339         },this);
16340         
16341         this.selections= ns;
16342         this.fireEvent("selectionchange", this, this.selections);
16343     },
16344
16345     /**
16346      * Gets a template node.
16347      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16348      * @return {HTMLElement} The node or null if it wasn't found
16349      */
16350     getNode : function(nodeInfo){
16351         if(typeof nodeInfo == "string"){
16352             return document.getElementById(nodeInfo);
16353         }else if(typeof nodeInfo == "number"){
16354             return this.nodes[nodeInfo];
16355         }
16356         return nodeInfo;
16357     },
16358
16359     /**
16360      * Gets a range template nodes.
16361      * @param {Number} startIndex
16362      * @param {Number} endIndex
16363      * @return {Array} An array of nodes
16364      */
16365     getNodes : function(start, end){
16366         var ns = this.nodes;
16367         start = start || 0;
16368         end = typeof end == "undefined" ? ns.length - 1 : end;
16369         var nodes = [];
16370         if(start <= end){
16371             for(var i = start; i <= end; i++){
16372                 nodes.push(ns[i]);
16373             }
16374         } else{
16375             for(var i = start; i >= end; i--){
16376                 nodes.push(ns[i]);
16377             }
16378         }
16379         return nodes;
16380     },
16381
16382     /**
16383      * Finds the index of the passed node
16384      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16385      * @return {Number} The index of the node or -1
16386      */
16387     indexOf : function(node){
16388         node = this.getNode(node);
16389         if(typeof node.nodeIndex == "number"){
16390             return node.nodeIndex;
16391         }
16392         var ns = this.nodes;
16393         for(var i = 0, len = ns.length; i < len; i++){
16394             if(ns[i] == node){
16395                 return i;
16396             }
16397         }
16398         return -1;
16399     }
16400 });
16401 /*
16402  * - LGPL
16403  *
16404  * based on jquery fullcalendar
16405  * 
16406  */
16407
16408 Roo.bootstrap = Roo.bootstrap || {};
16409 /**
16410  * @class Roo.bootstrap.Calendar
16411  * @extends Roo.bootstrap.Component
16412  * Bootstrap Calendar class
16413  * @cfg {Boolean} loadMask (true|false) default false
16414  * @cfg {Object} header generate the user specific header of the calendar, default false
16415
16416  * @constructor
16417  * Create a new Container
16418  * @param {Object} config The config object
16419  */
16420
16421
16422
16423 Roo.bootstrap.Calendar = function(config){
16424     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16425      this.addEvents({
16426         /**
16427              * @event select
16428              * Fires when a date is selected
16429              * @param {DatePicker} this
16430              * @param {Date} date The selected date
16431              */
16432         'select': true,
16433         /**
16434              * @event monthchange
16435              * Fires when the displayed month changes 
16436              * @param {DatePicker} this
16437              * @param {Date} date The selected month
16438              */
16439         'monthchange': true,
16440         /**
16441              * @event evententer
16442              * Fires when mouse over an event
16443              * @param {Calendar} this
16444              * @param {event} Event
16445              */
16446         'evententer': true,
16447         /**
16448              * @event eventleave
16449              * Fires when the mouse leaves an
16450              * @param {Calendar} this
16451              * @param {event}
16452              */
16453         'eventleave': true,
16454         /**
16455              * @event eventclick
16456              * Fires when the mouse click an
16457              * @param {Calendar} this
16458              * @param {event}
16459              */
16460         'eventclick': true
16461         
16462     });
16463
16464 };
16465
16466 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16467     
16468      /**
16469      * @cfg {Number} startDay
16470      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16471      */
16472     startDay : 0,
16473     
16474     loadMask : false,
16475     
16476     header : false,
16477       
16478     getAutoCreate : function(){
16479         
16480         
16481         var fc_button = function(name, corner, style, content ) {
16482             return Roo.apply({},{
16483                 tag : 'span',
16484                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16485                          (corner.length ?
16486                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16487                             ''
16488                         ),
16489                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16490                 unselectable: 'on'
16491             });
16492         };
16493         
16494         var header = {};
16495         
16496         if(!this.header){
16497             header = {
16498                 tag : 'table',
16499                 cls : 'fc-header',
16500                 style : 'width:100%',
16501                 cn : [
16502                     {
16503                         tag: 'tr',
16504                         cn : [
16505                             {
16506                                 tag : 'td',
16507                                 cls : 'fc-header-left',
16508                                 cn : [
16509                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16510                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16511                                     { tag: 'span', cls: 'fc-header-space' },
16512                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16513
16514
16515                                 ]
16516                             },
16517
16518                             {
16519                                 tag : 'td',
16520                                 cls : 'fc-header-center',
16521                                 cn : [
16522                                     {
16523                                         tag: 'span',
16524                                         cls: 'fc-header-title',
16525                                         cn : {
16526                                             tag: 'H2',
16527                                             html : 'month / year'
16528                                         }
16529                                     }
16530
16531                                 ]
16532                             },
16533                             {
16534                                 tag : 'td',
16535                                 cls : 'fc-header-right',
16536                                 cn : [
16537                               /*      fc_button('month', 'left', '', 'month' ),
16538                                     fc_button('week', '', '', 'week' ),
16539                                     fc_button('day', 'right', '', 'day' )
16540                                 */    
16541
16542                                 ]
16543                             }
16544
16545                         ]
16546                     }
16547                 ]
16548             };
16549         }
16550         
16551         header = this.header;
16552         
16553        
16554         var cal_heads = function() {
16555             var ret = [];
16556             // fixme - handle this.
16557             
16558             for (var i =0; i < Date.dayNames.length; i++) {
16559                 var d = Date.dayNames[i];
16560                 ret.push({
16561                     tag: 'th',
16562                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16563                     html : d.substring(0,3)
16564                 });
16565                 
16566             }
16567             ret[0].cls += ' fc-first';
16568             ret[6].cls += ' fc-last';
16569             return ret;
16570         };
16571         var cal_cell = function(n) {
16572             return  {
16573                 tag: 'td',
16574                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16575                 cn : [
16576                     {
16577                         cn : [
16578                             {
16579                                 cls: 'fc-day-number',
16580                                 html: 'D'
16581                             },
16582                             {
16583                                 cls: 'fc-day-content',
16584                              
16585                                 cn : [
16586                                      {
16587                                         style: 'position: relative;' // height: 17px;
16588                                     }
16589                                 ]
16590                             }
16591                             
16592                             
16593                         ]
16594                     }
16595                 ]
16596                 
16597             }
16598         };
16599         var cal_rows = function() {
16600             
16601             var ret = [];
16602             for (var r = 0; r < 6; r++) {
16603                 var row= {
16604                     tag : 'tr',
16605                     cls : 'fc-week',
16606                     cn : []
16607                 };
16608                 
16609                 for (var i =0; i < Date.dayNames.length; i++) {
16610                     var d = Date.dayNames[i];
16611                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16612
16613                 }
16614                 row.cn[0].cls+=' fc-first';
16615                 row.cn[0].cn[0].style = 'min-height:90px';
16616                 row.cn[6].cls+=' fc-last';
16617                 ret.push(row);
16618                 
16619             }
16620             ret[0].cls += ' fc-first';
16621             ret[4].cls += ' fc-prev-last';
16622             ret[5].cls += ' fc-last';
16623             return ret;
16624             
16625         };
16626         
16627         var cal_table = {
16628             tag: 'table',
16629             cls: 'fc-border-separate',
16630             style : 'width:100%',
16631             cellspacing  : 0,
16632             cn : [
16633                 { 
16634                     tag: 'thead',
16635                     cn : [
16636                         { 
16637                             tag: 'tr',
16638                             cls : 'fc-first fc-last',
16639                             cn : cal_heads()
16640                         }
16641                     ]
16642                 },
16643                 { 
16644                     tag: 'tbody',
16645                     cn : cal_rows()
16646                 }
16647                   
16648             ]
16649         };
16650          
16651          var cfg = {
16652             cls : 'fc fc-ltr',
16653             cn : [
16654                 header,
16655                 {
16656                     cls : 'fc-content',
16657                     style : "position: relative;",
16658                     cn : [
16659                         {
16660                             cls : 'fc-view fc-view-month fc-grid',
16661                             style : 'position: relative',
16662                             unselectable : 'on',
16663                             cn : [
16664                                 {
16665                                     cls : 'fc-event-container',
16666                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16667                                 },
16668                                 cal_table
16669                             ]
16670                         }
16671                     ]
16672     
16673                 }
16674            ] 
16675             
16676         };
16677         
16678          
16679         
16680         return cfg;
16681     },
16682     
16683     
16684     initEvents : function()
16685     {
16686         if(!this.store){
16687             throw "can not find store for calendar";
16688         }
16689         
16690         var mark = {
16691             tag: "div",
16692             cls:"x-dlg-mask",
16693             style: "text-align:center",
16694             cn: [
16695                 {
16696                     tag: "div",
16697                     style: "background-color:white;width:50%;margin:250 auto",
16698                     cn: [
16699                         {
16700                             tag: "img",
16701                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16702                         },
16703                         {
16704                             tag: "span",
16705                             html: "Loading"
16706                         }
16707                         
16708                     ]
16709                 }
16710             ]
16711         };
16712         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16713         
16714         var size = this.el.select('.fc-content', true).first().getSize();
16715         this.maskEl.setSize(size.width, size.height);
16716         this.maskEl.enableDisplayMode("block");
16717         if(!this.loadMask){
16718             this.maskEl.hide();
16719         }
16720         
16721         this.store = Roo.factory(this.store, Roo.data);
16722         this.store.on('load', this.onLoad, this);
16723         this.store.on('beforeload', this.onBeforeLoad, this);
16724         
16725         this.resize();
16726         
16727         this.cells = this.el.select('.fc-day',true);
16728         //Roo.log(this.cells);
16729         this.textNodes = this.el.query('.fc-day-number');
16730         this.cells.addClassOnOver('fc-state-hover');
16731         
16732         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16733         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16734         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16735         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16736         
16737         this.on('monthchange', this.onMonthChange, this);
16738         
16739         this.update(new Date().clearTime());
16740     },
16741     
16742     resize : function() {
16743         var sz  = this.el.getSize();
16744         
16745         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16746         this.el.select('.fc-day-content div',true).setHeight(34);
16747     },
16748     
16749     
16750     // private
16751     showPrevMonth : function(e){
16752         this.update(this.activeDate.add("mo", -1));
16753     },
16754     showToday : function(e){
16755         this.update(new Date().clearTime());
16756     },
16757     // private
16758     showNextMonth : function(e){
16759         this.update(this.activeDate.add("mo", 1));
16760     },
16761
16762     // private
16763     showPrevYear : function(){
16764         this.update(this.activeDate.add("y", -1));
16765     },
16766
16767     // private
16768     showNextYear : function(){
16769         this.update(this.activeDate.add("y", 1));
16770     },
16771
16772     
16773    // private
16774     update : function(date)
16775     {
16776         var vd = this.activeDate;
16777         this.activeDate = date;
16778 //        if(vd && this.el){
16779 //            var t = date.getTime();
16780 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16781 //                Roo.log('using add remove');
16782 //                
16783 //                this.fireEvent('monthchange', this, date);
16784 //                
16785 //                this.cells.removeClass("fc-state-highlight");
16786 //                this.cells.each(function(c){
16787 //                   if(c.dateValue == t){
16788 //                       c.addClass("fc-state-highlight");
16789 //                       setTimeout(function(){
16790 //                            try{c.dom.firstChild.focus();}catch(e){}
16791 //                       }, 50);
16792 //                       return false;
16793 //                   }
16794 //                   return true;
16795 //                });
16796 //                return;
16797 //            }
16798 //        }
16799         
16800         var days = date.getDaysInMonth();
16801         
16802         var firstOfMonth = date.getFirstDateOfMonth();
16803         var startingPos = firstOfMonth.getDay()-this.startDay;
16804         
16805         if(startingPos < this.startDay){
16806             startingPos += 7;
16807         }
16808         
16809         var pm = date.add(Date.MONTH, -1);
16810         var prevStart = pm.getDaysInMonth()-startingPos;
16811 //        
16812         this.cells = this.el.select('.fc-day',true);
16813         this.textNodes = this.el.query('.fc-day-number');
16814         this.cells.addClassOnOver('fc-state-hover');
16815         
16816         var cells = this.cells.elements;
16817         var textEls = this.textNodes;
16818         
16819         Roo.each(cells, function(cell){
16820             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16821         });
16822         
16823         days += startingPos;
16824
16825         // convert everything to numbers so it's fast
16826         var day = 86400000;
16827         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16828         //Roo.log(d);
16829         //Roo.log(pm);
16830         //Roo.log(prevStart);
16831         
16832         var today = new Date().clearTime().getTime();
16833         var sel = date.clearTime().getTime();
16834         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16835         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16836         var ddMatch = this.disabledDatesRE;
16837         var ddText = this.disabledDatesText;
16838         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16839         var ddaysText = this.disabledDaysText;
16840         var format = this.format;
16841         
16842         var setCellClass = function(cal, cell){
16843             cell.row = 0;
16844             cell.events = [];
16845             cell.more = [];
16846             //Roo.log('set Cell Class');
16847             cell.title = "";
16848             var t = d.getTime();
16849             
16850             //Roo.log(d);
16851             
16852             cell.dateValue = t;
16853             if(t == today){
16854                 cell.className += " fc-today";
16855                 cell.className += " fc-state-highlight";
16856                 cell.title = cal.todayText;
16857             }
16858             if(t == sel){
16859                 // disable highlight in other month..
16860                 //cell.className += " fc-state-highlight";
16861                 
16862             }
16863             // disabling
16864             if(t < min) {
16865                 cell.className = " fc-state-disabled";
16866                 cell.title = cal.minText;
16867                 return;
16868             }
16869             if(t > max) {
16870                 cell.className = " fc-state-disabled";
16871                 cell.title = cal.maxText;
16872                 return;
16873             }
16874             if(ddays){
16875                 if(ddays.indexOf(d.getDay()) != -1){
16876                     cell.title = ddaysText;
16877                     cell.className = " fc-state-disabled";
16878                 }
16879             }
16880             if(ddMatch && format){
16881                 var fvalue = d.dateFormat(format);
16882                 if(ddMatch.test(fvalue)){
16883                     cell.title = ddText.replace("%0", fvalue);
16884                     cell.className = " fc-state-disabled";
16885                 }
16886             }
16887             
16888             if (!cell.initialClassName) {
16889                 cell.initialClassName = cell.dom.className;
16890             }
16891             
16892             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16893         };
16894
16895         var i = 0;
16896         
16897         for(; i < startingPos; i++) {
16898             textEls[i].innerHTML = (++prevStart);
16899             d.setDate(d.getDate()+1);
16900             
16901             cells[i].className = "fc-past fc-other-month";
16902             setCellClass(this, cells[i]);
16903         }
16904         
16905         var intDay = 0;
16906         
16907         for(; i < days; i++){
16908             intDay = i - startingPos + 1;
16909             textEls[i].innerHTML = (intDay);
16910             d.setDate(d.getDate()+1);
16911             
16912             cells[i].className = ''; // "x-date-active";
16913             setCellClass(this, cells[i]);
16914         }
16915         var extraDays = 0;
16916         
16917         for(; i < 42; i++) {
16918             textEls[i].innerHTML = (++extraDays);
16919             d.setDate(d.getDate()+1);
16920             
16921             cells[i].className = "fc-future fc-other-month";
16922             setCellClass(this, cells[i]);
16923         }
16924         
16925         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16926         
16927         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16928         
16929         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16930         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16931         
16932         if(totalRows != 6){
16933             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16934             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16935         }
16936         
16937         this.fireEvent('monthchange', this, date);
16938         
16939         
16940         /*
16941         if(!this.internalRender){
16942             var main = this.el.dom.firstChild;
16943             var w = main.offsetWidth;
16944             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16945             Roo.fly(main).setWidth(w);
16946             this.internalRender = true;
16947             // opera does not respect the auto grow header center column
16948             // then, after it gets a width opera refuses to recalculate
16949             // without a second pass
16950             if(Roo.isOpera && !this.secondPass){
16951                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16952                 this.secondPass = true;
16953                 this.update.defer(10, this, [date]);
16954             }
16955         }
16956         */
16957         
16958     },
16959     
16960     findCell : function(dt) {
16961         dt = dt.clearTime().getTime();
16962         var ret = false;
16963         this.cells.each(function(c){
16964             //Roo.log("check " +c.dateValue + '?=' + dt);
16965             if(c.dateValue == dt){
16966                 ret = c;
16967                 return false;
16968             }
16969             return true;
16970         });
16971         
16972         return ret;
16973     },
16974     
16975     findCells : function(ev) {
16976         var s = ev.start.clone().clearTime().getTime();
16977        // Roo.log(s);
16978         var e= ev.end.clone().clearTime().getTime();
16979        // Roo.log(e);
16980         var ret = [];
16981         this.cells.each(function(c){
16982              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16983             
16984             if(c.dateValue > e){
16985                 return ;
16986             }
16987             if(c.dateValue < s){
16988                 return ;
16989             }
16990             ret.push(c);
16991         });
16992         
16993         return ret;    
16994     },
16995     
16996 //    findBestRow: function(cells)
16997 //    {
16998 //        var ret = 0;
16999 //        
17000 //        for (var i =0 ; i < cells.length;i++) {
17001 //            ret  = Math.max(cells[i].rows || 0,ret);
17002 //        }
17003 //        return ret;
17004 //        
17005 //    },
17006     
17007     
17008     addItem : function(ev)
17009     {
17010         // look for vertical location slot in
17011         var cells = this.findCells(ev);
17012         
17013 //        ev.row = this.findBestRow(cells);
17014         
17015         // work out the location.
17016         
17017         var crow = false;
17018         var rows = [];
17019         for(var i =0; i < cells.length; i++) {
17020             
17021             cells[i].row = cells[0].row;
17022             
17023             if(i == 0){
17024                 cells[i].row = cells[i].row + 1;
17025             }
17026             
17027             if (!crow) {
17028                 crow = {
17029                     start : cells[i],
17030                     end :  cells[i]
17031                 };
17032                 continue;
17033             }
17034             if (crow.start.getY() == cells[i].getY()) {
17035                 // on same row.
17036                 crow.end = cells[i];
17037                 continue;
17038             }
17039             // different row.
17040             rows.push(crow);
17041             crow = {
17042                 start: cells[i],
17043                 end : cells[i]
17044             };
17045             
17046         }
17047         
17048         rows.push(crow);
17049         ev.els = [];
17050         ev.rows = rows;
17051         ev.cells = cells;
17052         
17053         cells[0].events.push(ev);
17054         
17055         this.calevents.push(ev);
17056     },
17057     
17058     clearEvents: function() {
17059         
17060         if(!this.calevents){
17061             return;
17062         }
17063         
17064         Roo.each(this.cells.elements, function(c){
17065             c.row = 0;
17066             c.events = [];
17067             c.more = [];
17068         });
17069         
17070         Roo.each(this.calevents, function(e) {
17071             Roo.each(e.els, function(el) {
17072                 el.un('mouseenter' ,this.onEventEnter, this);
17073                 el.un('mouseleave' ,this.onEventLeave, this);
17074                 el.remove();
17075             },this);
17076         },this);
17077         
17078         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17079             e.remove();
17080         });
17081         
17082     },
17083     
17084     renderEvents: function()
17085     {   
17086         var _this = this;
17087         
17088         this.cells.each(function(c) {
17089             
17090             if(c.row < 5){
17091                 return;
17092             }
17093             
17094             var ev = c.events;
17095             
17096             var r = 4;
17097             if(c.row != c.events.length){
17098                 r = 4 - (4 - (c.row - c.events.length));
17099             }
17100             
17101             c.events = ev.slice(0, r);
17102             c.more = ev.slice(r);
17103             
17104             if(c.more.length && c.more.length == 1){
17105                 c.events.push(c.more.pop());
17106             }
17107             
17108             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17109             
17110         });
17111             
17112         this.cells.each(function(c) {
17113             
17114             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17115             
17116             
17117             for (var e = 0; e < c.events.length; e++){
17118                 var ev = c.events[e];
17119                 var rows = ev.rows;
17120                 
17121                 for(var i = 0; i < rows.length; i++) {
17122                 
17123                     // how many rows should it span..
17124
17125                     var  cfg = {
17126                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17127                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17128
17129                         unselectable : "on",
17130                         cn : [
17131                             {
17132                                 cls: 'fc-event-inner',
17133                                 cn : [
17134     //                                {
17135     //                                  tag:'span',
17136     //                                  cls: 'fc-event-time',
17137     //                                  html : cells.length > 1 ? '' : ev.time
17138     //                                },
17139                                     {
17140                                       tag:'span',
17141                                       cls: 'fc-event-title',
17142                                       html : String.format('{0}', ev.title)
17143                                     }
17144
17145
17146                                 ]
17147                             },
17148                             {
17149                                 cls: 'ui-resizable-handle ui-resizable-e',
17150                                 html : '&nbsp;&nbsp;&nbsp'
17151                             }
17152
17153                         ]
17154                     };
17155
17156                     if (i == 0) {
17157                         cfg.cls += ' fc-event-start';
17158                     }
17159                     if ((i+1) == rows.length) {
17160                         cfg.cls += ' fc-event-end';
17161                     }
17162
17163                     var ctr = _this.el.select('.fc-event-container',true).first();
17164                     var cg = ctr.createChild(cfg);
17165
17166                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17167                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17168
17169                     var r = (c.more.length) ? 1 : 0;
17170                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17171                     cg.setWidth(ebox.right - sbox.x -2);
17172
17173                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17174                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17175                     cg.on('click', _this.onEventClick, _this, ev);
17176
17177                     ev.els.push(cg);
17178                     
17179                 }
17180                 
17181             }
17182             
17183             
17184             if(c.more.length){
17185                 var  cfg = {
17186                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17187                     style : 'position: absolute',
17188                     unselectable : "on",
17189                     cn : [
17190                         {
17191                             cls: 'fc-event-inner',
17192                             cn : [
17193                                 {
17194                                   tag:'span',
17195                                   cls: 'fc-event-title',
17196                                   html : 'More'
17197                                 }
17198
17199
17200                             ]
17201                         },
17202                         {
17203                             cls: 'ui-resizable-handle ui-resizable-e',
17204                             html : '&nbsp;&nbsp;&nbsp'
17205                         }
17206
17207                     ]
17208                 };
17209
17210                 var ctr = _this.el.select('.fc-event-container',true).first();
17211                 var cg = ctr.createChild(cfg);
17212
17213                 var sbox = c.select('.fc-day-content',true).first().getBox();
17214                 var ebox = c.select('.fc-day-content',true).first().getBox();
17215                 //Roo.log(cg);
17216                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17217                 cg.setWidth(ebox.right - sbox.x -2);
17218
17219                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17220                 
17221             }
17222             
17223         });
17224         
17225         
17226         
17227     },
17228     
17229     onEventEnter: function (e, el,event,d) {
17230         this.fireEvent('evententer', this, el, event);
17231     },
17232     
17233     onEventLeave: function (e, el,event,d) {
17234         this.fireEvent('eventleave', this, el, event);
17235     },
17236     
17237     onEventClick: function (e, el,event,d) {
17238         this.fireEvent('eventclick', this, el, event);
17239     },
17240     
17241     onMonthChange: function () {
17242         this.store.load();
17243     },
17244     
17245     onMoreEventClick: function(e, el, more)
17246     {
17247         var _this = this;
17248         
17249         this.calpopover.placement = 'right';
17250         this.calpopover.setTitle('More');
17251         
17252         this.calpopover.setContent('');
17253         
17254         var ctr = this.calpopover.el.select('.popover-content', true).first();
17255         
17256         Roo.each(more, function(m){
17257             var cfg = {
17258                 cls : 'fc-event-hori fc-event-draggable',
17259                 html : m.title
17260             };
17261             var cg = ctr.createChild(cfg);
17262             
17263             cg.on('click', _this.onEventClick, _this, m);
17264         });
17265         
17266         this.calpopover.show(el);
17267         
17268         
17269     },
17270     
17271     onLoad: function () 
17272     {   
17273         this.calevents = [];
17274         var cal = this;
17275         
17276         if(this.store.getCount() > 0){
17277             this.store.data.each(function(d){
17278                cal.addItem({
17279                     id : d.data.id,
17280                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17281                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17282                     time : d.data.start_time,
17283                     title : d.data.title,
17284                     description : d.data.description,
17285                     venue : d.data.venue
17286                 });
17287             });
17288         }
17289         
17290         this.renderEvents();
17291         
17292         if(this.calevents.length && this.loadMask){
17293             this.maskEl.hide();
17294         }
17295     },
17296     
17297     onBeforeLoad: function()
17298     {
17299         this.clearEvents();
17300         if(this.loadMask){
17301             this.maskEl.show();
17302         }
17303     }
17304 });
17305
17306  
17307  /*
17308  * - LGPL
17309  *
17310  * element
17311  * 
17312  */
17313
17314 /**
17315  * @class Roo.bootstrap.Popover
17316  * @extends Roo.bootstrap.Component
17317  * Bootstrap Popover class
17318  * @cfg {String} html contents of the popover   (or false to use children..)
17319  * @cfg {String} title of popover (or false to hide)
17320  * @cfg {String} placement how it is placed
17321  * @cfg {String} trigger click || hover (or false to trigger manually)
17322  * @cfg {String} over what (parent or false to trigger manually.)
17323  * @cfg {Number} delay - delay before showing
17324  
17325  * @constructor
17326  * Create a new Popover
17327  * @param {Object} config The config object
17328  */
17329
17330 Roo.bootstrap.Popover = function(config){
17331     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17332     
17333     this.addEvents({
17334         // raw events
17335          /**
17336          * @event show
17337          * After the popover show
17338          * 
17339          * @param {Roo.bootstrap.Popover} this
17340          */
17341         "show" : true,
17342         /**
17343          * @event hide
17344          * After the popover hide
17345          * 
17346          * @param {Roo.bootstrap.Popover} this
17347          */
17348         "hide" : true
17349     });
17350 };
17351
17352 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17353     
17354     title: 'Fill in a title',
17355     html: false,
17356     
17357     placement : 'right',
17358     trigger : 'hover', // hover
17359     
17360     delay : 0,
17361     
17362     over: 'parent',
17363     
17364     can_build_overlaid : false,
17365     
17366     getChildContainer : function()
17367     {
17368         return this.el.select('.popover-content',true).first();
17369     },
17370     
17371     getAutoCreate : function(){
17372          
17373         var cfg = {
17374            cls : 'popover roo-dynamic',
17375            style: 'display:block',
17376            cn : [
17377                 {
17378                     cls : 'arrow'
17379                 },
17380                 {
17381                     cls : 'popover-inner',
17382                     cn : [
17383                         {
17384                             tag: 'h3',
17385                             cls: 'popover-title',
17386                             html : this.title
17387                         },
17388                         {
17389                             cls : 'popover-content',
17390                             html : this.html
17391                         }
17392                     ]
17393                     
17394                 }
17395            ]
17396         };
17397         
17398         return cfg;
17399     },
17400     setTitle: function(str)
17401     {
17402         this.title = str;
17403         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17404     },
17405     setContent: function(str)
17406     {
17407         this.html = str;
17408         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17409     },
17410     // as it get's added to the bottom of the page.
17411     onRender : function(ct, position)
17412     {
17413         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17414         if(!this.el){
17415             var cfg = Roo.apply({},  this.getAutoCreate());
17416             cfg.id = Roo.id();
17417             
17418             if (this.cls) {
17419                 cfg.cls += ' ' + this.cls;
17420             }
17421             if (this.style) {
17422                 cfg.style = this.style;
17423             }
17424             //Roo.log("adding to ");
17425             this.el = Roo.get(document.body).createChild(cfg, position);
17426 //            Roo.log(this.el);
17427         }
17428         this.initEvents();
17429     },
17430     
17431     initEvents : function()
17432     {
17433         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17434         this.el.enableDisplayMode('block');
17435         this.el.hide();
17436         if (this.over === false) {
17437             return; 
17438         }
17439         if (this.triggers === false) {
17440             return;
17441         }
17442         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17443         var triggers = this.trigger ? this.trigger.split(' ') : [];
17444         Roo.each(triggers, function(trigger) {
17445         
17446             if (trigger == 'click') {
17447                 on_el.on('click', this.toggle, this);
17448             } else if (trigger != 'manual') {
17449                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17450                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17451       
17452                 on_el.on(eventIn  ,this.enter, this);
17453                 on_el.on(eventOut, this.leave, this);
17454             }
17455         }, this);
17456         
17457     },
17458     
17459     
17460     // private
17461     timeout : null,
17462     hoverState : null,
17463     
17464     toggle : function () {
17465         this.hoverState == 'in' ? this.leave() : this.enter();
17466     },
17467     
17468     enter : function () {
17469         
17470         clearTimeout(this.timeout);
17471     
17472         this.hoverState = 'in';
17473     
17474         if (!this.delay || !this.delay.show) {
17475             this.show();
17476             return;
17477         }
17478         var _t = this;
17479         this.timeout = setTimeout(function () {
17480             if (_t.hoverState == 'in') {
17481                 _t.show();
17482             }
17483         }, this.delay.show)
17484     },
17485     
17486     leave : function() {
17487         clearTimeout(this.timeout);
17488     
17489         this.hoverState = 'out';
17490     
17491         if (!this.delay || !this.delay.hide) {
17492             this.hide();
17493             return;
17494         }
17495         var _t = this;
17496         this.timeout = setTimeout(function () {
17497             if (_t.hoverState == 'out') {
17498                 _t.hide();
17499             }
17500         }, this.delay.hide)
17501     },
17502     
17503     show : function (on_el)
17504     {
17505         if (!on_el) {
17506             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17507         }
17508         
17509         // set content.
17510         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17511         if (this.html !== false) {
17512             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17513         }
17514         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17515         if (!this.title.length) {
17516             this.el.select('.popover-title',true).hide();
17517         }
17518         
17519         var placement = typeof this.placement == 'function' ?
17520             this.placement.call(this, this.el, on_el) :
17521             this.placement;
17522             
17523         var autoToken = /\s?auto?\s?/i;
17524         var autoPlace = autoToken.test(placement);
17525         if (autoPlace) {
17526             placement = placement.replace(autoToken, '') || 'top';
17527         }
17528         
17529         //this.el.detach()
17530         //this.el.setXY([0,0]);
17531         this.el.show();
17532         this.el.dom.style.display='block';
17533         this.el.addClass(placement);
17534         
17535         //this.el.appendTo(on_el);
17536         
17537         var p = this.getPosition();
17538         var box = this.el.getBox();
17539         
17540         if (autoPlace) {
17541             // fixme..
17542         }
17543         var align = Roo.bootstrap.Popover.alignment[placement];
17544         
17545 //        Roo.log(align);
17546         this.el.alignTo(on_el, align[0],align[1]);
17547         //var arrow = this.el.select('.arrow',true).first();
17548         //arrow.set(align[2], 
17549         
17550         this.el.addClass('in');
17551         
17552         
17553         if (this.el.hasClass('fade')) {
17554             // fade it?
17555         }
17556         
17557         this.hoverState = 'in';
17558         
17559         this.fireEvent('show', this);
17560         
17561     },
17562     hide : function()
17563     {
17564         this.el.setXY([0,0]);
17565         this.el.removeClass('in');
17566         this.el.hide();
17567         this.hoverState = null;
17568         
17569         this.fireEvent('hide', this);
17570     }
17571     
17572 });
17573
17574 Roo.bootstrap.Popover.alignment = {
17575     'left' : ['r-l', [-10,0], 'right'],
17576     'right' : ['l-r', [10,0], 'left'],
17577     'bottom' : ['t-b', [0,10], 'top'],
17578     'top' : [ 'b-t', [0,-10], 'bottom']
17579 };
17580
17581  /*
17582  * - LGPL
17583  *
17584  * Progress
17585  * 
17586  */
17587
17588 /**
17589  * @class Roo.bootstrap.Progress
17590  * @extends Roo.bootstrap.Component
17591  * Bootstrap Progress class
17592  * @cfg {Boolean} striped striped of the progress bar
17593  * @cfg {Boolean} active animated of the progress bar
17594  * 
17595  * 
17596  * @constructor
17597  * Create a new Progress
17598  * @param {Object} config The config object
17599  */
17600
17601 Roo.bootstrap.Progress = function(config){
17602     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17603 };
17604
17605 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17606     
17607     striped : false,
17608     active: false,
17609     
17610     getAutoCreate : function(){
17611         var cfg = {
17612             tag: 'div',
17613             cls: 'progress'
17614         };
17615         
17616         
17617         if(this.striped){
17618             cfg.cls += ' progress-striped';
17619         }
17620       
17621         if(this.active){
17622             cfg.cls += ' active';
17623         }
17624         
17625         
17626         return cfg;
17627     }
17628    
17629 });
17630
17631  
17632
17633  /*
17634  * - LGPL
17635  *
17636  * ProgressBar
17637  * 
17638  */
17639
17640 /**
17641  * @class Roo.bootstrap.ProgressBar
17642  * @extends Roo.bootstrap.Component
17643  * Bootstrap ProgressBar class
17644  * @cfg {Number} aria_valuenow aria-value now
17645  * @cfg {Number} aria_valuemin aria-value min
17646  * @cfg {Number} aria_valuemax aria-value max
17647  * @cfg {String} label label for the progress bar
17648  * @cfg {String} panel (success | info | warning | danger )
17649  * @cfg {String} role role of the progress bar
17650  * @cfg {String} sr_only text
17651  * 
17652  * 
17653  * @constructor
17654  * Create a new ProgressBar
17655  * @param {Object} config The config object
17656  */
17657
17658 Roo.bootstrap.ProgressBar = function(config){
17659     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17660 };
17661
17662 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17663     
17664     aria_valuenow : 0,
17665     aria_valuemin : 0,
17666     aria_valuemax : 100,
17667     label : false,
17668     panel : false,
17669     role : false,
17670     sr_only: false,
17671     
17672     getAutoCreate : function()
17673     {
17674         
17675         var cfg = {
17676             tag: 'div',
17677             cls: 'progress-bar',
17678             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17679         };
17680         
17681         if(this.sr_only){
17682             cfg.cn = {
17683                 tag: 'span',
17684                 cls: 'sr-only',
17685                 html: this.sr_only
17686             }
17687         }
17688         
17689         if(this.role){
17690             cfg.role = this.role;
17691         }
17692         
17693         if(this.aria_valuenow){
17694             cfg['aria-valuenow'] = this.aria_valuenow;
17695         }
17696         
17697         if(this.aria_valuemin){
17698             cfg['aria-valuemin'] = this.aria_valuemin;
17699         }
17700         
17701         if(this.aria_valuemax){
17702             cfg['aria-valuemax'] = this.aria_valuemax;
17703         }
17704         
17705         if(this.label && !this.sr_only){
17706             cfg.html = this.label;
17707         }
17708         
17709         if(this.panel){
17710             cfg.cls += ' progress-bar-' + this.panel;
17711         }
17712         
17713         return cfg;
17714     },
17715     
17716     update : function(aria_valuenow)
17717     {
17718         this.aria_valuenow = aria_valuenow;
17719         
17720         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17721     }
17722    
17723 });
17724
17725  
17726
17727  /*
17728  * - LGPL
17729  *
17730  * column
17731  * 
17732  */
17733
17734 /**
17735  * @class Roo.bootstrap.TabGroup
17736  * @extends Roo.bootstrap.Column
17737  * Bootstrap Column class
17738  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17739  * @cfg {Boolean} carousel true to make the group behave like a carousel
17740  * @cfg {Boolean} bullets show bullets for the panels
17741  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17742  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17743  * @cfg {Boolean} showarrow (true|false) show arrow default true
17744  * 
17745  * @constructor
17746  * Create a new TabGroup
17747  * @param {Object} config The config object
17748  */
17749
17750 Roo.bootstrap.TabGroup = function(config){
17751     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17752     if (!this.navId) {
17753         this.navId = Roo.id();
17754     }
17755     this.tabs = [];
17756     Roo.bootstrap.TabGroup.register(this);
17757     
17758 };
17759
17760 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17761     
17762     carousel : false,
17763     transition : false,
17764     bullets : 0,
17765     timer : 0,
17766     autoslide : false,
17767     slideFn : false,
17768     slideOnTouch : false,
17769     showarrow : true,
17770     
17771     getAutoCreate : function()
17772     {
17773         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17774         
17775         cfg.cls += ' tab-content';
17776         
17777         if (this.carousel) {
17778             cfg.cls += ' carousel slide';
17779             
17780             cfg.cn = [{
17781                cls : 'carousel-inner',
17782                cn : []
17783             }];
17784         
17785             if(this.bullets  && !Roo.isTouch){
17786                 
17787                 var bullets = {
17788                     cls : 'carousel-bullets',
17789                     cn : []
17790                 };
17791                
17792                 if(this.bullets_cls){
17793                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17794                 }
17795                 
17796                 bullets.cn.push({
17797                     cls : 'clear'
17798                 });
17799                 
17800                 cfg.cn[0].cn.push(bullets);
17801             }
17802             
17803             if(this.showarrow){
17804                 cfg.cn[0].cn.push({
17805                     tag : 'div',
17806                     class : 'carousel-arrow',
17807                     cn : [
17808                         {
17809                             tag : 'div',
17810                             class : 'carousel-prev',
17811                             cn : [
17812                                 {
17813                                     tag : 'i',
17814                                     class : 'fa fa-chevron-left'
17815                                 }
17816                             ]
17817                         },
17818                         {
17819                             tag : 'div',
17820                             class : 'carousel-next',
17821                             cn : [
17822                                 {
17823                                     tag : 'i',
17824                                     class : 'fa fa-chevron-right'
17825                                 }
17826                             ]
17827                         }
17828                     ]
17829                 });
17830             }
17831             
17832         }
17833         
17834         return cfg;
17835     },
17836     
17837     initEvents:  function()
17838     {
17839 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17840 //            this.el.on("touchstart", this.onTouchStart, this);
17841 //        }
17842         
17843         if(this.autoslide){
17844             var _this = this;
17845             
17846             this.slideFn = window.setInterval(function() {
17847                 _this.showPanelNext();
17848             }, this.timer);
17849         }
17850         
17851         if(this.showarrow){
17852             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17853             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17854         }
17855         
17856         
17857     },
17858     
17859 //    onTouchStart : function(e, el, o)
17860 //    {
17861 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17862 //            return;
17863 //        }
17864 //        
17865 //        this.showPanelNext();
17866 //    },
17867     
17868     
17869     getChildContainer : function()
17870     {
17871         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17872     },
17873     
17874     /**
17875     * register a Navigation item
17876     * @param {Roo.bootstrap.NavItem} the navitem to add
17877     */
17878     register : function(item)
17879     {
17880         this.tabs.push( item);
17881         item.navId = this.navId; // not really needed..
17882         this.addBullet();
17883     
17884     },
17885     
17886     getActivePanel : function()
17887     {
17888         var r = false;
17889         Roo.each(this.tabs, function(t) {
17890             if (t.active) {
17891                 r = t;
17892                 return false;
17893             }
17894             return null;
17895         });
17896         return r;
17897         
17898     },
17899     getPanelByName : function(n)
17900     {
17901         var r = false;
17902         Roo.each(this.tabs, function(t) {
17903             if (t.tabId == n) {
17904                 r = t;
17905                 return false;
17906             }
17907             return null;
17908         });
17909         return r;
17910     },
17911     indexOfPanel : function(p)
17912     {
17913         var r = false;
17914         Roo.each(this.tabs, function(t,i) {
17915             if (t.tabId == p.tabId) {
17916                 r = i;
17917                 return false;
17918             }
17919             return null;
17920         });
17921         return r;
17922     },
17923     /**
17924      * show a specific panel
17925      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17926      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17927      */
17928     showPanel : function (pan)
17929     {
17930         if(this.transition || typeof(pan) == 'undefined'){
17931             Roo.log("waiting for the transitionend");
17932             return;
17933         }
17934         
17935         if (typeof(pan) == 'number') {
17936             pan = this.tabs[pan];
17937         }
17938         
17939         if (typeof(pan) == 'string') {
17940             pan = this.getPanelByName(pan);
17941         }
17942         
17943         var cur = this.getActivePanel();
17944         
17945         if(!pan || !cur){
17946             Roo.log('pan or acitve pan is undefined');
17947             return false;
17948         }
17949         
17950         if (pan.tabId == this.getActivePanel().tabId) {
17951             return true;
17952         }
17953         
17954         if (false === cur.fireEvent('beforedeactivate')) {
17955             return false;
17956         }
17957         
17958         if(this.bullets > 0 && !Roo.isTouch){
17959             this.setActiveBullet(this.indexOfPanel(pan));
17960         }
17961         
17962         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17963             
17964             this.transition = true;
17965             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17966             var lr = dir == 'next' ? 'left' : 'right';
17967             pan.el.addClass(dir); // or prev
17968             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17969             cur.el.addClass(lr); // or right
17970             pan.el.addClass(lr);
17971             
17972             var _this = this;
17973             cur.el.on('transitionend', function() {
17974                 Roo.log("trans end?");
17975                 
17976                 pan.el.removeClass([lr,dir]);
17977                 pan.setActive(true);
17978                 
17979                 cur.el.removeClass([lr]);
17980                 cur.setActive(false);
17981                 
17982                 _this.transition = false;
17983                 
17984             }, this, { single:  true } );
17985             
17986             return true;
17987         }
17988         
17989         cur.setActive(false);
17990         pan.setActive(true);
17991         
17992         return true;
17993         
17994     },
17995     showPanelNext : function()
17996     {
17997         var i = this.indexOfPanel(this.getActivePanel());
17998         
17999         if (i >= this.tabs.length - 1 && !this.autoslide) {
18000             return;
18001         }
18002         
18003         if (i >= this.tabs.length - 1 && this.autoslide) {
18004             i = -1;
18005         }
18006         
18007         this.showPanel(this.tabs[i+1]);
18008     },
18009     
18010     showPanelPrev : function()
18011     {
18012         var i = this.indexOfPanel(this.getActivePanel());
18013         
18014         if (i  < 1 && !this.autoslide) {
18015             return;
18016         }
18017         
18018         if (i < 1 && this.autoslide) {
18019             i = this.tabs.length;
18020         }
18021         
18022         this.showPanel(this.tabs[i-1]);
18023     },
18024     
18025     
18026     addBullet: function()
18027     {
18028         if(!this.bullets || Roo.isTouch){
18029             return;
18030         }
18031         var ctr = this.el.select('.carousel-bullets',true).first();
18032         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18033         var bullet = ctr.createChild({
18034             cls : 'bullet bullet-' + i
18035         },ctr.dom.lastChild);
18036         
18037         
18038         var _this = this;
18039         
18040         bullet.on('click', (function(e, el, o, ii, t){
18041
18042             e.preventDefault();
18043
18044             this.showPanel(ii);
18045
18046             if(this.autoslide && this.slideFn){
18047                 clearInterval(this.slideFn);
18048                 this.slideFn = window.setInterval(function() {
18049                     _this.showPanelNext();
18050                 }, this.timer);
18051             }
18052
18053         }).createDelegate(this, [i, bullet], true));
18054                 
18055         
18056     },
18057      
18058     setActiveBullet : function(i)
18059     {
18060         if(Roo.isTouch){
18061             return;
18062         }
18063         
18064         Roo.each(this.el.select('.bullet', true).elements, function(el){
18065             el.removeClass('selected');
18066         });
18067
18068         var bullet = this.el.select('.bullet-' + i, true).first();
18069         
18070         if(!bullet){
18071             return;
18072         }
18073         
18074         bullet.addClass('selected');
18075     }
18076     
18077     
18078   
18079 });
18080
18081  
18082
18083  
18084  
18085 Roo.apply(Roo.bootstrap.TabGroup, {
18086     
18087     groups: {},
18088      /**
18089     * register a Navigation Group
18090     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18091     */
18092     register : function(navgrp)
18093     {
18094         this.groups[navgrp.navId] = navgrp;
18095         
18096     },
18097     /**
18098     * fetch a Navigation Group based on the navigation ID
18099     * if one does not exist , it will get created.
18100     * @param {string} the navgroup to add
18101     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18102     */
18103     get: function(navId) {
18104         if (typeof(this.groups[navId]) == 'undefined') {
18105             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18106         }
18107         return this.groups[navId] ;
18108     }
18109     
18110     
18111     
18112 });
18113
18114  /*
18115  * - LGPL
18116  *
18117  * TabPanel
18118  * 
18119  */
18120
18121 /**
18122  * @class Roo.bootstrap.TabPanel
18123  * @extends Roo.bootstrap.Component
18124  * Bootstrap TabPanel class
18125  * @cfg {Boolean} active panel active
18126  * @cfg {String} html panel content
18127  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18128  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18129  * @cfg {String} href click to link..
18130  * 
18131  * 
18132  * @constructor
18133  * Create a new TabPanel
18134  * @param {Object} config The config object
18135  */
18136
18137 Roo.bootstrap.TabPanel = function(config){
18138     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18139     this.addEvents({
18140         /**
18141              * @event changed
18142              * Fires when the active status changes
18143              * @param {Roo.bootstrap.TabPanel} this
18144              * @param {Boolean} state the new state
18145             
18146          */
18147         'changed': true,
18148         /**
18149              * @event beforedeactivate
18150              * Fires before a tab is de-activated - can be used to do validation on a form.
18151              * @param {Roo.bootstrap.TabPanel} this
18152              * @return {Boolean} false if there is an error
18153             
18154          */
18155         'beforedeactivate': true
18156      });
18157     
18158     this.tabId = this.tabId || Roo.id();
18159   
18160 };
18161
18162 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18163     
18164     active: false,
18165     html: false,
18166     tabId: false,
18167     navId : false,
18168     href : '',
18169     
18170     getAutoCreate : function(){
18171         var cfg = {
18172             tag: 'div',
18173             // item is needed for carousel - not sure if it has any effect otherwise
18174             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18175             html: this.html || ''
18176         };
18177         
18178         if(this.active){
18179             cfg.cls += ' active';
18180         }
18181         
18182         if(this.tabId){
18183             cfg.tabId = this.tabId;
18184         }
18185         
18186         
18187         return cfg;
18188     },
18189     
18190     initEvents:  function()
18191     {
18192         var p = this.parent();
18193         
18194         this.navId = this.navId || p.navId;
18195         
18196         if (typeof(this.navId) != 'undefined') {
18197             // not really needed.. but just in case.. parent should be a NavGroup.
18198             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18199             
18200             tg.register(this);
18201             
18202             var i = tg.tabs.length - 1;
18203             
18204             if(this.active && tg.bullets > 0 && i < tg.bullets){
18205                 tg.setActiveBullet(i);
18206             }
18207         }
18208         
18209         this.el.on('click', this.onClick, this);
18210         
18211         if(Roo.isTouch){
18212             this.el.on("touchstart", this.onTouchStart, this);
18213             this.el.on("touchmove", this.onTouchMove, this);
18214             this.el.on("touchend", this.onTouchEnd, this);
18215         }
18216         
18217     },
18218     
18219     onRender : function(ct, position)
18220     {
18221         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18222     },
18223     
18224     setActive : function(state)
18225     {
18226         Roo.log("panel - set active " + this.tabId + "=" + state);
18227         
18228         this.active = state;
18229         if (!state) {
18230             this.el.removeClass('active');
18231             
18232         } else  if (!this.el.hasClass('active')) {
18233             this.el.addClass('active');
18234         }
18235         
18236         this.fireEvent('changed', this, state);
18237     },
18238     
18239     onClick : function(e)
18240     {
18241         e.preventDefault();
18242         
18243         if(!this.href.length){
18244             return;
18245         }
18246         
18247         window.location.href = this.href;
18248     },
18249     
18250     startX : 0,
18251     startY : 0,
18252     endX : 0,
18253     endY : 0,
18254     swiping : false,
18255     
18256     onTouchStart : function(e)
18257     {
18258         this.swiping = false;
18259         
18260         this.startX = e.browserEvent.touches[0].clientX;
18261         this.startY = e.browserEvent.touches[0].clientY;
18262     },
18263     
18264     onTouchMove : function(e)
18265     {
18266         this.swiping = true;
18267         
18268         this.endX = e.browserEvent.touches[0].clientX;
18269         this.endY = e.browserEvent.touches[0].clientY;
18270     },
18271     
18272     onTouchEnd : function(e)
18273     {
18274         if(!this.swiping){
18275             this.onClick(e);
18276             return;
18277         }
18278         
18279         var tabGroup = this.parent();
18280         
18281         if(this.endX > this.startX){ // swiping right
18282             tabGroup.showPanelPrev();
18283             return;
18284         }
18285         
18286         if(this.startX > this.endX){ // swiping left
18287             tabGroup.showPanelNext();
18288             return;
18289         }
18290     }
18291     
18292     
18293 });
18294  
18295
18296  
18297
18298  /*
18299  * - LGPL
18300  *
18301  * DateField
18302  * 
18303  */
18304
18305 /**
18306  * @class Roo.bootstrap.DateField
18307  * @extends Roo.bootstrap.Input
18308  * Bootstrap DateField class
18309  * @cfg {Number} weekStart default 0
18310  * @cfg {String} viewMode default empty, (months|years)
18311  * @cfg {String} minViewMode default empty, (months|years)
18312  * @cfg {Number} startDate default -Infinity
18313  * @cfg {Number} endDate default Infinity
18314  * @cfg {Boolean} todayHighlight default false
18315  * @cfg {Boolean} todayBtn default false
18316  * @cfg {Boolean} calendarWeeks default false
18317  * @cfg {Object} daysOfWeekDisabled default empty
18318  * @cfg {Boolean} singleMode default false (true | false)
18319  * 
18320  * @cfg {Boolean} keyboardNavigation default true
18321  * @cfg {String} language default en
18322  * 
18323  * @constructor
18324  * Create a new DateField
18325  * @param {Object} config The config object
18326  */
18327
18328 Roo.bootstrap.DateField = function(config){
18329     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18330      this.addEvents({
18331             /**
18332              * @event show
18333              * Fires when this field show.
18334              * @param {Roo.bootstrap.DateField} this
18335              * @param {Mixed} date The date value
18336              */
18337             show : true,
18338             /**
18339              * @event show
18340              * Fires when this field hide.
18341              * @param {Roo.bootstrap.DateField} this
18342              * @param {Mixed} date The date value
18343              */
18344             hide : true,
18345             /**
18346              * @event select
18347              * Fires when select a date.
18348              * @param {Roo.bootstrap.DateField} this
18349              * @param {Mixed} date The date value
18350              */
18351             select : true,
18352             /**
18353              * @event beforeselect
18354              * Fires when before select a date.
18355              * @param {Roo.bootstrap.DateField} this
18356              * @param {Mixed} date The date value
18357              */
18358             beforeselect : true
18359         });
18360 };
18361
18362 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18363     
18364     /**
18365      * @cfg {String} format
18366      * The default date format string which can be overriden for localization support.  The format must be
18367      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18368      */
18369     format : "m/d/y",
18370     /**
18371      * @cfg {String} altFormats
18372      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18373      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18374      */
18375     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18376     
18377     weekStart : 0,
18378     
18379     viewMode : '',
18380     
18381     minViewMode : '',
18382     
18383     todayHighlight : false,
18384     
18385     todayBtn: false,
18386     
18387     language: 'en',
18388     
18389     keyboardNavigation: true,
18390     
18391     calendarWeeks: false,
18392     
18393     startDate: -Infinity,
18394     
18395     endDate: Infinity,
18396     
18397     daysOfWeekDisabled: [],
18398     
18399     _events: [],
18400     
18401     singleMode : false,
18402     
18403     UTCDate: function()
18404     {
18405         return new Date(Date.UTC.apply(Date, arguments));
18406     },
18407     
18408     UTCToday: function()
18409     {
18410         var today = new Date();
18411         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18412     },
18413     
18414     getDate: function() {
18415             var d = this.getUTCDate();
18416             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18417     },
18418     
18419     getUTCDate: function() {
18420             return this.date;
18421     },
18422     
18423     setDate: function(d) {
18424             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18425     },
18426     
18427     setUTCDate: function(d) {
18428             this.date = d;
18429             this.setValue(this.formatDate(this.date));
18430     },
18431         
18432     onRender: function(ct, position)
18433     {
18434         
18435         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18436         
18437         this.language = this.language || 'en';
18438         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18439         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18440         
18441         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18442         this.format = this.format || 'm/d/y';
18443         this.isInline = false;
18444         this.isInput = true;
18445         this.component = this.el.select('.add-on', true).first() || false;
18446         this.component = (this.component && this.component.length === 0) ? false : this.component;
18447         this.hasInput = this.component && this.inputEl().length;
18448         
18449         if (typeof(this.minViewMode === 'string')) {
18450             switch (this.minViewMode) {
18451                 case 'months':
18452                     this.minViewMode = 1;
18453                     break;
18454                 case 'years':
18455                     this.minViewMode = 2;
18456                     break;
18457                 default:
18458                     this.minViewMode = 0;
18459                     break;
18460             }
18461         }
18462         
18463         if (typeof(this.viewMode === 'string')) {
18464             switch (this.viewMode) {
18465                 case 'months':
18466                     this.viewMode = 1;
18467                     break;
18468                 case 'years':
18469                     this.viewMode = 2;
18470                     break;
18471                 default:
18472                     this.viewMode = 0;
18473                     break;
18474             }
18475         }
18476                 
18477         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18478         
18479 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18480         
18481         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18482         
18483         this.picker().on('mousedown', this.onMousedown, this);
18484         this.picker().on('click', this.onClick, this);
18485         
18486         this.picker().addClass('datepicker-dropdown');
18487         
18488         this.startViewMode = this.viewMode;
18489         
18490         if(this.singleMode){
18491             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18492                 v.setVisibilityMode(Roo.Element.DISPLAY);
18493                 v.hide();
18494             });
18495             
18496             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18497                 v.setStyle('width', '189px');
18498             });
18499         }
18500         
18501         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18502             if(!this.calendarWeeks){
18503                 v.remove();
18504                 return;
18505             }
18506             
18507             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18508             v.attr('colspan', function(i, val){
18509                 return parseInt(val) + 1;
18510             });
18511         });
18512                         
18513         
18514         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18515         
18516         this.setStartDate(this.startDate);
18517         this.setEndDate(this.endDate);
18518         
18519         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18520         
18521         this.fillDow();
18522         this.fillMonths();
18523         this.update();
18524         this.showMode();
18525         
18526         if(this.isInline) {
18527             this.show();
18528         }
18529     },
18530     
18531     picker : function()
18532     {
18533         return this.pickerEl;
18534 //        return this.el.select('.datepicker', true).first();
18535     },
18536     
18537     fillDow: function()
18538     {
18539         var dowCnt = this.weekStart;
18540         
18541         var dow = {
18542             tag: 'tr',
18543             cn: [
18544                 
18545             ]
18546         };
18547         
18548         if(this.calendarWeeks){
18549             dow.cn.push({
18550                 tag: 'th',
18551                 cls: 'cw',
18552                 html: '&nbsp;'
18553             })
18554         }
18555         
18556         while (dowCnt < this.weekStart + 7) {
18557             dow.cn.push({
18558                 tag: 'th',
18559                 cls: 'dow',
18560                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18561             });
18562         }
18563         
18564         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18565     },
18566     
18567     fillMonths: function()
18568     {    
18569         var i = 0;
18570         var months = this.picker().select('>.datepicker-months td', true).first();
18571         
18572         months.dom.innerHTML = '';
18573         
18574         while (i < 12) {
18575             var month = {
18576                 tag: 'span',
18577                 cls: 'month',
18578                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18579             };
18580             
18581             months.createChild(month);
18582         }
18583         
18584     },
18585     
18586     update: function()
18587     {
18588         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;
18589         
18590         if (this.date < this.startDate) {
18591             this.viewDate = new Date(this.startDate);
18592         } else if (this.date > this.endDate) {
18593             this.viewDate = new Date(this.endDate);
18594         } else {
18595             this.viewDate = new Date(this.date);
18596         }
18597         
18598         this.fill();
18599     },
18600     
18601     fill: function() 
18602     {
18603         var d = new Date(this.viewDate),
18604                 year = d.getUTCFullYear(),
18605                 month = d.getUTCMonth(),
18606                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18607                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18608                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18609                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18610                 currentDate = this.date && this.date.valueOf(),
18611                 today = this.UTCToday();
18612         
18613         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18614         
18615 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18616         
18617 //        this.picker.select('>tfoot th.today').
18618 //                                              .text(dates[this.language].today)
18619 //                                              .toggle(this.todayBtn !== false);
18620     
18621         this.updateNavArrows();
18622         this.fillMonths();
18623                                                 
18624         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18625         
18626         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18627          
18628         prevMonth.setUTCDate(day);
18629         
18630         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18631         
18632         var nextMonth = new Date(prevMonth);
18633         
18634         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18635         
18636         nextMonth = nextMonth.valueOf();
18637         
18638         var fillMonths = false;
18639         
18640         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18641         
18642         while(prevMonth.valueOf() < nextMonth) {
18643             var clsName = '';
18644             
18645             if (prevMonth.getUTCDay() === this.weekStart) {
18646                 if(fillMonths){
18647                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18648                 }
18649                     
18650                 fillMonths = {
18651                     tag: 'tr',
18652                     cn: []
18653                 };
18654                 
18655                 if(this.calendarWeeks){
18656                     // ISO 8601: First week contains first thursday.
18657                     // ISO also states week starts on Monday, but we can be more abstract here.
18658                     var
18659                     // Start of current week: based on weekstart/current date
18660                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18661                     // Thursday of this week
18662                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18663                     // First Thursday of year, year from thursday
18664                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18665                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18666                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18667                     
18668                     fillMonths.cn.push({
18669                         tag: 'td',
18670                         cls: 'cw',
18671                         html: calWeek
18672                     });
18673                 }
18674             }
18675             
18676             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18677                 clsName += ' old';
18678             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18679                 clsName += ' new';
18680             }
18681             if (this.todayHighlight &&
18682                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18683                 prevMonth.getUTCMonth() == today.getMonth() &&
18684                 prevMonth.getUTCDate() == today.getDate()) {
18685                 clsName += ' today';
18686             }
18687             
18688             if (currentDate && prevMonth.valueOf() === currentDate) {
18689                 clsName += ' active';
18690             }
18691             
18692             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18693                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18694                     clsName += ' disabled';
18695             }
18696             
18697             fillMonths.cn.push({
18698                 tag: 'td',
18699                 cls: 'day ' + clsName,
18700                 html: prevMonth.getDate()
18701             });
18702             
18703             prevMonth.setDate(prevMonth.getDate()+1);
18704         }
18705           
18706         var currentYear = this.date && this.date.getUTCFullYear();
18707         var currentMonth = this.date && this.date.getUTCMonth();
18708         
18709         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18710         
18711         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18712             v.removeClass('active');
18713             
18714             if(currentYear === year && k === currentMonth){
18715                 v.addClass('active');
18716             }
18717             
18718             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18719                 v.addClass('disabled');
18720             }
18721             
18722         });
18723         
18724         
18725         year = parseInt(year/10, 10) * 10;
18726         
18727         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18728         
18729         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18730         
18731         year -= 1;
18732         for (var i = -1; i < 11; i++) {
18733             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18734                 tag: 'span',
18735                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18736                 html: year
18737             });
18738             
18739             year += 1;
18740         }
18741     },
18742     
18743     showMode: function(dir) 
18744     {
18745         if (dir) {
18746             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18747         }
18748         
18749         Roo.each(this.picker().select('>div',true).elements, function(v){
18750             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18751             v.hide();
18752         });
18753         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18754     },
18755     
18756     place: function()
18757     {
18758         if(this.isInline) {
18759             return;
18760         }
18761         
18762         this.picker().removeClass(['bottom', 'top']);
18763         
18764         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18765             /*
18766              * place to the top of element!
18767              *
18768              */
18769             
18770             this.picker().addClass('top');
18771             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18772             
18773             return;
18774         }
18775         
18776         this.picker().addClass('bottom');
18777         
18778         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18779     },
18780     
18781     parseDate : function(value)
18782     {
18783         if(!value || value instanceof Date){
18784             return value;
18785         }
18786         var v = Date.parseDate(value, this.format);
18787         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18788             v = Date.parseDate(value, 'Y-m-d');
18789         }
18790         if(!v && this.altFormats){
18791             if(!this.altFormatsArray){
18792                 this.altFormatsArray = this.altFormats.split("|");
18793             }
18794             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18795                 v = Date.parseDate(value, this.altFormatsArray[i]);
18796             }
18797         }
18798         return v;
18799     },
18800     
18801     formatDate : function(date, fmt)
18802     {   
18803         return (!date || !(date instanceof Date)) ?
18804         date : date.dateFormat(fmt || this.format);
18805     },
18806     
18807     onFocus : function()
18808     {
18809         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18810         this.show();
18811     },
18812     
18813     onBlur : function()
18814     {
18815         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18816         
18817         var d = this.inputEl().getValue();
18818         
18819         this.setValue(d);
18820                 
18821         this.hide();
18822     },
18823     
18824     show : function()
18825     {
18826         this.picker().show();
18827         this.update();
18828         this.place();
18829         
18830         this.fireEvent('show', this, this.date);
18831     },
18832     
18833     hide : function()
18834     {
18835         if(this.isInline) {
18836             return;
18837         }
18838         this.picker().hide();
18839         this.viewMode = this.startViewMode;
18840         this.showMode();
18841         
18842         this.fireEvent('hide', this, this.date);
18843         
18844     },
18845     
18846     onMousedown: function(e)
18847     {
18848         e.stopPropagation();
18849         e.preventDefault();
18850     },
18851     
18852     keyup: function(e)
18853     {
18854         Roo.bootstrap.DateField.superclass.keyup.call(this);
18855         this.update();
18856     },
18857
18858     setValue: function(v)
18859     {
18860         if(this.fireEvent('beforeselect', this, v) !== false){
18861             var d = new Date(this.parseDate(v) ).clearTime();
18862         
18863             if(isNaN(d.getTime())){
18864                 this.date = this.viewDate = '';
18865                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18866                 return;
18867             }
18868
18869             v = this.formatDate(d);
18870
18871             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18872
18873             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18874
18875             this.update();
18876
18877             this.fireEvent('select', this, this.date);
18878         }
18879     },
18880     
18881     getValue: function()
18882     {
18883         return this.formatDate(this.date);
18884     },
18885     
18886     fireKey: function(e)
18887     {
18888         if (!this.picker().isVisible()){
18889             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18890                 this.show();
18891             }
18892             return;
18893         }
18894         
18895         var dateChanged = false,
18896         dir, day, month,
18897         newDate, newViewDate;
18898         
18899         switch(e.keyCode){
18900             case 27: // escape
18901                 this.hide();
18902                 e.preventDefault();
18903                 break;
18904             case 37: // left
18905             case 39: // right
18906                 if (!this.keyboardNavigation) {
18907                     break;
18908                 }
18909                 dir = e.keyCode == 37 ? -1 : 1;
18910                 
18911                 if (e.ctrlKey){
18912                     newDate = this.moveYear(this.date, dir);
18913                     newViewDate = this.moveYear(this.viewDate, dir);
18914                 } else if (e.shiftKey){
18915                     newDate = this.moveMonth(this.date, dir);
18916                     newViewDate = this.moveMonth(this.viewDate, dir);
18917                 } else {
18918                     newDate = new Date(this.date);
18919                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18920                     newViewDate = new Date(this.viewDate);
18921                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18922                 }
18923                 if (this.dateWithinRange(newDate)){
18924                     this.date = newDate;
18925                     this.viewDate = newViewDate;
18926                     this.setValue(this.formatDate(this.date));
18927 //                    this.update();
18928                     e.preventDefault();
18929                     dateChanged = true;
18930                 }
18931                 break;
18932             case 38: // up
18933             case 40: // down
18934                 if (!this.keyboardNavigation) {
18935                     break;
18936                 }
18937                 dir = e.keyCode == 38 ? -1 : 1;
18938                 if (e.ctrlKey){
18939                     newDate = this.moveYear(this.date, dir);
18940                     newViewDate = this.moveYear(this.viewDate, dir);
18941                 } else if (e.shiftKey){
18942                     newDate = this.moveMonth(this.date, dir);
18943                     newViewDate = this.moveMonth(this.viewDate, dir);
18944                 } else {
18945                     newDate = new Date(this.date);
18946                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18947                     newViewDate = new Date(this.viewDate);
18948                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18949                 }
18950                 if (this.dateWithinRange(newDate)){
18951                     this.date = newDate;
18952                     this.viewDate = newViewDate;
18953                     this.setValue(this.formatDate(this.date));
18954 //                    this.update();
18955                     e.preventDefault();
18956                     dateChanged = true;
18957                 }
18958                 break;
18959             case 13: // enter
18960                 this.setValue(this.formatDate(this.date));
18961                 this.hide();
18962                 e.preventDefault();
18963                 break;
18964             case 9: // tab
18965                 this.setValue(this.formatDate(this.date));
18966                 this.hide();
18967                 break;
18968             case 16: // shift
18969             case 17: // ctrl
18970             case 18: // alt
18971                 break;
18972             default :
18973                 this.hide();
18974                 
18975         }
18976     },
18977     
18978     
18979     onClick: function(e) 
18980     {
18981         e.stopPropagation();
18982         e.preventDefault();
18983         
18984         var target = e.getTarget();
18985         
18986         if(target.nodeName.toLowerCase() === 'i'){
18987             target = Roo.get(target).dom.parentNode;
18988         }
18989         
18990         var nodeName = target.nodeName;
18991         var className = target.className;
18992         var html = target.innerHTML;
18993         //Roo.log(nodeName);
18994         
18995         switch(nodeName.toLowerCase()) {
18996             case 'th':
18997                 switch(className) {
18998                     case 'switch':
18999                         this.showMode(1);
19000                         break;
19001                     case 'prev':
19002                     case 'next':
19003                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19004                         switch(this.viewMode){
19005                                 case 0:
19006                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19007                                         break;
19008                                 case 1:
19009                                 case 2:
19010                                         this.viewDate = this.moveYear(this.viewDate, dir);
19011                                         break;
19012                         }
19013                         this.fill();
19014                         break;
19015                     case 'today':
19016                         var date = new Date();
19017                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19018 //                        this.fill()
19019                         this.setValue(this.formatDate(this.date));
19020                         
19021                         this.hide();
19022                         break;
19023                 }
19024                 break;
19025             case 'span':
19026                 if (className.indexOf('disabled') < 0) {
19027                     this.viewDate.setUTCDate(1);
19028                     if (className.indexOf('month') > -1) {
19029                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19030                     } else {
19031                         var year = parseInt(html, 10) || 0;
19032                         this.viewDate.setUTCFullYear(year);
19033                         
19034                     }
19035                     
19036                     if(this.singleMode){
19037                         this.setValue(this.formatDate(this.viewDate));
19038                         this.hide();
19039                         return;
19040                     }
19041                     
19042                     this.showMode(-1);
19043                     this.fill();
19044                 }
19045                 break;
19046                 
19047             case 'td':
19048                 //Roo.log(className);
19049                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19050                     var day = parseInt(html, 10) || 1;
19051                     var year = this.viewDate.getUTCFullYear(),
19052                         month = this.viewDate.getUTCMonth();
19053
19054                     if (className.indexOf('old') > -1) {
19055                         if(month === 0 ){
19056                             month = 11;
19057                             year -= 1;
19058                         }else{
19059                             month -= 1;
19060                         }
19061                     } else if (className.indexOf('new') > -1) {
19062                         if (month == 11) {
19063                             month = 0;
19064                             year += 1;
19065                         } else {
19066                             month += 1;
19067                         }
19068                     }
19069                     //Roo.log([year,month,day]);
19070                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19071                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19072 //                    this.fill();
19073                     //Roo.log(this.formatDate(this.date));
19074                     this.setValue(this.formatDate(this.date));
19075                     this.hide();
19076                 }
19077                 break;
19078         }
19079     },
19080     
19081     setStartDate: function(startDate)
19082     {
19083         this.startDate = startDate || -Infinity;
19084         if (this.startDate !== -Infinity) {
19085             this.startDate = this.parseDate(this.startDate);
19086         }
19087         this.update();
19088         this.updateNavArrows();
19089     },
19090
19091     setEndDate: function(endDate)
19092     {
19093         this.endDate = endDate || Infinity;
19094         if (this.endDate !== Infinity) {
19095             this.endDate = this.parseDate(this.endDate);
19096         }
19097         this.update();
19098         this.updateNavArrows();
19099     },
19100     
19101     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19102     {
19103         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19104         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19105             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19106         }
19107         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19108             return parseInt(d, 10);
19109         });
19110         this.update();
19111         this.updateNavArrows();
19112     },
19113     
19114     updateNavArrows: function() 
19115     {
19116         if(this.singleMode){
19117             return;
19118         }
19119         
19120         var d = new Date(this.viewDate),
19121         year = d.getUTCFullYear(),
19122         month = d.getUTCMonth();
19123         
19124         Roo.each(this.picker().select('.prev', true).elements, function(v){
19125             v.show();
19126             switch (this.viewMode) {
19127                 case 0:
19128
19129                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19130                         v.hide();
19131                     }
19132                     break;
19133                 case 1:
19134                 case 2:
19135                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19136                         v.hide();
19137                     }
19138                     break;
19139             }
19140         });
19141         
19142         Roo.each(this.picker().select('.next', true).elements, function(v){
19143             v.show();
19144             switch (this.viewMode) {
19145                 case 0:
19146
19147                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19148                         v.hide();
19149                     }
19150                     break;
19151                 case 1:
19152                 case 2:
19153                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19154                         v.hide();
19155                     }
19156                     break;
19157             }
19158         })
19159     },
19160     
19161     moveMonth: function(date, dir)
19162     {
19163         if (!dir) {
19164             return date;
19165         }
19166         var new_date = new Date(date.valueOf()),
19167         day = new_date.getUTCDate(),
19168         month = new_date.getUTCMonth(),
19169         mag = Math.abs(dir),
19170         new_month, test;
19171         dir = dir > 0 ? 1 : -1;
19172         if (mag == 1){
19173             test = dir == -1
19174             // If going back one month, make sure month is not current month
19175             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19176             ? function(){
19177                 return new_date.getUTCMonth() == month;
19178             }
19179             // If going forward one month, make sure month is as expected
19180             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19181             : function(){
19182                 return new_date.getUTCMonth() != new_month;
19183             };
19184             new_month = month + dir;
19185             new_date.setUTCMonth(new_month);
19186             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19187             if (new_month < 0 || new_month > 11) {
19188                 new_month = (new_month + 12) % 12;
19189             }
19190         } else {
19191             // For magnitudes >1, move one month at a time...
19192             for (var i=0; i<mag; i++) {
19193                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19194                 new_date = this.moveMonth(new_date, dir);
19195             }
19196             // ...then reset the day, keeping it in the new month
19197             new_month = new_date.getUTCMonth();
19198             new_date.setUTCDate(day);
19199             test = function(){
19200                 return new_month != new_date.getUTCMonth();
19201             };
19202         }
19203         // Common date-resetting loop -- if date is beyond end of month, make it
19204         // end of month
19205         while (test()){
19206             new_date.setUTCDate(--day);
19207             new_date.setUTCMonth(new_month);
19208         }
19209         return new_date;
19210     },
19211
19212     moveYear: function(date, dir)
19213     {
19214         return this.moveMonth(date, dir*12);
19215     },
19216
19217     dateWithinRange: function(date)
19218     {
19219         return date >= this.startDate && date <= this.endDate;
19220     },
19221
19222     
19223     remove: function() 
19224     {
19225         this.picker().remove();
19226     },
19227     
19228     validateValue : function(value)
19229     {
19230         if(this.getVisibilityEl().hasClass('hidden')){
19231             return true;
19232         }
19233         
19234         if(value.length < 1)  {
19235             if(this.allowBlank){
19236                 return true;
19237             }
19238             return false;
19239         }
19240         
19241         if(value.length < this.minLength){
19242             return false;
19243         }
19244         if(value.length > this.maxLength){
19245             return false;
19246         }
19247         if(this.vtype){
19248             var vt = Roo.form.VTypes;
19249             if(!vt[this.vtype](value, this)){
19250                 return false;
19251             }
19252         }
19253         if(typeof this.validator == "function"){
19254             var msg = this.validator(value);
19255             if(msg !== true){
19256                 return false;
19257             }
19258         }
19259         
19260         if(this.regex && !this.regex.test(value)){
19261             return false;
19262         }
19263         
19264         if(typeof(this.parseDate(value)) == 'undefined'){
19265             return false;
19266         }
19267         
19268         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19269             return false;
19270         }      
19271         
19272         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19273             return false;
19274         } 
19275         
19276         
19277         return true;
19278     },
19279     
19280     setVisible : function(visible)
19281     {
19282         if(!this.getEl()){
19283             return;
19284         }
19285         
19286         this.getEl().removeClass('hidden');
19287         
19288         if(visible){
19289             return;
19290         }
19291         
19292         this.getEl().addClass('hidden');
19293     }
19294    
19295 });
19296
19297 Roo.apply(Roo.bootstrap.DateField,  {
19298     
19299     head : {
19300         tag: 'thead',
19301         cn: [
19302         {
19303             tag: 'tr',
19304             cn: [
19305             {
19306                 tag: 'th',
19307                 cls: 'prev',
19308                 html: '<i class="fa fa-arrow-left"/>'
19309             },
19310             {
19311                 tag: 'th',
19312                 cls: 'switch',
19313                 colspan: '5'
19314             },
19315             {
19316                 tag: 'th',
19317                 cls: 'next',
19318                 html: '<i class="fa fa-arrow-right"/>'
19319             }
19320
19321             ]
19322         }
19323         ]
19324     },
19325     
19326     content : {
19327         tag: 'tbody',
19328         cn: [
19329         {
19330             tag: 'tr',
19331             cn: [
19332             {
19333                 tag: 'td',
19334                 colspan: '7'
19335             }
19336             ]
19337         }
19338         ]
19339     },
19340     
19341     footer : {
19342         tag: 'tfoot',
19343         cn: [
19344         {
19345             tag: 'tr',
19346             cn: [
19347             {
19348                 tag: 'th',
19349                 colspan: '7',
19350                 cls: 'today'
19351             }
19352                     
19353             ]
19354         }
19355         ]
19356     },
19357     
19358     dates:{
19359         en: {
19360             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19361             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19362             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19363             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19364             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19365             today: "Today"
19366         }
19367     },
19368     
19369     modes: [
19370     {
19371         clsName: 'days',
19372         navFnc: 'Month',
19373         navStep: 1
19374     },
19375     {
19376         clsName: 'months',
19377         navFnc: 'FullYear',
19378         navStep: 1
19379     },
19380     {
19381         clsName: 'years',
19382         navFnc: 'FullYear',
19383         navStep: 10
19384     }]
19385 });
19386
19387 Roo.apply(Roo.bootstrap.DateField,  {
19388   
19389     template : {
19390         tag: 'div',
19391         cls: 'datepicker dropdown-menu roo-dynamic',
19392         cn: [
19393         {
19394             tag: 'div',
19395             cls: 'datepicker-days',
19396             cn: [
19397             {
19398                 tag: 'table',
19399                 cls: 'table-condensed',
19400                 cn:[
19401                 Roo.bootstrap.DateField.head,
19402                 {
19403                     tag: 'tbody'
19404                 },
19405                 Roo.bootstrap.DateField.footer
19406                 ]
19407             }
19408             ]
19409         },
19410         {
19411             tag: 'div',
19412             cls: 'datepicker-months',
19413             cn: [
19414             {
19415                 tag: 'table',
19416                 cls: 'table-condensed',
19417                 cn:[
19418                 Roo.bootstrap.DateField.head,
19419                 Roo.bootstrap.DateField.content,
19420                 Roo.bootstrap.DateField.footer
19421                 ]
19422             }
19423             ]
19424         },
19425         {
19426             tag: 'div',
19427             cls: 'datepicker-years',
19428             cn: [
19429             {
19430                 tag: 'table',
19431                 cls: 'table-condensed',
19432                 cn:[
19433                 Roo.bootstrap.DateField.head,
19434                 Roo.bootstrap.DateField.content,
19435                 Roo.bootstrap.DateField.footer
19436                 ]
19437             }
19438             ]
19439         }
19440         ]
19441     }
19442 });
19443
19444  
19445
19446  /*
19447  * - LGPL
19448  *
19449  * TimeField
19450  * 
19451  */
19452
19453 /**
19454  * @class Roo.bootstrap.TimeField
19455  * @extends Roo.bootstrap.Input
19456  * Bootstrap DateField class
19457  * 
19458  * 
19459  * @constructor
19460  * Create a new TimeField
19461  * @param {Object} config The config object
19462  */
19463
19464 Roo.bootstrap.TimeField = function(config){
19465     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19466     this.addEvents({
19467             /**
19468              * @event show
19469              * Fires when this field show.
19470              * @param {Roo.bootstrap.DateField} thisthis
19471              * @param {Mixed} date The date value
19472              */
19473             show : true,
19474             /**
19475              * @event show
19476              * Fires when this field hide.
19477              * @param {Roo.bootstrap.DateField} this
19478              * @param {Mixed} date The date value
19479              */
19480             hide : true,
19481             /**
19482              * @event select
19483              * Fires when select a date.
19484              * @param {Roo.bootstrap.DateField} this
19485              * @param {Mixed} date The date value
19486              */
19487             select : true
19488         });
19489 };
19490
19491 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19492     
19493     /**
19494      * @cfg {String} format
19495      * The default time format string which can be overriden for localization support.  The format must be
19496      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19497      */
19498     format : "H:i",
19499        
19500     onRender: function(ct, position)
19501     {
19502         
19503         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19504                 
19505         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19506         
19507         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19508         
19509         this.pop = this.picker().select('>.datepicker-time',true).first();
19510         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19511         
19512         this.picker().on('mousedown', this.onMousedown, this);
19513         this.picker().on('click', this.onClick, this);
19514         
19515         this.picker().addClass('datepicker-dropdown');
19516     
19517         this.fillTime();
19518         this.update();
19519             
19520         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19521         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19522         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19523         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19524         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19525         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19526
19527     },
19528     
19529     fireKey: function(e){
19530         if (!this.picker().isVisible()){
19531             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19532                 this.show();
19533             }
19534             return;
19535         }
19536
19537         e.preventDefault();
19538         
19539         switch(e.keyCode){
19540             case 27: // escape
19541                 this.hide();
19542                 break;
19543             case 37: // left
19544             case 39: // right
19545                 this.onTogglePeriod();
19546                 break;
19547             case 38: // up
19548                 this.onIncrementMinutes();
19549                 break;
19550             case 40: // down
19551                 this.onDecrementMinutes();
19552                 break;
19553             case 13: // enter
19554             case 9: // tab
19555                 this.setTime();
19556                 break;
19557         }
19558     },
19559     
19560     onClick: function(e) {
19561         e.stopPropagation();
19562         e.preventDefault();
19563     },
19564     
19565     picker : function()
19566     {
19567         return this.el.select('.datepicker', true).first();
19568     },
19569     
19570     fillTime: function()
19571     {    
19572         var time = this.pop.select('tbody', true).first();
19573         
19574         time.dom.innerHTML = '';
19575         
19576         time.createChild({
19577             tag: 'tr',
19578             cn: [
19579                 {
19580                     tag: 'td',
19581                     cn: [
19582                         {
19583                             tag: 'a',
19584                             href: '#',
19585                             cls: 'btn',
19586                             cn: [
19587                                 {
19588                                     tag: 'span',
19589                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19590                                 }
19591                             ]
19592                         } 
19593                     ]
19594                 },
19595                 {
19596                     tag: 'td',
19597                     cls: 'separator'
19598                 },
19599                 {
19600                     tag: 'td',
19601                     cn: [
19602                         {
19603                             tag: 'a',
19604                             href: '#',
19605                             cls: 'btn',
19606                             cn: [
19607                                 {
19608                                     tag: 'span',
19609                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19610                                 }
19611                             ]
19612                         }
19613                     ]
19614                 },
19615                 {
19616                     tag: 'td',
19617                     cls: 'separator'
19618                 }
19619             ]
19620         });
19621         
19622         time.createChild({
19623             tag: 'tr',
19624             cn: [
19625                 {
19626                     tag: 'td',
19627                     cn: [
19628                         {
19629                             tag: 'span',
19630                             cls: 'timepicker-hour',
19631                             html: '00'
19632                         }  
19633                     ]
19634                 },
19635                 {
19636                     tag: 'td',
19637                     cls: 'separator',
19638                     html: ':'
19639                 },
19640                 {
19641                     tag: 'td',
19642                     cn: [
19643                         {
19644                             tag: 'span',
19645                             cls: 'timepicker-minute',
19646                             html: '00'
19647                         }  
19648                     ]
19649                 },
19650                 {
19651                     tag: 'td',
19652                     cls: 'separator'
19653                 },
19654                 {
19655                     tag: 'td',
19656                     cn: [
19657                         {
19658                             tag: 'button',
19659                             type: 'button',
19660                             cls: 'btn btn-primary period',
19661                             html: 'AM'
19662                             
19663                         }
19664                     ]
19665                 }
19666             ]
19667         });
19668         
19669         time.createChild({
19670             tag: 'tr',
19671             cn: [
19672                 {
19673                     tag: 'td',
19674                     cn: [
19675                         {
19676                             tag: 'a',
19677                             href: '#',
19678                             cls: 'btn',
19679                             cn: [
19680                                 {
19681                                     tag: 'span',
19682                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19683                                 }
19684                             ]
19685                         }
19686                     ]
19687                 },
19688                 {
19689                     tag: 'td',
19690                     cls: 'separator'
19691                 },
19692                 {
19693                     tag: 'td',
19694                     cn: [
19695                         {
19696                             tag: 'a',
19697                             href: '#',
19698                             cls: 'btn',
19699                             cn: [
19700                                 {
19701                                     tag: 'span',
19702                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19703                                 }
19704                             ]
19705                         }
19706                     ]
19707                 },
19708                 {
19709                     tag: 'td',
19710                     cls: 'separator'
19711                 }
19712             ]
19713         });
19714         
19715     },
19716     
19717     update: function()
19718     {
19719         
19720         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19721         
19722         this.fill();
19723     },
19724     
19725     fill: function() 
19726     {
19727         var hours = this.time.getHours();
19728         var minutes = this.time.getMinutes();
19729         var period = 'AM';
19730         
19731         if(hours > 11){
19732             period = 'PM';
19733         }
19734         
19735         if(hours == 0){
19736             hours = 12;
19737         }
19738         
19739         
19740         if(hours > 12){
19741             hours = hours - 12;
19742         }
19743         
19744         if(hours < 10){
19745             hours = '0' + hours;
19746         }
19747         
19748         if(minutes < 10){
19749             minutes = '0' + minutes;
19750         }
19751         
19752         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19753         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19754         this.pop.select('button', true).first().dom.innerHTML = period;
19755         
19756     },
19757     
19758     place: function()
19759     {   
19760         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19761         
19762         var cls = ['bottom'];
19763         
19764         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19765             cls.pop();
19766             cls.push('top');
19767         }
19768         
19769         cls.push('right');
19770         
19771         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19772             cls.pop();
19773             cls.push('left');
19774         }
19775         
19776         this.picker().addClass(cls.join('-'));
19777         
19778         var _this = this;
19779         
19780         Roo.each(cls, function(c){
19781             if(c == 'bottom'){
19782                 _this.picker().setTop(_this.inputEl().getHeight());
19783                 return;
19784             }
19785             if(c == 'top'){
19786                 _this.picker().setTop(0 - _this.picker().getHeight());
19787                 return;
19788             }
19789             
19790             if(c == 'left'){
19791                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19792                 return;
19793             }
19794             if(c == 'right'){
19795                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19796                 return;
19797             }
19798         });
19799         
19800     },
19801   
19802     onFocus : function()
19803     {
19804         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19805         this.show();
19806     },
19807     
19808     onBlur : function()
19809     {
19810         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19811         this.hide();
19812     },
19813     
19814     show : function()
19815     {
19816         this.picker().show();
19817         this.pop.show();
19818         this.update();
19819         this.place();
19820         
19821         this.fireEvent('show', this, this.date);
19822     },
19823     
19824     hide : function()
19825     {
19826         this.picker().hide();
19827         this.pop.hide();
19828         
19829         this.fireEvent('hide', this, this.date);
19830     },
19831     
19832     setTime : function()
19833     {
19834         this.hide();
19835         this.setValue(this.time.format(this.format));
19836         
19837         this.fireEvent('select', this, this.date);
19838         
19839         
19840     },
19841     
19842     onMousedown: function(e){
19843         e.stopPropagation();
19844         e.preventDefault();
19845     },
19846     
19847     onIncrementHours: function()
19848     {
19849         Roo.log('onIncrementHours');
19850         this.time = this.time.add(Date.HOUR, 1);
19851         this.update();
19852         
19853     },
19854     
19855     onDecrementHours: function()
19856     {
19857         Roo.log('onDecrementHours');
19858         this.time = this.time.add(Date.HOUR, -1);
19859         this.update();
19860     },
19861     
19862     onIncrementMinutes: function()
19863     {
19864         Roo.log('onIncrementMinutes');
19865         this.time = this.time.add(Date.MINUTE, 1);
19866         this.update();
19867     },
19868     
19869     onDecrementMinutes: function()
19870     {
19871         Roo.log('onDecrementMinutes');
19872         this.time = this.time.add(Date.MINUTE, -1);
19873         this.update();
19874     },
19875     
19876     onTogglePeriod: function()
19877     {
19878         Roo.log('onTogglePeriod');
19879         this.time = this.time.add(Date.HOUR, 12);
19880         this.update();
19881     }
19882     
19883    
19884 });
19885
19886 Roo.apply(Roo.bootstrap.TimeField,  {
19887     
19888     content : {
19889         tag: 'tbody',
19890         cn: [
19891             {
19892                 tag: 'tr',
19893                 cn: [
19894                 {
19895                     tag: 'td',
19896                     colspan: '7'
19897                 }
19898                 ]
19899             }
19900         ]
19901     },
19902     
19903     footer : {
19904         tag: 'tfoot',
19905         cn: [
19906             {
19907                 tag: 'tr',
19908                 cn: [
19909                 {
19910                     tag: 'th',
19911                     colspan: '7',
19912                     cls: '',
19913                     cn: [
19914                         {
19915                             tag: 'button',
19916                             cls: 'btn btn-info ok',
19917                             html: 'OK'
19918                         }
19919                     ]
19920                 }
19921
19922                 ]
19923             }
19924         ]
19925     }
19926 });
19927
19928 Roo.apply(Roo.bootstrap.TimeField,  {
19929   
19930     template : {
19931         tag: 'div',
19932         cls: 'datepicker dropdown-menu',
19933         cn: [
19934             {
19935                 tag: 'div',
19936                 cls: 'datepicker-time',
19937                 cn: [
19938                 {
19939                     tag: 'table',
19940                     cls: 'table-condensed',
19941                     cn:[
19942                     Roo.bootstrap.TimeField.content,
19943                     Roo.bootstrap.TimeField.footer
19944                     ]
19945                 }
19946                 ]
19947             }
19948         ]
19949     }
19950 });
19951
19952  
19953
19954  /*
19955  * - LGPL
19956  *
19957  * MonthField
19958  * 
19959  */
19960
19961 /**
19962  * @class Roo.bootstrap.MonthField
19963  * @extends Roo.bootstrap.Input
19964  * Bootstrap MonthField class
19965  * 
19966  * @cfg {String} language default en
19967  * 
19968  * @constructor
19969  * Create a new MonthField
19970  * @param {Object} config The config object
19971  */
19972
19973 Roo.bootstrap.MonthField = function(config){
19974     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19975     
19976     this.addEvents({
19977         /**
19978          * @event show
19979          * Fires when this field show.
19980          * @param {Roo.bootstrap.MonthField} this
19981          * @param {Mixed} date The date value
19982          */
19983         show : true,
19984         /**
19985          * @event show
19986          * Fires when this field hide.
19987          * @param {Roo.bootstrap.MonthField} this
19988          * @param {Mixed} date The date value
19989          */
19990         hide : true,
19991         /**
19992          * @event select
19993          * Fires when select a date.
19994          * @param {Roo.bootstrap.MonthField} this
19995          * @param {String} oldvalue The old value
19996          * @param {String} newvalue The new value
19997          */
19998         select : true
19999     });
20000 };
20001
20002 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20003     
20004     onRender: function(ct, position)
20005     {
20006         
20007         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20008         
20009         this.language = this.language || 'en';
20010         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20011         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20012         
20013         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20014         this.isInline = false;
20015         this.isInput = true;
20016         this.component = this.el.select('.add-on', true).first() || false;
20017         this.component = (this.component && this.component.length === 0) ? false : this.component;
20018         this.hasInput = this.component && this.inputEL().length;
20019         
20020         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20021         
20022         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20023         
20024         this.picker().on('mousedown', this.onMousedown, this);
20025         this.picker().on('click', this.onClick, this);
20026         
20027         this.picker().addClass('datepicker-dropdown');
20028         
20029         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20030             v.setStyle('width', '189px');
20031         });
20032         
20033         this.fillMonths();
20034         
20035         this.update();
20036         
20037         if(this.isInline) {
20038             this.show();
20039         }
20040         
20041     },
20042     
20043     setValue: function(v, suppressEvent)
20044     {   
20045         var o = this.getValue();
20046         
20047         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20048         
20049         this.update();
20050
20051         if(suppressEvent !== true){
20052             this.fireEvent('select', this, o, v);
20053         }
20054         
20055     },
20056     
20057     getValue: function()
20058     {
20059         return this.value;
20060     },
20061     
20062     onClick: function(e) 
20063     {
20064         e.stopPropagation();
20065         e.preventDefault();
20066         
20067         var target = e.getTarget();
20068         
20069         if(target.nodeName.toLowerCase() === 'i'){
20070             target = Roo.get(target).dom.parentNode;
20071         }
20072         
20073         var nodeName = target.nodeName;
20074         var className = target.className;
20075         var html = target.innerHTML;
20076         
20077         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20078             return;
20079         }
20080         
20081         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20082         
20083         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20084         
20085         this.hide();
20086                         
20087     },
20088     
20089     picker : function()
20090     {
20091         return this.pickerEl;
20092     },
20093     
20094     fillMonths: function()
20095     {    
20096         var i = 0;
20097         var months = this.picker().select('>.datepicker-months td', true).first();
20098         
20099         months.dom.innerHTML = '';
20100         
20101         while (i < 12) {
20102             var month = {
20103                 tag: 'span',
20104                 cls: 'month',
20105                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20106             };
20107             
20108             months.createChild(month);
20109         }
20110         
20111     },
20112     
20113     update: function()
20114     {
20115         var _this = this;
20116         
20117         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20118             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20119         }
20120         
20121         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20122             e.removeClass('active');
20123             
20124             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20125                 e.addClass('active');
20126             }
20127         })
20128     },
20129     
20130     place: function()
20131     {
20132         if(this.isInline) {
20133             return;
20134         }
20135         
20136         this.picker().removeClass(['bottom', 'top']);
20137         
20138         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20139             /*
20140              * place to the top of element!
20141              *
20142              */
20143             
20144             this.picker().addClass('top');
20145             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20146             
20147             return;
20148         }
20149         
20150         this.picker().addClass('bottom');
20151         
20152         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20153     },
20154     
20155     onFocus : function()
20156     {
20157         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20158         this.show();
20159     },
20160     
20161     onBlur : function()
20162     {
20163         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20164         
20165         var d = this.inputEl().getValue();
20166         
20167         this.setValue(d);
20168                 
20169         this.hide();
20170     },
20171     
20172     show : function()
20173     {
20174         this.picker().show();
20175         this.picker().select('>.datepicker-months', true).first().show();
20176         this.update();
20177         this.place();
20178         
20179         this.fireEvent('show', this, this.date);
20180     },
20181     
20182     hide : function()
20183     {
20184         if(this.isInline) {
20185             return;
20186         }
20187         this.picker().hide();
20188         this.fireEvent('hide', this, this.date);
20189         
20190     },
20191     
20192     onMousedown: function(e)
20193     {
20194         e.stopPropagation();
20195         e.preventDefault();
20196     },
20197     
20198     keyup: function(e)
20199     {
20200         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20201         this.update();
20202     },
20203
20204     fireKey: function(e)
20205     {
20206         if (!this.picker().isVisible()){
20207             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20208                 this.show();
20209             }
20210             return;
20211         }
20212         
20213         var dir;
20214         
20215         switch(e.keyCode){
20216             case 27: // escape
20217                 this.hide();
20218                 e.preventDefault();
20219                 break;
20220             case 37: // left
20221             case 39: // right
20222                 dir = e.keyCode == 37 ? -1 : 1;
20223                 
20224                 this.vIndex = this.vIndex + dir;
20225                 
20226                 if(this.vIndex < 0){
20227                     this.vIndex = 0;
20228                 }
20229                 
20230                 if(this.vIndex > 11){
20231                     this.vIndex = 11;
20232                 }
20233                 
20234                 if(isNaN(this.vIndex)){
20235                     this.vIndex = 0;
20236                 }
20237                 
20238                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20239                 
20240                 break;
20241             case 38: // up
20242             case 40: // down
20243                 
20244                 dir = e.keyCode == 38 ? -1 : 1;
20245                 
20246                 this.vIndex = this.vIndex + dir * 4;
20247                 
20248                 if(this.vIndex < 0){
20249                     this.vIndex = 0;
20250                 }
20251                 
20252                 if(this.vIndex > 11){
20253                     this.vIndex = 11;
20254                 }
20255                 
20256                 if(isNaN(this.vIndex)){
20257                     this.vIndex = 0;
20258                 }
20259                 
20260                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20261                 break;
20262                 
20263             case 13: // enter
20264                 
20265                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20266                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20267                 }
20268                 
20269                 this.hide();
20270                 e.preventDefault();
20271                 break;
20272             case 9: // tab
20273                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20274                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20275                 }
20276                 this.hide();
20277                 break;
20278             case 16: // shift
20279             case 17: // ctrl
20280             case 18: // alt
20281                 break;
20282             default :
20283                 this.hide();
20284                 
20285         }
20286     },
20287     
20288     remove: function() 
20289     {
20290         this.picker().remove();
20291     }
20292    
20293 });
20294
20295 Roo.apply(Roo.bootstrap.MonthField,  {
20296     
20297     content : {
20298         tag: 'tbody',
20299         cn: [
20300         {
20301             tag: 'tr',
20302             cn: [
20303             {
20304                 tag: 'td',
20305                 colspan: '7'
20306             }
20307             ]
20308         }
20309         ]
20310     },
20311     
20312     dates:{
20313         en: {
20314             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20315             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20316         }
20317     }
20318 });
20319
20320 Roo.apply(Roo.bootstrap.MonthField,  {
20321   
20322     template : {
20323         tag: 'div',
20324         cls: 'datepicker dropdown-menu roo-dynamic',
20325         cn: [
20326             {
20327                 tag: 'div',
20328                 cls: 'datepicker-months',
20329                 cn: [
20330                 {
20331                     tag: 'table',
20332                     cls: 'table-condensed',
20333                     cn:[
20334                         Roo.bootstrap.DateField.content
20335                     ]
20336                 }
20337                 ]
20338             }
20339         ]
20340     }
20341 });
20342
20343  
20344
20345  
20346  /*
20347  * - LGPL
20348  *
20349  * CheckBox
20350  * 
20351  */
20352
20353 /**
20354  * @class Roo.bootstrap.CheckBox
20355  * @extends Roo.bootstrap.Input
20356  * Bootstrap CheckBox class
20357  * 
20358  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20359  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20360  * @cfg {String} boxLabel The text that appears beside the checkbox
20361  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20362  * @cfg {Boolean} checked initnal the element
20363  * @cfg {Boolean} inline inline the element (default false)
20364  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20365  * @cfg {String} tooltip label tooltip
20366  * 
20367  * @constructor
20368  * Create a new CheckBox
20369  * @param {Object} config The config object
20370  */
20371
20372 Roo.bootstrap.CheckBox = function(config){
20373     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20374    
20375     this.addEvents({
20376         /**
20377         * @event check
20378         * Fires when the element is checked or unchecked.
20379         * @param {Roo.bootstrap.CheckBox} this This input
20380         * @param {Boolean} checked The new checked value
20381         */
20382        check : true,
20383        /**
20384         * @event click
20385         * Fires when the element is click.
20386         * @param {Roo.bootstrap.CheckBox} this This input
20387         */
20388        click : true
20389     });
20390     
20391 };
20392
20393 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20394   
20395     inputType: 'checkbox',
20396     inputValue: 1,
20397     valueOff: 0,
20398     boxLabel: false,
20399     checked: false,
20400     weight : false,
20401     inline: false,
20402     tooltip : '',
20403     
20404     getAutoCreate : function()
20405     {
20406         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20407         
20408         var id = Roo.id();
20409         
20410         var cfg = {};
20411         
20412         cfg.cls = 'form-group ' + this.inputType; //input-group
20413         
20414         if(this.inline){
20415             cfg.cls += ' ' + this.inputType + '-inline';
20416         }
20417         
20418         var input =  {
20419             tag: 'input',
20420             id : id,
20421             type : this.inputType,
20422             value : this.inputValue,
20423             cls : 'roo-' + this.inputType, //'form-box',
20424             placeholder : this.placeholder || ''
20425             
20426         };
20427         
20428         if(this.inputType != 'radio'){
20429             var hidden =  {
20430                 tag: 'input',
20431                 type : 'hidden',
20432                 cls : 'roo-hidden-value',
20433                 value : this.checked ? this.inputValue : this.valueOff
20434             };
20435         }
20436         
20437             
20438         if (this.weight) { // Validity check?
20439             cfg.cls += " " + this.inputType + "-" + this.weight;
20440         }
20441         
20442         if (this.disabled) {
20443             input.disabled=true;
20444         }
20445         
20446         if(this.checked){
20447             input.checked = this.checked;
20448         }
20449         
20450         if (this.name) {
20451             
20452             input.name = this.name;
20453             
20454             if(this.inputType != 'radio'){
20455                 hidden.name = this.name;
20456                 input.name = '_hidden_' + this.name;
20457             }
20458         }
20459         
20460         if (this.size) {
20461             input.cls += ' input-' + this.size;
20462         }
20463         
20464         var settings=this;
20465         
20466         ['xs','sm','md','lg'].map(function(size){
20467             if (settings[size]) {
20468                 cfg.cls += ' col-' + size + '-' + settings[size];
20469             }
20470         });
20471         
20472         var inputblock = input;
20473          
20474         if (this.before || this.after) {
20475             
20476             inputblock = {
20477                 cls : 'input-group',
20478                 cn :  [] 
20479             };
20480             
20481             if (this.before) {
20482                 inputblock.cn.push({
20483                     tag :'span',
20484                     cls : 'input-group-addon',
20485                     html : this.before
20486                 });
20487             }
20488             
20489             inputblock.cn.push(input);
20490             
20491             if(this.inputType != 'radio'){
20492                 inputblock.cn.push(hidden);
20493             }
20494             
20495             if (this.after) {
20496                 inputblock.cn.push({
20497                     tag :'span',
20498                     cls : 'input-group-addon',
20499                     html : this.after
20500                 });
20501             }
20502             
20503         }
20504         
20505         if (align ==='left' && this.fieldLabel.length) {
20506 //                Roo.log("left and has label");
20507             cfg.cn = [
20508                 {
20509                     tag: 'label',
20510                     'for' :  id,
20511                     cls : 'control-label',
20512                     html : this.fieldLabel
20513                 },
20514                 {
20515                     cls : "", 
20516                     cn: [
20517                         inputblock
20518                     ]
20519                 }
20520             ];
20521             
20522             if(this.labelWidth > 12){
20523                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20524             }
20525             
20526             if(this.labelWidth < 13 && this.labelmd == 0){
20527                 this.labelmd = this.labelWidth;
20528             }
20529             
20530             if(this.labellg > 0){
20531                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20532                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20533             }
20534             
20535             if(this.labelmd > 0){
20536                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20537                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20538             }
20539             
20540             if(this.labelsm > 0){
20541                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20542                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20543             }
20544             
20545             if(this.labelxs > 0){
20546                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20547                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20548             }
20549             
20550         } else if ( this.fieldLabel.length) {
20551 //                Roo.log(" label");
20552                 cfg.cn = [
20553                    
20554                     {
20555                         tag: this.boxLabel ? 'span' : 'label',
20556                         'for': id,
20557                         cls: 'control-label box-input-label',
20558                         //cls : 'input-group-addon',
20559                         html : this.fieldLabel
20560                     },
20561                     
20562                     inputblock
20563                     
20564                 ];
20565
20566         } else {
20567             
20568 //                Roo.log(" no label && no align");
20569                 cfg.cn = [  inputblock ] ;
20570                 
20571                 
20572         }
20573         
20574         if(this.boxLabel){
20575              var boxLabelCfg = {
20576                 tag: 'label',
20577                 //'for': id, // box label is handled by onclick - so no for...
20578                 cls: 'box-label',
20579                 html: this.boxLabel
20580             };
20581             
20582             if(this.tooltip){
20583                 boxLabelCfg.tooltip = this.tooltip;
20584             }
20585              
20586             cfg.cn.push(boxLabelCfg);
20587         }
20588         
20589         if(this.inputType != 'radio'){
20590             cfg.cn.push(hidden);
20591         }
20592         
20593         return cfg;
20594         
20595     },
20596     
20597     /**
20598      * return the real input element.
20599      */
20600     inputEl: function ()
20601     {
20602         return this.el.select('input.roo-' + this.inputType,true).first();
20603     },
20604     hiddenEl: function ()
20605     {
20606         return this.el.select('input.roo-hidden-value',true).first();
20607     },
20608     
20609     labelEl: function()
20610     {
20611         return this.el.select('label.control-label',true).first();
20612     },
20613     /* depricated... */
20614     
20615     label: function()
20616     {
20617         return this.labelEl();
20618     },
20619     
20620     boxLabelEl: function()
20621     {
20622         return this.el.select('label.box-label',true).first();
20623     },
20624     
20625     initEvents : function()
20626     {
20627 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20628         
20629         this.inputEl().on('click', this.onClick,  this);
20630         
20631         if (this.boxLabel) { 
20632             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20633         }
20634         
20635         this.startValue = this.getValue();
20636         
20637         if(this.groupId){
20638             Roo.bootstrap.CheckBox.register(this);
20639         }
20640     },
20641     
20642     onClick : function(e)
20643     {   
20644         if(this.fireEvent('click', this, e) !== false){
20645             this.setChecked(!this.checked);
20646         }
20647         
20648     },
20649     
20650     setChecked : function(state,suppressEvent)
20651     {
20652         this.startValue = this.getValue();
20653
20654         if(this.inputType == 'radio'){
20655             
20656             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20657                 e.dom.checked = false;
20658             });
20659             
20660             this.inputEl().dom.checked = true;
20661             
20662             this.inputEl().dom.value = this.inputValue;
20663             
20664             if(suppressEvent !== true){
20665                 this.fireEvent('check', this, true);
20666             }
20667             
20668             this.validate();
20669             
20670             return;
20671         }
20672         
20673         this.checked = state;
20674         
20675         this.inputEl().dom.checked = state;
20676         
20677         
20678         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20679         
20680         if(suppressEvent !== true){
20681             this.fireEvent('check', this, state);
20682         }
20683         
20684         this.validate();
20685     },
20686     
20687     getValue : function()
20688     {
20689         if(this.inputType == 'radio'){
20690             return this.getGroupValue();
20691         }
20692         
20693         return this.hiddenEl().dom.value;
20694         
20695     },
20696     
20697     getGroupValue : function()
20698     {
20699         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20700             return '';
20701         }
20702         
20703         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20704     },
20705     
20706     setValue : function(v,suppressEvent)
20707     {
20708         if(this.inputType == 'radio'){
20709             this.setGroupValue(v, suppressEvent);
20710             return;
20711         }
20712         
20713         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20714         
20715         this.validate();
20716     },
20717     
20718     setGroupValue : function(v, suppressEvent)
20719     {
20720         this.startValue = this.getValue();
20721         
20722         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20723             e.dom.checked = false;
20724             
20725             if(e.dom.value == v){
20726                 e.dom.checked = true;
20727             }
20728         });
20729         
20730         if(suppressEvent !== true){
20731             this.fireEvent('check', this, true);
20732         }
20733
20734         this.validate();
20735         
20736         return;
20737     },
20738     
20739     validate : function()
20740     {
20741         if(this.getVisibilityEl().hasClass('hidden')){
20742             return true;
20743         }
20744         
20745         if(
20746                 this.disabled || 
20747                 (this.inputType == 'radio' && this.validateRadio()) ||
20748                 (this.inputType == 'checkbox' && this.validateCheckbox())
20749         ){
20750             this.markValid();
20751             return true;
20752         }
20753         
20754         this.markInvalid();
20755         return false;
20756     },
20757     
20758     validateRadio : function()
20759     {
20760         if(this.getVisibilityEl().hasClass('hidden')){
20761             return true;
20762         }
20763         
20764         if(this.allowBlank){
20765             return true;
20766         }
20767         
20768         var valid = false;
20769         
20770         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20771             if(!e.dom.checked){
20772                 return;
20773             }
20774             
20775             valid = true;
20776             
20777             return false;
20778         });
20779         
20780         return valid;
20781     },
20782     
20783     validateCheckbox : function()
20784     {
20785         if(!this.groupId){
20786             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20787             //return (this.getValue() == this.inputValue) ? true : false;
20788         }
20789         
20790         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20791         
20792         if(!group){
20793             return false;
20794         }
20795         
20796         var r = false;
20797         
20798         for(var i in group){
20799             if(group[i].el.isVisible(true)){
20800                 r = false;
20801                 break;
20802             }
20803             
20804             r = true;
20805         }
20806         
20807         for(var i in group){
20808             if(r){
20809                 break;
20810             }
20811             
20812             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20813         }
20814         
20815         return r;
20816     },
20817     
20818     /**
20819      * Mark this field as valid
20820      */
20821     markValid : function()
20822     {
20823         var _this = this;
20824         
20825         this.fireEvent('valid', this);
20826         
20827         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20828         
20829         if(this.groupId){
20830             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20831         }
20832         
20833         if(label){
20834             label.markValid();
20835         }
20836
20837         if(this.inputType == 'radio'){
20838             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20839                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20840                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20841             });
20842             
20843             return;
20844         }
20845
20846         if(!this.groupId){
20847             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20848             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20849             return;
20850         }
20851         
20852         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20853         
20854         if(!group){
20855             return;
20856         }
20857         
20858         for(var i in group){
20859             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20860             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20861         }
20862     },
20863     
20864      /**
20865      * Mark this field as invalid
20866      * @param {String} msg The validation message
20867      */
20868     markInvalid : function(msg)
20869     {
20870         if(this.allowBlank){
20871             return;
20872         }
20873         
20874         var _this = this;
20875         
20876         this.fireEvent('invalid', this, msg);
20877         
20878         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20879         
20880         if(this.groupId){
20881             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20882         }
20883         
20884         if(label){
20885             label.markInvalid();
20886         }
20887             
20888         if(this.inputType == 'radio'){
20889             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20890                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20891                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20892             });
20893             
20894             return;
20895         }
20896         
20897         if(!this.groupId){
20898             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20899             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20900             return;
20901         }
20902         
20903         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20904         
20905         if(!group){
20906             return;
20907         }
20908         
20909         for(var i in group){
20910             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20911             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20912         }
20913         
20914     },
20915     
20916     clearInvalid : function()
20917     {
20918         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20919         
20920         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20921         
20922         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20923         
20924         if (label && label.iconEl) {
20925             label.iconEl.removeClass(label.validClass);
20926             label.iconEl.removeClass(label.invalidClass);
20927         }
20928     },
20929     
20930     disable : function()
20931     {
20932         if(this.inputType != 'radio'){
20933             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20934             return;
20935         }
20936         
20937         var _this = this;
20938         
20939         if(this.rendered){
20940             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20941                 _this.getActionEl().addClass(this.disabledClass);
20942                 e.dom.disabled = true;
20943             });
20944         }
20945         
20946         this.disabled = true;
20947         this.fireEvent("disable", this);
20948         return this;
20949     },
20950
20951     enable : function()
20952     {
20953         if(this.inputType != 'radio'){
20954             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20955             return;
20956         }
20957         
20958         var _this = this;
20959         
20960         if(this.rendered){
20961             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20962                 _this.getActionEl().removeClass(this.disabledClass);
20963                 e.dom.disabled = false;
20964             });
20965         }
20966         
20967         this.disabled = false;
20968         this.fireEvent("enable", this);
20969         return this;
20970     },
20971     
20972     setBoxLabel : function(v)
20973     {
20974         this.boxLabel = v;
20975         
20976         if(this.rendered){
20977             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20978         }
20979     }
20980
20981 });
20982
20983 Roo.apply(Roo.bootstrap.CheckBox, {
20984     
20985     groups: {},
20986     
20987      /**
20988     * register a CheckBox Group
20989     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20990     */
20991     register : function(checkbox)
20992     {
20993         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20994             this.groups[checkbox.groupId] = {};
20995         }
20996         
20997         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20998             return;
20999         }
21000         
21001         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21002         
21003     },
21004     /**
21005     * fetch a CheckBox Group based on the group ID
21006     * @param {string} the group ID
21007     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21008     */
21009     get: function(groupId) {
21010         if (typeof(this.groups[groupId]) == 'undefined') {
21011             return false;
21012         }
21013         
21014         return this.groups[groupId] ;
21015     }
21016     
21017     
21018 });
21019 /*
21020  * - LGPL
21021  *
21022  * RadioItem
21023  * 
21024  */
21025
21026 /**
21027  * @class Roo.bootstrap.Radio
21028  * @extends Roo.bootstrap.Component
21029  * Bootstrap Radio class
21030  * @cfg {String} boxLabel - the label associated
21031  * @cfg {String} value - the value of radio
21032  * 
21033  * @constructor
21034  * Create a new Radio
21035  * @param {Object} config The config object
21036  */
21037 Roo.bootstrap.Radio = function(config){
21038     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21039     
21040 };
21041
21042 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21043     
21044     boxLabel : '',
21045     
21046     value : '',
21047     
21048     getAutoCreate : function()
21049     {
21050         var cfg = {
21051             tag : 'div',
21052             cls : 'form-group radio',
21053             cn : [
21054                 {
21055                     tag : 'label',
21056                     cls : 'box-label',
21057                     html : this.boxLabel
21058                 }
21059             ]
21060         };
21061         
21062         return cfg;
21063     },
21064     
21065     initEvents : function() 
21066     {
21067         this.parent().register(this);
21068         
21069         this.el.on('click', this.onClick, this);
21070         
21071     },
21072     
21073     onClick : function(e)
21074     {
21075         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21076             this.setChecked(true);
21077         }
21078     },
21079     
21080     setChecked : function(state, suppressEvent)
21081     {
21082         this.parent().setValue(this.value, suppressEvent);
21083         
21084     },
21085     
21086     setBoxLabel : function(v)
21087     {
21088         this.boxLabel = v;
21089         
21090         if(this.rendered){
21091             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21092         }
21093     }
21094     
21095 });
21096  
21097
21098  /*
21099  * - LGPL
21100  *
21101  * Input
21102  * 
21103  */
21104
21105 /**
21106  * @class Roo.bootstrap.SecurePass
21107  * @extends Roo.bootstrap.Input
21108  * Bootstrap SecurePass class
21109  *
21110  * 
21111  * @constructor
21112  * Create a new SecurePass
21113  * @param {Object} config The config object
21114  */
21115  
21116 Roo.bootstrap.SecurePass = function (config) {
21117     // these go here, so the translation tool can replace them..
21118     this.errors = {
21119         PwdEmpty: "Please type a password, and then retype it to confirm.",
21120         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21121         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21122         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21123         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21124         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21125         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21126         TooWeak: "Your password is Too Weak."
21127     },
21128     this.meterLabel = "Password strength:";
21129     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21130     this.meterClass = [
21131         "roo-password-meter-tooweak", 
21132         "roo-password-meter-weak", 
21133         "roo-password-meter-medium", 
21134         "roo-password-meter-strong", 
21135         "roo-password-meter-grey"
21136     ];
21137     
21138     this.errors = {};
21139     
21140     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21141 }
21142
21143 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21144     /**
21145      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21146      * {
21147      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21148      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21149      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21150      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21151      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21152      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21153      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21154      * })
21155      */
21156     // private
21157     
21158     meterWidth: 300,
21159     errorMsg :'',    
21160     errors: false,
21161     imageRoot: '/',
21162     /**
21163      * @cfg {String/Object} Label for the strength meter (defaults to
21164      * 'Password strength:')
21165      */
21166     // private
21167     meterLabel: '',
21168     /**
21169      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21170      * ['Weak', 'Medium', 'Strong'])
21171      */
21172     // private    
21173     pwdStrengths: false,    
21174     // private
21175     strength: 0,
21176     // private
21177     _lastPwd: null,
21178     // private
21179     kCapitalLetter: 0,
21180     kSmallLetter: 1,
21181     kDigit: 2,
21182     kPunctuation: 3,
21183     
21184     insecure: false,
21185     // private
21186     initEvents: function ()
21187     {
21188         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21189
21190         if (this.el.is('input[type=password]') && Roo.isSafari) {
21191             this.el.on('keydown', this.SafariOnKeyDown, this);
21192         }
21193
21194         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21195     },
21196     // private
21197     onRender: function (ct, position)
21198     {
21199         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21200         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21201         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21202
21203         this.trigger.createChild({
21204                    cn: [
21205                     {
21206                     //id: 'PwdMeter',
21207                     tag: 'div',
21208                     cls: 'roo-password-meter-grey col-xs-12',
21209                     style: {
21210                         //width: 0,
21211                         //width: this.meterWidth + 'px'                                                
21212                         }
21213                     },
21214                     {                            
21215                          cls: 'roo-password-meter-text'                          
21216                     }
21217                 ]            
21218         });
21219
21220          
21221         if (this.hideTrigger) {
21222             this.trigger.setDisplayed(false);
21223         }
21224         this.setSize(this.width || '', this.height || '');
21225     },
21226     // private
21227     onDestroy: function ()
21228     {
21229         if (this.trigger) {
21230             this.trigger.removeAllListeners();
21231             this.trigger.remove();
21232         }
21233         if (this.wrap) {
21234             this.wrap.remove();
21235         }
21236         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21237     },
21238     // private
21239     checkStrength: function ()
21240     {
21241         var pwd = this.inputEl().getValue();
21242         if (pwd == this._lastPwd) {
21243             return;
21244         }
21245
21246         var strength;
21247         if (this.ClientSideStrongPassword(pwd)) {
21248             strength = 3;
21249         } else if (this.ClientSideMediumPassword(pwd)) {
21250             strength = 2;
21251         } else if (this.ClientSideWeakPassword(pwd)) {
21252             strength = 1;
21253         } else {
21254             strength = 0;
21255         }
21256         
21257         Roo.log('strength1: ' + strength);
21258         
21259         //var pm = this.trigger.child('div/div/div').dom;
21260         var pm = this.trigger.child('div/div');
21261         pm.removeClass(this.meterClass);
21262         pm.addClass(this.meterClass[strength]);
21263                 
21264         
21265         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21266                 
21267         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21268         
21269         this._lastPwd = pwd;
21270     },
21271     reset: function ()
21272     {
21273         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21274         
21275         this._lastPwd = '';
21276         
21277         var pm = this.trigger.child('div/div');
21278         pm.removeClass(this.meterClass);
21279         pm.addClass('roo-password-meter-grey');        
21280         
21281         
21282         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21283         
21284         pt.innerHTML = '';
21285         this.inputEl().dom.type='password';
21286     },
21287     // private
21288     validateValue: function (value)
21289     {
21290         
21291         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21292             return false;
21293         }
21294         if (value.length == 0) {
21295             if (this.allowBlank) {
21296                 this.clearInvalid();
21297                 return true;
21298             }
21299
21300             this.markInvalid(this.errors.PwdEmpty);
21301             this.errorMsg = this.errors.PwdEmpty;
21302             return false;
21303         }
21304         
21305         if(this.insecure){
21306             return true;
21307         }
21308         
21309         if ('[\x21-\x7e]*'.match(value)) {
21310             this.markInvalid(this.errors.PwdBadChar);
21311             this.errorMsg = this.errors.PwdBadChar;
21312             return false;
21313         }
21314         if (value.length < 6) {
21315             this.markInvalid(this.errors.PwdShort);
21316             this.errorMsg = this.errors.PwdShort;
21317             return false;
21318         }
21319         if (value.length > 16) {
21320             this.markInvalid(this.errors.PwdLong);
21321             this.errorMsg = this.errors.PwdLong;
21322             return false;
21323         }
21324         var strength;
21325         if (this.ClientSideStrongPassword(value)) {
21326             strength = 3;
21327         } else if (this.ClientSideMediumPassword(value)) {
21328             strength = 2;
21329         } else if (this.ClientSideWeakPassword(value)) {
21330             strength = 1;
21331         } else {
21332             strength = 0;
21333         }
21334
21335         
21336         if (strength < 2) {
21337             //this.markInvalid(this.errors.TooWeak);
21338             this.errorMsg = this.errors.TooWeak;
21339             //return false;
21340         }
21341         
21342         
21343         console.log('strength2: ' + strength);
21344         
21345         //var pm = this.trigger.child('div/div/div').dom;
21346         
21347         var pm = this.trigger.child('div/div');
21348         pm.removeClass(this.meterClass);
21349         pm.addClass(this.meterClass[strength]);
21350                 
21351         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21352                 
21353         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21354         
21355         this.errorMsg = ''; 
21356         return true;
21357     },
21358     // private
21359     CharacterSetChecks: function (type)
21360     {
21361         this.type = type;
21362         this.fResult = false;
21363     },
21364     // private
21365     isctype: function (character, type)
21366     {
21367         switch (type) {  
21368             case this.kCapitalLetter:
21369                 if (character >= 'A' && character <= 'Z') {
21370                     return true;
21371                 }
21372                 break;
21373             
21374             case this.kSmallLetter:
21375                 if (character >= 'a' && character <= 'z') {
21376                     return true;
21377                 }
21378                 break;
21379             
21380             case this.kDigit:
21381                 if (character >= '0' && character <= '9') {
21382                     return true;
21383                 }
21384                 break;
21385             
21386             case this.kPunctuation:
21387                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21388                     return true;
21389                 }
21390                 break;
21391             
21392             default:
21393                 return false;
21394         }
21395
21396     },
21397     // private
21398     IsLongEnough: function (pwd, size)
21399     {
21400         return !(pwd == null || isNaN(size) || pwd.length < size);
21401     },
21402     // private
21403     SpansEnoughCharacterSets: function (word, nb)
21404     {
21405         if (!this.IsLongEnough(word, nb))
21406         {
21407             return false;
21408         }
21409
21410         var characterSetChecks = new Array(
21411             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21412             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21413         );
21414         
21415         for (var index = 0; index < word.length; ++index) {
21416             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21417                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21418                     characterSetChecks[nCharSet].fResult = true;
21419                     break;
21420                 }
21421             }
21422         }
21423
21424         var nCharSets = 0;
21425         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21426             if (characterSetChecks[nCharSet].fResult) {
21427                 ++nCharSets;
21428             }
21429         }
21430
21431         if (nCharSets < nb) {
21432             return false;
21433         }
21434         return true;
21435     },
21436     // private
21437     ClientSideStrongPassword: function (pwd)
21438     {
21439         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21440     },
21441     // private
21442     ClientSideMediumPassword: function (pwd)
21443     {
21444         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21445     },
21446     // private
21447     ClientSideWeakPassword: function (pwd)
21448     {
21449         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21450     }
21451           
21452 })//<script type="text/javascript">
21453
21454 /*
21455  * Based  Ext JS Library 1.1.1
21456  * Copyright(c) 2006-2007, Ext JS, LLC.
21457  * LGPL
21458  *
21459  */
21460  
21461 /**
21462  * @class Roo.HtmlEditorCore
21463  * @extends Roo.Component
21464  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21465  *
21466  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21467  */
21468
21469 Roo.HtmlEditorCore = function(config){
21470     
21471     
21472     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21473     
21474     
21475     this.addEvents({
21476         /**
21477          * @event initialize
21478          * Fires when the editor is fully initialized (including the iframe)
21479          * @param {Roo.HtmlEditorCore} this
21480          */
21481         initialize: true,
21482         /**
21483          * @event activate
21484          * Fires when the editor is first receives the focus. Any insertion must wait
21485          * until after this event.
21486          * @param {Roo.HtmlEditorCore} this
21487          */
21488         activate: true,
21489          /**
21490          * @event beforesync
21491          * Fires before the textarea is updated with content from the editor iframe. Return false
21492          * to cancel the sync.
21493          * @param {Roo.HtmlEditorCore} this
21494          * @param {String} html
21495          */
21496         beforesync: true,
21497          /**
21498          * @event beforepush
21499          * Fires before the iframe editor is updated with content from the textarea. Return false
21500          * to cancel the push.
21501          * @param {Roo.HtmlEditorCore} this
21502          * @param {String} html
21503          */
21504         beforepush: true,
21505          /**
21506          * @event sync
21507          * Fires when the textarea is updated with content from the editor iframe.
21508          * @param {Roo.HtmlEditorCore} this
21509          * @param {String} html
21510          */
21511         sync: true,
21512          /**
21513          * @event push
21514          * Fires when the iframe editor is updated with content from the textarea.
21515          * @param {Roo.HtmlEditorCore} this
21516          * @param {String} html
21517          */
21518         push: true,
21519         
21520         /**
21521          * @event editorevent
21522          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21523          * @param {Roo.HtmlEditorCore} this
21524          */
21525         editorevent: true
21526         
21527     });
21528     
21529     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21530     
21531     // defaults : white / black...
21532     this.applyBlacklists();
21533     
21534     
21535     
21536 };
21537
21538
21539 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21540
21541
21542      /**
21543      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21544      */
21545     
21546     owner : false,
21547     
21548      /**
21549      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21550      *                        Roo.resizable.
21551      */
21552     resizable : false,
21553      /**
21554      * @cfg {Number} height (in pixels)
21555      */   
21556     height: 300,
21557    /**
21558      * @cfg {Number} width (in pixels)
21559      */   
21560     width: 500,
21561     
21562     /**
21563      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21564      * 
21565      */
21566     stylesheets: false,
21567     
21568     // id of frame..
21569     frameId: false,
21570     
21571     // private properties
21572     validationEvent : false,
21573     deferHeight: true,
21574     initialized : false,
21575     activated : false,
21576     sourceEditMode : false,
21577     onFocus : Roo.emptyFn,
21578     iframePad:3,
21579     hideMode:'offsets',
21580     
21581     clearUp: true,
21582     
21583     // blacklist + whitelisted elements..
21584     black: false,
21585     white: false,
21586      
21587     bodyCls : '',
21588
21589     /**
21590      * Protected method that will not generally be called directly. It
21591      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21592      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21593      */
21594     getDocMarkup : function(){
21595         // body styles..
21596         var st = '';
21597         
21598         // inherit styels from page...?? 
21599         if (this.stylesheets === false) {
21600             
21601             Roo.get(document.head).select('style').each(function(node) {
21602                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21603             });
21604             
21605             Roo.get(document.head).select('link').each(function(node) { 
21606                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21607             });
21608             
21609         } else if (!this.stylesheets.length) {
21610                 // simple..
21611                 st = '<style type="text/css">' +
21612                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21613                    '</style>';
21614         } else { 
21615             st = '<style type="text/css">' +
21616                     this.stylesheets +
21617                 '</style>';
21618         }
21619         
21620         st +=  '<style type="text/css">' +
21621             'IMG { cursor: pointer } ' +
21622         '</style>';
21623
21624         var cls = 'roo-htmleditor-body';
21625         
21626         if(this.bodyCls.length){
21627             cls += ' ' + this.bodyCls;
21628         }
21629         
21630         return '<html><head>' + st  +
21631             //<style type="text/css">' +
21632             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21633             //'</style>' +
21634             ' </head><body class="' +  cls + '"></body></html>';
21635     },
21636
21637     // private
21638     onRender : function(ct, position)
21639     {
21640         var _t = this;
21641         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21642         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21643         
21644         
21645         this.el.dom.style.border = '0 none';
21646         this.el.dom.setAttribute('tabIndex', -1);
21647         this.el.addClass('x-hidden hide');
21648         
21649         
21650         
21651         if(Roo.isIE){ // fix IE 1px bogus margin
21652             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21653         }
21654        
21655         
21656         this.frameId = Roo.id();
21657         
21658          
21659         
21660         var iframe = this.owner.wrap.createChild({
21661             tag: 'iframe',
21662             cls: 'form-control', // bootstrap..
21663             id: this.frameId,
21664             name: this.frameId,
21665             frameBorder : 'no',
21666             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21667         }, this.el
21668         );
21669         
21670         
21671         this.iframe = iframe.dom;
21672
21673          this.assignDocWin();
21674         
21675         this.doc.designMode = 'on';
21676        
21677         this.doc.open();
21678         this.doc.write(this.getDocMarkup());
21679         this.doc.close();
21680
21681         
21682         var task = { // must defer to wait for browser to be ready
21683             run : function(){
21684                 //console.log("run task?" + this.doc.readyState);
21685                 this.assignDocWin();
21686                 if(this.doc.body || this.doc.readyState == 'complete'){
21687                     try {
21688                         this.doc.designMode="on";
21689                     } catch (e) {
21690                         return;
21691                     }
21692                     Roo.TaskMgr.stop(task);
21693                     this.initEditor.defer(10, this);
21694                 }
21695             },
21696             interval : 10,
21697             duration: 10000,
21698             scope: this
21699         };
21700         Roo.TaskMgr.start(task);
21701
21702     },
21703
21704     // private
21705     onResize : function(w, h)
21706     {
21707          Roo.log('resize: ' +w + ',' + h );
21708         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21709         if(!this.iframe){
21710             return;
21711         }
21712         if(typeof w == 'number'){
21713             
21714             this.iframe.style.width = w + 'px';
21715         }
21716         if(typeof h == 'number'){
21717             
21718             this.iframe.style.height = h + 'px';
21719             if(this.doc){
21720                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21721             }
21722         }
21723         
21724     },
21725
21726     /**
21727      * Toggles the editor between standard and source edit mode.
21728      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21729      */
21730     toggleSourceEdit : function(sourceEditMode){
21731         
21732         this.sourceEditMode = sourceEditMode === true;
21733         
21734         if(this.sourceEditMode){
21735  
21736             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21737             
21738         }else{
21739             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21740             //this.iframe.className = '';
21741             this.deferFocus();
21742         }
21743         //this.setSize(this.owner.wrap.getSize());
21744         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21745     },
21746
21747     
21748   
21749
21750     /**
21751      * Protected method that will not generally be called directly. If you need/want
21752      * custom HTML cleanup, this is the method you should override.
21753      * @param {String} html The HTML to be cleaned
21754      * return {String} The cleaned HTML
21755      */
21756     cleanHtml : function(html){
21757         html = String(html);
21758         if(html.length > 5){
21759             if(Roo.isSafari){ // strip safari nonsense
21760                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21761             }
21762         }
21763         if(html == '&nbsp;'){
21764             html = '';
21765         }
21766         return html;
21767     },
21768
21769     /**
21770      * HTML Editor -> Textarea
21771      * Protected method that will not generally be called directly. Syncs the contents
21772      * of the editor iframe with the textarea.
21773      */
21774     syncValue : function(){
21775         if(this.initialized){
21776             var bd = (this.doc.body || this.doc.documentElement);
21777             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21778             var html = bd.innerHTML;
21779             if(Roo.isSafari){
21780                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21781                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21782                 if(m && m[1]){
21783                     html = '<div style="'+m[0]+'">' + html + '</div>';
21784                 }
21785             }
21786             html = this.cleanHtml(html);
21787             // fix up the special chars.. normaly like back quotes in word...
21788             // however we do not want to do this with chinese..
21789             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21790                 var cc = b.charCodeAt();
21791                 if (
21792                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21793                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21794                     (cc >= 0xf900 && cc < 0xfb00 )
21795                 ) {
21796                         return b;
21797                 }
21798                 return "&#"+cc+";" 
21799             });
21800             if(this.owner.fireEvent('beforesync', this, html) !== false){
21801                 this.el.dom.value = html;
21802                 this.owner.fireEvent('sync', this, html);
21803             }
21804         }
21805     },
21806
21807     /**
21808      * Protected method that will not generally be called directly. Pushes the value of the textarea
21809      * into the iframe editor.
21810      */
21811     pushValue : function(){
21812         if(this.initialized){
21813             var v = this.el.dom.value.trim();
21814             
21815 //            if(v.length < 1){
21816 //                v = '&#160;';
21817 //            }
21818             
21819             if(this.owner.fireEvent('beforepush', this, v) !== false){
21820                 var d = (this.doc.body || this.doc.documentElement);
21821                 d.innerHTML = v;
21822                 this.cleanUpPaste();
21823                 this.el.dom.value = d.innerHTML;
21824                 this.owner.fireEvent('push', this, v);
21825             }
21826         }
21827     },
21828
21829     // private
21830     deferFocus : function(){
21831         this.focus.defer(10, this);
21832     },
21833
21834     // doc'ed in Field
21835     focus : function(){
21836         if(this.win && !this.sourceEditMode){
21837             this.win.focus();
21838         }else{
21839             this.el.focus();
21840         }
21841     },
21842     
21843     assignDocWin: function()
21844     {
21845         var iframe = this.iframe;
21846         
21847          if(Roo.isIE){
21848             this.doc = iframe.contentWindow.document;
21849             this.win = iframe.contentWindow;
21850         } else {
21851 //            if (!Roo.get(this.frameId)) {
21852 //                return;
21853 //            }
21854 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21855 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21856             
21857             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21858                 return;
21859             }
21860             
21861             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21862             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21863         }
21864     },
21865     
21866     // private
21867     initEditor : function(){
21868         //console.log("INIT EDITOR");
21869         this.assignDocWin();
21870         
21871         
21872         
21873         this.doc.designMode="on";
21874         this.doc.open();
21875         this.doc.write(this.getDocMarkup());
21876         this.doc.close();
21877         
21878         var dbody = (this.doc.body || this.doc.documentElement);
21879         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21880         // this copies styles from the containing element into thsi one..
21881         // not sure why we need all of this..
21882         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21883         
21884         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21885         //ss['background-attachment'] = 'fixed'; // w3c
21886         dbody.bgProperties = 'fixed'; // ie
21887         //Roo.DomHelper.applyStyles(dbody, ss);
21888         Roo.EventManager.on(this.doc, {
21889             //'mousedown': this.onEditorEvent,
21890             'mouseup': this.onEditorEvent,
21891             'dblclick': this.onEditorEvent,
21892             'click': this.onEditorEvent,
21893             'keyup': this.onEditorEvent,
21894             buffer:100,
21895             scope: this
21896         });
21897         if(Roo.isGecko){
21898             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21899         }
21900         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21901             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21902         }
21903         this.initialized = true;
21904
21905         this.owner.fireEvent('initialize', this);
21906         this.pushValue();
21907     },
21908
21909     // private
21910     onDestroy : function(){
21911         
21912         
21913         
21914         if(this.rendered){
21915             
21916             //for (var i =0; i < this.toolbars.length;i++) {
21917             //    // fixme - ask toolbars for heights?
21918             //    this.toolbars[i].onDestroy();
21919            // }
21920             
21921             //this.wrap.dom.innerHTML = '';
21922             //this.wrap.remove();
21923         }
21924     },
21925
21926     // private
21927     onFirstFocus : function(){
21928         
21929         this.assignDocWin();
21930         
21931         
21932         this.activated = true;
21933          
21934     
21935         if(Roo.isGecko){ // prevent silly gecko errors
21936             this.win.focus();
21937             var s = this.win.getSelection();
21938             if(!s.focusNode || s.focusNode.nodeType != 3){
21939                 var r = s.getRangeAt(0);
21940                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21941                 r.collapse(true);
21942                 this.deferFocus();
21943             }
21944             try{
21945                 this.execCmd('useCSS', true);
21946                 this.execCmd('styleWithCSS', false);
21947             }catch(e){}
21948         }
21949         this.owner.fireEvent('activate', this);
21950     },
21951
21952     // private
21953     adjustFont: function(btn){
21954         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21955         //if(Roo.isSafari){ // safari
21956         //    adjust *= 2;
21957        // }
21958         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21959         if(Roo.isSafari){ // safari
21960             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21961             v =  (v < 10) ? 10 : v;
21962             v =  (v > 48) ? 48 : v;
21963             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21964             
21965         }
21966         
21967         
21968         v = Math.max(1, v+adjust);
21969         
21970         this.execCmd('FontSize', v  );
21971     },
21972
21973     onEditorEvent : function(e)
21974     {
21975         this.owner.fireEvent('editorevent', this, e);
21976       //  this.updateToolbar();
21977         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21978     },
21979
21980     insertTag : function(tg)
21981     {
21982         // could be a bit smarter... -> wrap the current selected tRoo..
21983         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21984             
21985             range = this.createRange(this.getSelection());
21986             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21987             wrappingNode.appendChild(range.extractContents());
21988             range.insertNode(wrappingNode);
21989
21990             return;
21991             
21992             
21993             
21994         }
21995         this.execCmd("formatblock",   tg);
21996         
21997     },
21998     
21999     insertText : function(txt)
22000     {
22001         
22002         
22003         var range = this.createRange();
22004         range.deleteContents();
22005                //alert(Sender.getAttribute('label'));
22006                
22007         range.insertNode(this.doc.createTextNode(txt));
22008     } ,
22009     
22010      
22011
22012     /**
22013      * Executes a Midas editor command on the editor document and performs necessary focus and
22014      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22015      * @param {String} cmd The Midas command
22016      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22017      */
22018     relayCmd : function(cmd, value){
22019         this.win.focus();
22020         this.execCmd(cmd, value);
22021         this.owner.fireEvent('editorevent', this);
22022         //this.updateToolbar();
22023         this.owner.deferFocus();
22024     },
22025
22026     /**
22027      * Executes a Midas editor command directly on the editor document.
22028      * For visual commands, you should use {@link #relayCmd} instead.
22029      * <b>This should only be called after the editor is initialized.</b>
22030      * @param {String} cmd The Midas command
22031      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22032      */
22033     execCmd : function(cmd, value){
22034         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22035         this.syncValue();
22036     },
22037  
22038  
22039    
22040     /**
22041      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22042      * to insert tRoo.
22043      * @param {String} text | dom node.. 
22044      */
22045     insertAtCursor : function(text)
22046     {
22047         
22048         if(!this.activated){
22049             return;
22050         }
22051         /*
22052         if(Roo.isIE){
22053             this.win.focus();
22054             var r = this.doc.selection.createRange();
22055             if(r){
22056                 r.collapse(true);
22057                 r.pasteHTML(text);
22058                 this.syncValue();
22059                 this.deferFocus();
22060             
22061             }
22062             return;
22063         }
22064         */
22065         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22066             this.win.focus();
22067             
22068             
22069             // from jquery ui (MIT licenced)
22070             var range, node;
22071             var win = this.win;
22072             
22073             if (win.getSelection && win.getSelection().getRangeAt) {
22074                 range = win.getSelection().getRangeAt(0);
22075                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22076                 range.insertNode(node);
22077             } else if (win.document.selection && win.document.selection.createRange) {
22078                 // no firefox support
22079                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22080                 win.document.selection.createRange().pasteHTML(txt);
22081             } else {
22082                 // no firefox support
22083                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22084                 this.execCmd('InsertHTML', txt);
22085             } 
22086             
22087             this.syncValue();
22088             
22089             this.deferFocus();
22090         }
22091     },
22092  // private
22093     mozKeyPress : function(e){
22094         if(e.ctrlKey){
22095             var c = e.getCharCode(), cmd;
22096           
22097             if(c > 0){
22098                 c = String.fromCharCode(c).toLowerCase();
22099                 switch(c){
22100                     case 'b':
22101                         cmd = 'bold';
22102                         break;
22103                     case 'i':
22104                         cmd = 'italic';
22105                         break;
22106                     
22107                     case 'u':
22108                         cmd = 'underline';
22109                         break;
22110                     
22111                     case 'v':
22112                         this.cleanUpPaste.defer(100, this);
22113                         return;
22114                         
22115                 }
22116                 if(cmd){
22117                     this.win.focus();
22118                     this.execCmd(cmd);
22119                     this.deferFocus();
22120                     e.preventDefault();
22121                 }
22122                 
22123             }
22124         }
22125     },
22126
22127     // private
22128     fixKeys : function(){ // load time branching for fastest keydown performance
22129         if(Roo.isIE){
22130             return function(e){
22131                 var k = e.getKey(), r;
22132                 if(k == e.TAB){
22133                     e.stopEvent();
22134                     r = this.doc.selection.createRange();
22135                     if(r){
22136                         r.collapse(true);
22137                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22138                         this.deferFocus();
22139                     }
22140                     return;
22141                 }
22142                 
22143                 if(k == e.ENTER){
22144                     r = this.doc.selection.createRange();
22145                     if(r){
22146                         var target = r.parentElement();
22147                         if(!target || target.tagName.toLowerCase() != 'li'){
22148                             e.stopEvent();
22149                             r.pasteHTML('<br />');
22150                             r.collapse(false);
22151                             r.select();
22152                         }
22153                     }
22154                 }
22155                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22156                     this.cleanUpPaste.defer(100, this);
22157                     return;
22158                 }
22159                 
22160                 
22161             };
22162         }else if(Roo.isOpera){
22163             return function(e){
22164                 var k = e.getKey();
22165                 if(k == e.TAB){
22166                     e.stopEvent();
22167                     this.win.focus();
22168                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22169                     this.deferFocus();
22170                 }
22171                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22172                     this.cleanUpPaste.defer(100, this);
22173                     return;
22174                 }
22175                 
22176             };
22177         }else if(Roo.isSafari){
22178             return function(e){
22179                 var k = e.getKey();
22180                 
22181                 if(k == e.TAB){
22182                     e.stopEvent();
22183                     this.execCmd('InsertText','\t');
22184                     this.deferFocus();
22185                     return;
22186                 }
22187                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22188                     this.cleanUpPaste.defer(100, this);
22189                     return;
22190                 }
22191                 
22192              };
22193         }
22194     }(),
22195     
22196     getAllAncestors: function()
22197     {
22198         var p = this.getSelectedNode();
22199         var a = [];
22200         if (!p) {
22201             a.push(p); // push blank onto stack..
22202             p = this.getParentElement();
22203         }
22204         
22205         
22206         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22207             a.push(p);
22208             p = p.parentNode;
22209         }
22210         a.push(this.doc.body);
22211         return a;
22212     },
22213     lastSel : false,
22214     lastSelNode : false,
22215     
22216     
22217     getSelection : function() 
22218     {
22219         this.assignDocWin();
22220         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22221     },
22222     
22223     getSelectedNode: function() 
22224     {
22225         // this may only work on Gecko!!!
22226         
22227         // should we cache this!!!!
22228         
22229         
22230         
22231          
22232         var range = this.createRange(this.getSelection()).cloneRange();
22233         
22234         if (Roo.isIE) {
22235             var parent = range.parentElement();
22236             while (true) {
22237                 var testRange = range.duplicate();
22238                 testRange.moveToElementText(parent);
22239                 if (testRange.inRange(range)) {
22240                     break;
22241                 }
22242                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22243                     break;
22244                 }
22245                 parent = parent.parentElement;
22246             }
22247             return parent;
22248         }
22249         
22250         // is ancestor a text element.
22251         var ac =  range.commonAncestorContainer;
22252         if (ac.nodeType == 3) {
22253             ac = ac.parentNode;
22254         }
22255         
22256         var ar = ac.childNodes;
22257          
22258         var nodes = [];
22259         var other_nodes = [];
22260         var has_other_nodes = false;
22261         for (var i=0;i<ar.length;i++) {
22262             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22263                 continue;
22264             }
22265             // fullly contained node.
22266             
22267             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22268                 nodes.push(ar[i]);
22269                 continue;
22270             }
22271             
22272             // probably selected..
22273             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22274                 other_nodes.push(ar[i]);
22275                 continue;
22276             }
22277             // outer..
22278             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22279                 continue;
22280             }
22281             
22282             
22283             has_other_nodes = true;
22284         }
22285         if (!nodes.length && other_nodes.length) {
22286             nodes= other_nodes;
22287         }
22288         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22289             return false;
22290         }
22291         
22292         return nodes[0];
22293     },
22294     createRange: function(sel)
22295     {
22296         // this has strange effects when using with 
22297         // top toolbar - not sure if it's a great idea.
22298         //this.editor.contentWindow.focus();
22299         if (typeof sel != "undefined") {
22300             try {
22301                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22302             } catch(e) {
22303                 return this.doc.createRange();
22304             }
22305         } else {
22306             return this.doc.createRange();
22307         }
22308     },
22309     getParentElement: function()
22310     {
22311         
22312         this.assignDocWin();
22313         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22314         
22315         var range = this.createRange(sel);
22316          
22317         try {
22318             var p = range.commonAncestorContainer;
22319             while (p.nodeType == 3) { // text node
22320                 p = p.parentNode;
22321             }
22322             return p;
22323         } catch (e) {
22324             return null;
22325         }
22326     
22327     },
22328     /***
22329      *
22330      * Range intersection.. the hard stuff...
22331      *  '-1' = before
22332      *  '0' = hits..
22333      *  '1' = after.
22334      *         [ -- selected range --- ]
22335      *   [fail]                        [fail]
22336      *
22337      *    basically..
22338      *      if end is before start or  hits it. fail.
22339      *      if start is after end or hits it fail.
22340      *
22341      *   if either hits (but other is outside. - then it's not 
22342      *   
22343      *    
22344      **/
22345     
22346     
22347     // @see http://www.thismuchiknow.co.uk/?p=64.
22348     rangeIntersectsNode : function(range, node)
22349     {
22350         var nodeRange = node.ownerDocument.createRange();
22351         try {
22352             nodeRange.selectNode(node);
22353         } catch (e) {
22354             nodeRange.selectNodeContents(node);
22355         }
22356     
22357         var rangeStartRange = range.cloneRange();
22358         rangeStartRange.collapse(true);
22359     
22360         var rangeEndRange = range.cloneRange();
22361         rangeEndRange.collapse(false);
22362     
22363         var nodeStartRange = nodeRange.cloneRange();
22364         nodeStartRange.collapse(true);
22365     
22366         var nodeEndRange = nodeRange.cloneRange();
22367         nodeEndRange.collapse(false);
22368     
22369         return rangeStartRange.compareBoundaryPoints(
22370                  Range.START_TO_START, nodeEndRange) == -1 &&
22371                rangeEndRange.compareBoundaryPoints(
22372                  Range.START_TO_START, nodeStartRange) == 1;
22373         
22374          
22375     },
22376     rangeCompareNode : function(range, node)
22377     {
22378         var nodeRange = node.ownerDocument.createRange();
22379         try {
22380             nodeRange.selectNode(node);
22381         } catch (e) {
22382             nodeRange.selectNodeContents(node);
22383         }
22384         
22385         
22386         range.collapse(true);
22387     
22388         nodeRange.collapse(true);
22389      
22390         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22391         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22392          
22393         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22394         
22395         var nodeIsBefore   =  ss == 1;
22396         var nodeIsAfter    = ee == -1;
22397         
22398         if (nodeIsBefore && nodeIsAfter) {
22399             return 0; // outer
22400         }
22401         if (!nodeIsBefore && nodeIsAfter) {
22402             return 1; //right trailed.
22403         }
22404         
22405         if (nodeIsBefore && !nodeIsAfter) {
22406             return 2;  // left trailed.
22407         }
22408         // fully contined.
22409         return 3;
22410     },
22411
22412     // private? - in a new class?
22413     cleanUpPaste :  function()
22414     {
22415         // cleans up the whole document..
22416         Roo.log('cleanuppaste');
22417         
22418         this.cleanUpChildren(this.doc.body);
22419         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22420         if (clean != this.doc.body.innerHTML) {
22421             this.doc.body.innerHTML = clean;
22422         }
22423         
22424     },
22425     
22426     cleanWordChars : function(input) {// change the chars to hex code
22427         var he = Roo.HtmlEditorCore;
22428         
22429         var output = input;
22430         Roo.each(he.swapCodes, function(sw) { 
22431             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22432             
22433             output = output.replace(swapper, sw[1]);
22434         });
22435         
22436         return output;
22437     },
22438     
22439     
22440     cleanUpChildren : function (n)
22441     {
22442         if (!n.childNodes.length) {
22443             return;
22444         }
22445         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22446            this.cleanUpChild(n.childNodes[i]);
22447         }
22448     },
22449     
22450     
22451         
22452     
22453     cleanUpChild : function (node)
22454     {
22455         var ed = this;
22456         //console.log(node);
22457         if (node.nodeName == "#text") {
22458             // clean up silly Windows -- stuff?
22459             return; 
22460         }
22461         if (node.nodeName == "#comment") {
22462             node.parentNode.removeChild(node);
22463             // clean up silly Windows -- stuff?
22464             return; 
22465         }
22466         var lcname = node.tagName.toLowerCase();
22467         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22468         // whitelist of tags..
22469         
22470         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22471             // remove node.
22472             node.parentNode.removeChild(node);
22473             return;
22474             
22475         }
22476         
22477         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22478         
22479         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22480         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22481         
22482         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22483         //    remove_keep_children = true;
22484         //}
22485         
22486         if (remove_keep_children) {
22487             this.cleanUpChildren(node);
22488             // inserts everything just before this node...
22489             while (node.childNodes.length) {
22490                 var cn = node.childNodes[0];
22491                 node.removeChild(cn);
22492                 node.parentNode.insertBefore(cn, node);
22493             }
22494             node.parentNode.removeChild(node);
22495             return;
22496         }
22497         
22498         if (!node.attributes || !node.attributes.length) {
22499             this.cleanUpChildren(node);
22500             return;
22501         }
22502         
22503         function cleanAttr(n,v)
22504         {
22505             
22506             if (v.match(/^\./) || v.match(/^\//)) {
22507                 return;
22508             }
22509             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22510                 return;
22511             }
22512             if (v.match(/^#/)) {
22513                 return;
22514             }
22515 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22516             node.removeAttribute(n);
22517             
22518         }
22519         
22520         var cwhite = this.cwhite;
22521         var cblack = this.cblack;
22522             
22523         function cleanStyle(n,v)
22524         {
22525             if (v.match(/expression/)) { //XSS?? should we even bother..
22526                 node.removeAttribute(n);
22527                 return;
22528             }
22529             
22530             var parts = v.split(/;/);
22531             var clean = [];
22532             
22533             Roo.each(parts, function(p) {
22534                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22535                 if (!p.length) {
22536                     return true;
22537                 }
22538                 var l = p.split(':').shift().replace(/\s+/g,'');
22539                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22540                 
22541                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22542 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22543                     //node.removeAttribute(n);
22544                     return true;
22545                 }
22546                 //Roo.log()
22547                 // only allow 'c whitelisted system attributes'
22548                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22549 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22550                     //node.removeAttribute(n);
22551                     return true;
22552                 }
22553                 
22554                 
22555                  
22556                 
22557                 clean.push(p);
22558                 return true;
22559             });
22560             if (clean.length) { 
22561                 node.setAttribute(n, clean.join(';'));
22562             } else {
22563                 node.removeAttribute(n);
22564             }
22565             
22566         }
22567         
22568         
22569         for (var i = node.attributes.length-1; i > -1 ; i--) {
22570             var a = node.attributes[i];
22571             //console.log(a);
22572             
22573             if (a.name.toLowerCase().substr(0,2)=='on')  {
22574                 node.removeAttribute(a.name);
22575                 continue;
22576             }
22577             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22578                 node.removeAttribute(a.name);
22579                 continue;
22580             }
22581             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22582                 cleanAttr(a.name,a.value); // fixme..
22583                 continue;
22584             }
22585             if (a.name == 'style') {
22586                 cleanStyle(a.name,a.value);
22587                 continue;
22588             }
22589             /// clean up MS crap..
22590             // tecnically this should be a list of valid class'es..
22591             
22592             
22593             if (a.name == 'class') {
22594                 if (a.value.match(/^Mso/)) {
22595                     node.className = '';
22596                 }
22597                 
22598                 if (a.value.match(/^body$/)) {
22599                     node.className = '';
22600                 }
22601                 continue;
22602             }
22603             
22604             // style cleanup!?
22605             // class cleanup?
22606             
22607         }
22608         
22609         
22610         this.cleanUpChildren(node);
22611         
22612         
22613     },
22614     
22615     /**
22616      * Clean up MS wordisms...
22617      */
22618     cleanWord : function(node)
22619     {
22620         
22621         
22622         if (!node) {
22623             this.cleanWord(this.doc.body);
22624             return;
22625         }
22626         if (node.nodeName == "#text") {
22627             // clean up silly Windows -- stuff?
22628             return; 
22629         }
22630         if (node.nodeName == "#comment") {
22631             node.parentNode.removeChild(node);
22632             // clean up silly Windows -- stuff?
22633             return; 
22634         }
22635         
22636         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22637             node.parentNode.removeChild(node);
22638             return;
22639         }
22640         
22641         // remove - but keep children..
22642         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22643             while (node.childNodes.length) {
22644                 var cn = node.childNodes[0];
22645                 node.removeChild(cn);
22646                 node.parentNode.insertBefore(cn, node);
22647             }
22648             node.parentNode.removeChild(node);
22649             this.iterateChildren(node, this.cleanWord);
22650             return;
22651         }
22652         // clean styles
22653         if (node.className.length) {
22654             
22655             var cn = node.className.split(/\W+/);
22656             var cna = [];
22657             Roo.each(cn, function(cls) {
22658                 if (cls.match(/Mso[a-zA-Z]+/)) {
22659                     return;
22660                 }
22661                 cna.push(cls);
22662             });
22663             node.className = cna.length ? cna.join(' ') : '';
22664             if (!cna.length) {
22665                 node.removeAttribute("class");
22666             }
22667         }
22668         
22669         if (node.hasAttribute("lang")) {
22670             node.removeAttribute("lang");
22671         }
22672         
22673         if (node.hasAttribute("style")) {
22674             
22675             var styles = node.getAttribute("style").split(";");
22676             var nstyle = [];
22677             Roo.each(styles, function(s) {
22678                 if (!s.match(/:/)) {
22679                     return;
22680                 }
22681                 var kv = s.split(":");
22682                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22683                     return;
22684                 }
22685                 // what ever is left... we allow.
22686                 nstyle.push(s);
22687             });
22688             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22689             if (!nstyle.length) {
22690                 node.removeAttribute('style');
22691             }
22692         }
22693         this.iterateChildren(node, this.cleanWord);
22694         
22695         
22696         
22697     },
22698     /**
22699      * iterateChildren of a Node, calling fn each time, using this as the scole..
22700      * @param {DomNode} node node to iterate children of.
22701      * @param {Function} fn method of this class to call on each item.
22702      */
22703     iterateChildren : function(node, fn)
22704     {
22705         if (!node.childNodes.length) {
22706                 return;
22707         }
22708         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22709            fn.call(this, node.childNodes[i])
22710         }
22711     },
22712     
22713     
22714     /**
22715      * cleanTableWidths.
22716      *
22717      * Quite often pasting from word etc.. results in tables with column and widths.
22718      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22719      *
22720      */
22721     cleanTableWidths : function(node)
22722     {
22723          
22724          
22725         if (!node) {
22726             this.cleanTableWidths(this.doc.body);
22727             return;
22728         }
22729         
22730         // ignore list...
22731         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22732             return; 
22733         }
22734         Roo.log(node.tagName);
22735         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22736             this.iterateChildren(node, this.cleanTableWidths);
22737             return;
22738         }
22739         if (node.hasAttribute('width')) {
22740             node.removeAttribute('width');
22741         }
22742         
22743          
22744         if (node.hasAttribute("style")) {
22745             // pretty basic...
22746             
22747             var styles = node.getAttribute("style").split(";");
22748             var nstyle = [];
22749             Roo.each(styles, function(s) {
22750                 if (!s.match(/:/)) {
22751                     return;
22752                 }
22753                 var kv = s.split(":");
22754                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22755                     return;
22756                 }
22757                 // what ever is left... we allow.
22758                 nstyle.push(s);
22759             });
22760             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22761             if (!nstyle.length) {
22762                 node.removeAttribute('style');
22763             }
22764         }
22765         
22766         this.iterateChildren(node, this.cleanTableWidths);
22767         
22768         
22769     },
22770     
22771     
22772     
22773     
22774     domToHTML : function(currentElement, depth, nopadtext) {
22775         
22776         depth = depth || 0;
22777         nopadtext = nopadtext || false;
22778     
22779         if (!currentElement) {
22780             return this.domToHTML(this.doc.body);
22781         }
22782         
22783         //Roo.log(currentElement);
22784         var j;
22785         var allText = false;
22786         var nodeName = currentElement.nodeName;
22787         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22788         
22789         if  (nodeName == '#text') {
22790             
22791             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22792         }
22793         
22794         
22795         var ret = '';
22796         if (nodeName != 'BODY') {
22797              
22798             var i = 0;
22799             // Prints the node tagName, such as <A>, <IMG>, etc
22800             if (tagName) {
22801                 var attr = [];
22802                 for(i = 0; i < currentElement.attributes.length;i++) {
22803                     // quoting?
22804                     var aname = currentElement.attributes.item(i).name;
22805                     if (!currentElement.attributes.item(i).value.length) {
22806                         continue;
22807                     }
22808                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22809                 }
22810                 
22811                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22812             } 
22813             else {
22814                 
22815                 // eack
22816             }
22817         } else {
22818             tagName = false;
22819         }
22820         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22821             return ret;
22822         }
22823         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22824             nopadtext = true;
22825         }
22826         
22827         
22828         // Traverse the tree
22829         i = 0;
22830         var currentElementChild = currentElement.childNodes.item(i);
22831         var allText = true;
22832         var innerHTML  = '';
22833         lastnode = '';
22834         while (currentElementChild) {
22835             // Formatting code (indent the tree so it looks nice on the screen)
22836             var nopad = nopadtext;
22837             if (lastnode == 'SPAN') {
22838                 nopad  = true;
22839             }
22840             // text
22841             if  (currentElementChild.nodeName == '#text') {
22842                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22843                 toadd = nopadtext ? toadd : toadd.trim();
22844                 if (!nopad && toadd.length > 80) {
22845                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22846                 }
22847                 innerHTML  += toadd;
22848                 
22849                 i++;
22850                 currentElementChild = currentElement.childNodes.item(i);
22851                 lastNode = '';
22852                 continue;
22853             }
22854             allText = false;
22855             
22856             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22857                 
22858             // Recursively traverse the tree structure of the child node
22859             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22860             lastnode = currentElementChild.nodeName;
22861             i++;
22862             currentElementChild=currentElement.childNodes.item(i);
22863         }
22864         
22865         ret += innerHTML;
22866         
22867         if (!allText) {
22868                 // The remaining code is mostly for formatting the tree
22869             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22870         }
22871         
22872         
22873         if (tagName) {
22874             ret+= "</"+tagName+">";
22875         }
22876         return ret;
22877         
22878     },
22879         
22880     applyBlacklists : function()
22881     {
22882         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22883         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22884         
22885         this.white = [];
22886         this.black = [];
22887         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22888             if (b.indexOf(tag) > -1) {
22889                 return;
22890             }
22891             this.white.push(tag);
22892             
22893         }, this);
22894         
22895         Roo.each(w, function(tag) {
22896             if (b.indexOf(tag) > -1) {
22897                 return;
22898             }
22899             if (this.white.indexOf(tag) > -1) {
22900                 return;
22901             }
22902             this.white.push(tag);
22903             
22904         }, this);
22905         
22906         
22907         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22908             if (w.indexOf(tag) > -1) {
22909                 return;
22910             }
22911             this.black.push(tag);
22912             
22913         }, this);
22914         
22915         Roo.each(b, function(tag) {
22916             if (w.indexOf(tag) > -1) {
22917                 return;
22918             }
22919             if (this.black.indexOf(tag) > -1) {
22920                 return;
22921             }
22922             this.black.push(tag);
22923             
22924         }, this);
22925         
22926         
22927         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22928         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22929         
22930         this.cwhite = [];
22931         this.cblack = [];
22932         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22933             if (b.indexOf(tag) > -1) {
22934                 return;
22935             }
22936             this.cwhite.push(tag);
22937             
22938         }, this);
22939         
22940         Roo.each(w, function(tag) {
22941             if (b.indexOf(tag) > -1) {
22942                 return;
22943             }
22944             if (this.cwhite.indexOf(tag) > -1) {
22945                 return;
22946             }
22947             this.cwhite.push(tag);
22948             
22949         }, this);
22950         
22951         
22952         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22953             if (w.indexOf(tag) > -1) {
22954                 return;
22955             }
22956             this.cblack.push(tag);
22957             
22958         }, this);
22959         
22960         Roo.each(b, function(tag) {
22961             if (w.indexOf(tag) > -1) {
22962                 return;
22963             }
22964             if (this.cblack.indexOf(tag) > -1) {
22965                 return;
22966             }
22967             this.cblack.push(tag);
22968             
22969         }, this);
22970     },
22971     
22972     setStylesheets : function(stylesheets)
22973     {
22974         if(typeof(stylesheets) == 'string'){
22975             Roo.get(this.iframe.contentDocument.head).createChild({
22976                 tag : 'link',
22977                 rel : 'stylesheet',
22978                 type : 'text/css',
22979                 href : stylesheets
22980             });
22981             
22982             return;
22983         }
22984         var _this = this;
22985      
22986         Roo.each(stylesheets, function(s) {
22987             if(!s.length){
22988                 return;
22989             }
22990             
22991             Roo.get(_this.iframe.contentDocument.head).createChild({
22992                 tag : 'link',
22993                 rel : 'stylesheet',
22994                 type : 'text/css',
22995                 href : s
22996             });
22997         });
22998
22999         
23000     },
23001     
23002     removeStylesheets : function()
23003     {
23004         var _this = this;
23005         
23006         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23007             s.remove();
23008         });
23009     },
23010     
23011     setStyle : function(style)
23012     {
23013         Roo.get(this.iframe.contentDocument.head).createChild({
23014             tag : 'style',
23015             type : 'text/css',
23016             html : style
23017         });
23018
23019         return;
23020     }
23021     
23022     // hide stuff that is not compatible
23023     /**
23024      * @event blur
23025      * @hide
23026      */
23027     /**
23028      * @event change
23029      * @hide
23030      */
23031     /**
23032      * @event focus
23033      * @hide
23034      */
23035     /**
23036      * @event specialkey
23037      * @hide
23038      */
23039     /**
23040      * @cfg {String} fieldClass @hide
23041      */
23042     /**
23043      * @cfg {String} focusClass @hide
23044      */
23045     /**
23046      * @cfg {String} autoCreate @hide
23047      */
23048     /**
23049      * @cfg {String} inputType @hide
23050      */
23051     /**
23052      * @cfg {String} invalidClass @hide
23053      */
23054     /**
23055      * @cfg {String} invalidText @hide
23056      */
23057     /**
23058      * @cfg {String} msgFx @hide
23059      */
23060     /**
23061      * @cfg {String} validateOnBlur @hide
23062      */
23063 });
23064
23065 Roo.HtmlEditorCore.white = [
23066         'area', 'br', 'img', 'input', 'hr', 'wbr',
23067         
23068        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23069        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23070        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23071        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23072        'table',   'ul',         'xmp', 
23073        
23074        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23075       'thead',   'tr', 
23076      
23077       'dir', 'menu', 'ol', 'ul', 'dl',
23078        
23079       'embed',  'object'
23080 ];
23081
23082
23083 Roo.HtmlEditorCore.black = [
23084     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23085         'applet', // 
23086         'base',   'basefont', 'bgsound', 'blink',  'body', 
23087         'frame',  'frameset', 'head',    'html',   'ilayer', 
23088         'iframe', 'layer',  'link',     'meta',    'object',   
23089         'script', 'style' ,'title',  'xml' // clean later..
23090 ];
23091 Roo.HtmlEditorCore.clean = [
23092     'script', 'style', 'title', 'xml'
23093 ];
23094 Roo.HtmlEditorCore.remove = [
23095     'font'
23096 ];
23097 // attributes..
23098
23099 Roo.HtmlEditorCore.ablack = [
23100     'on'
23101 ];
23102     
23103 Roo.HtmlEditorCore.aclean = [ 
23104     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23105 ];
23106
23107 // protocols..
23108 Roo.HtmlEditorCore.pwhite= [
23109         'http',  'https',  'mailto'
23110 ];
23111
23112 // white listed style attributes.
23113 Roo.HtmlEditorCore.cwhite= [
23114       //  'text-align', /// default is to allow most things..
23115       
23116          
23117 //        'font-size'//??
23118 ];
23119
23120 // black listed style attributes.
23121 Roo.HtmlEditorCore.cblack= [
23122       //  'font-size' -- this can be set by the project 
23123 ];
23124
23125
23126 Roo.HtmlEditorCore.swapCodes   =[ 
23127     [    8211, "--" ], 
23128     [    8212, "--" ], 
23129     [    8216,  "'" ],  
23130     [    8217, "'" ],  
23131     [    8220, '"' ],  
23132     [    8221, '"' ],  
23133     [    8226, "*" ],  
23134     [    8230, "..." ]
23135 ]; 
23136
23137     /*
23138  * - LGPL
23139  *
23140  * HtmlEditor
23141  * 
23142  */
23143
23144 /**
23145  * @class Roo.bootstrap.HtmlEditor
23146  * @extends Roo.bootstrap.TextArea
23147  * Bootstrap HtmlEditor class
23148
23149  * @constructor
23150  * Create a new HtmlEditor
23151  * @param {Object} config The config object
23152  */
23153
23154 Roo.bootstrap.HtmlEditor = function(config){
23155     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23156     if (!this.toolbars) {
23157         this.toolbars = [];
23158     }
23159     
23160     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23161     this.addEvents({
23162             /**
23163              * @event initialize
23164              * Fires when the editor is fully initialized (including the iframe)
23165              * @param {HtmlEditor} this
23166              */
23167             initialize: true,
23168             /**
23169              * @event activate
23170              * Fires when the editor is first receives the focus. Any insertion must wait
23171              * until after this event.
23172              * @param {HtmlEditor} this
23173              */
23174             activate: true,
23175              /**
23176              * @event beforesync
23177              * Fires before the textarea is updated with content from the editor iframe. Return false
23178              * to cancel the sync.
23179              * @param {HtmlEditor} this
23180              * @param {String} html
23181              */
23182             beforesync: true,
23183              /**
23184              * @event beforepush
23185              * Fires before the iframe editor is updated with content from the textarea. Return false
23186              * to cancel the push.
23187              * @param {HtmlEditor} this
23188              * @param {String} html
23189              */
23190             beforepush: true,
23191              /**
23192              * @event sync
23193              * Fires when the textarea is updated with content from the editor iframe.
23194              * @param {HtmlEditor} this
23195              * @param {String} html
23196              */
23197             sync: true,
23198              /**
23199              * @event push
23200              * Fires when the iframe editor is updated with content from the textarea.
23201              * @param {HtmlEditor} this
23202              * @param {String} html
23203              */
23204             push: true,
23205              /**
23206              * @event editmodechange
23207              * Fires when the editor switches edit modes
23208              * @param {HtmlEditor} this
23209              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23210              */
23211             editmodechange: true,
23212             /**
23213              * @event editorevent
23214              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23215              * @param {HtmlEditor} this
23216              */
23217             editorevent: true,
23218             /**
23219              * @event firstfocus
23220              * Fires when on first focus - needed by toolbars..
23221              * @param {HtmlEditor} this
23222              */
23223             firstfocus: true,
23224             /**
23225              * @event autosave
23226              * Auto save the htmlEditor value as a file into Events
23227              * @param {HtmlEditor} this
23228              */
23229             autosave: true,
23230             /**
23231              * @event savedpreview
23232              * preview the saved version of htmlEditor
23233              * @param {HtmlEditor} this
23234              */
23235             savedpreview: true
23236         });
23237 };
23238
23239
23240 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23241     
23242     
23243       /**
23244      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23245      */
23246     toolbars : false,
23247     
23248      /**
23249     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23250     */
23251     btns : [],
23252    
23253      /**
23254      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23255      *                        Roo.resizable.
23256      */
23257     resizable : false,
23258      /**
23259      * @cfg {Number} height (in pixels)
23260      */   
23261     height: 300,
23262    /**
23263      * @cfg {Number} width (in pixels)
23264      */   
23265     width: false,
23266     
23267     /**
23268      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23269      * 
23270      */
23271     stylesheets: false,
23272     
23273     // id of frame..
23274     frameId: false,
23275     
23276     // private properties
23277     validationEvent : false,
23278     deferHeight: true,
23279     initialized : false,
23280     activated : false,
23281     
23282     onFocus : Roo.emptyFn,
23283     iframePad:3,
23284     hideMode:'offsets',
23285     
23286     tbContainer : false,
23287     
23288     bodyCls : '',
23289     
23290     toolbarContainer :function() {
23291         return this.wrap.select('.x-html-editor-tb',true).first();
23292     },
23293
23294     /**
23295      * Protected method that will not generally be called directly. It
23296      * is called when the editor creates its toolbar. Override this method if you need to
23297      * add custom toolbar buttons.
23298      * @param {HtmlEditor} editor
23299      */
23300     createToolbar : function(){
23301         Roo.log('renewing');
23302         Roo.log("create toolbars");
23303         
23304         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23305         this.toolbars[0].render(this.toolbarContainer());
23306         
23307         return;
23308         
23309 //        if (!editor.toolbars || !editor.toolbars.length) {
23310 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23311 //        }
23312 //        
23313 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23314 //            editor.toolbars[i] = Roo.factory(
23315 //                    typeof(editor.toolbars[i]) == 'string' ?
23316 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23317 //                Roo.bootstrap.HtmlEditor);
23318 //            editor.toolbars[i].init(editor);
23319 //        }
23320     },
23321
23322      
23323     // private
23324     onRender : function(ct, position)
23325     {
23326        // Roo.log("Call onRender: " + this.xtype);
23327         var _t = this;
23328         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23329       
23330         this.wrap = this.inputEl().wrap({
23331             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23332         });
23333         
23334         this.editorcore.onRender(ct, position);
23335          
23336         if (this.resizable) {
23337             this.resizeEl = new Roo.Resizable(this.wrap, {
23338                 pinned : true,
23339                 wrap: true,
23340                 dynamic : true,
23341                 minHeight : this.height,
23342                 height: this.height,
23343                 handles : this.resizable,
23344                 width: this.width,
23345                 listeners : {
23346                     resize : function(r, w, h) {
23347                         _t.onResize(w,h); // -something
23348                     }
23349                 }
23350             });
23351             
23352         }
23353         this.createToolbar(this);
23354        
23355         
23356         if(!this.width && this.resizable){
23357             this.setSize(this.wrap.getSize());
23358         }
23359         if (this.resizeEl) {
23360             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23361             // should trigger onReize..
23362         }
23363         
23364     },
23365
23366     // private
23367     onResize : function(w, h)
23368     {
23369         Roo.log('resize: ' +w + ',' + h );
23370         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23371         var ew = false;
23372         var eh = false;
23373         
23374         if(this.inputEl() ){
23375             if(typeof w == 'number'){
23376                 var aw = w - this.wrap.getFrameWidth('lr');
23377                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23378                 ew = aw;
23379             }
23380             if(typeof h == 'number'){
23381                  var tbh = -11;  // fixme it needs to tool bar size!
23382                 for (var i =0; i < this.toolbars.length;i++) {
23383                     // fixme - ask toolbars for heights?
23384                     tbh += this.toolbars[i].el.getHeight();
23385                     //if (this.toolbars[i].footer) {
23386                     //    tbh += this.toolbars[i].footer.el.getHeight();
23387                     //}
23388                 }
23389               
23390                 
23391                 
23392                 
23393                 
23394                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23395                 ah -= 5; // knock a few pixes off for look..
23396                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23397                 var eh = ah;
23398             }
23399         }
23400         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23401         this.editorcore.onResize(ew,eh);
23402         
23403     },
23404
23405     /**
23406      * Toggles the editor between standard and source edit mode.
23407      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23408      */
23409     toggleSourceEdit : function(sourceEditMode)
23410     {
23411         this.editorcore.toggleSourceEdit(sourceEditMode);
23412         
23413         if(this.editorcore.sourceEditMode){
23414             Roo.log('editor - showing textarea');
23415             
23416 //            Roo.log('in');
23417 //            Roo.log(this.syncValue());
23418             this.syncValue();
23419             this.inputEl().removeClass(['hide', 'x-hidden']);
23420             this.inputEl().dom.removeAttribute('tabIndex');
23421             this.inputEl().focus();
23422         }else{
23423             Roo.log('editor - hiding textarea');
23424 //            Roo.log('out')
23425 //            Roo.log(this.pushValue()); 
23426             this.pushValue();
23427             
23428             this.inputEl().addClass(['hide', 'x-hidden']);
23429             this.inputEl().dom.setAttribute('tabIndex', -1);
23430             //this.deferFocus();
23431         }
23432          
23433         if(this.resizable){
23434             this.setSize(this.wrap.getSize());
23435         }
23436         
23437         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23438     },
23439  
23440     // private (for BoxComponent)
23441     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23442
23443     // private (for BoxComponent)
23444     getResizeEl : function(){
23445         return this.wrap;
23446     },
23447
23448     // private (for BoxComponent)
23449     getPositionEl : function(){
23450         return this.wrap;
23451     },
23452
23453     // private
23454     initEvents : function(){
23455         this.originalValue = this.getValue();
23456     },
23457
23458 //    /**
23459 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23460 //     * @method
23461 //     */
23462 //    markInvalid : Roo.emptyFn,
23463 //    /**
23464 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23465 //     * @method
23466 //     */
23467 //    clearInvalid : Roo.emptyFn,
23468
23469     setValue : function(v){
23470         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23471         this.editorcore.pushValue();
23472     },
23473
23474      
23475     // private
23476     deferFocus : function(){
23477         this.focus.defer(10, this);
23478     },
23479
23480     // doc'ed in Field
23481     focus : function(){
23482         this.editorcore.focus();
23483         
23484     },
23485       
23486
23487     // private
23488     onDestroy : function(){
23489         
23490         
23491         
23492         if(this.rendered){
23493             
23494             for (var i =0; i < this.toolbars.length;i++) {
23495                 // fixme - ask toolbars for heights?
23496                 this.toolbars[i].onDestroy();
23497             }
23498             
23499             this.wrap.dom.innerHTML = '';
23500             this.wrap.remove();
23501         }
23502     },
23503
23504     // private
23505     onFirstFocus : function(){
23506         //Roo.log("onFirstFocus");
23507         this.editorcore.onFirstFocus();
23508          for (var i =0; i < this.toolbars.length;i++) {
23509             this.toolbars[i].onFirstFocus();
23510         }
23511         
23512     },
23513     
23514     // private
23515     syncValue : function()
23516     {   
23517         this.editorcore.syncValue();
23518     },
23519     
23520     pushValue : function()
23521     {   
23522         this.editorcore.pushValue();
23523     }
23524      
23525     
23526     // hide stuff that is not compatible
23527     /**
23528      * @event blur
23529      * @hide
23530      */
23531     /**
23532      * @event change
23533      * @hide
23534      */
23535     /**
23536      * @event focus
23537      * @hide
23538      */
23539     /**
23540      * @event specialkey
23541      * @hide
23542      */
23543     /**
23544      * @cfg {String} fieldClass @hide
23545      */
23546     /**
23547      * @cfg {String} focusClass @hide
23548      */
23549     /**
23550      * @cfg {String} autoCreate @hide
23551      */
23552     /**
23553      * @cfg {String} inputType @hide
23554      */
23555     /**
23556      * @cfg {String} invalidClass @hide
23557      */
23558     /**
23559      * @cfg {String} invalidText @hide
23560      */
23561     /**
23562      * @cfg {String} msgFx @hide
23563      */
23564     /**
23565      * @cfg {String} validateOnBlur @hide
23566      */
23567 });
23568  
23569     
23570    
23571    
23572    
23573       
23574 Roo.namespace('Roo.bootstrap.htmleditor');
23575 /**
23576  * @class Roo.bootstrap.HtmlEditorToolbar1
23577  * Basic Toolbar
23578  * 
23579  * Usage:
23580  *
23581  new Roo.bootstrap.HtmlEditor({
23582     ....
23583     toolbars : [
23584         new Roo.bootstrap.HtmlEditorToolbar1({
23585             disable : { fonts: 1 , format: 1, ..., ... , ...],
23586             btns : [ .... ]
23587         })
23588     }
23589      
23590  * 
23591  * @cfg {Object} disable List of elements to disable..
23592  * @cfg {Array} btns List of additional buttons.
23593  * 
23594  * 
23595  * NEEDS Extra CSS? 
23596  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23597  */
23598  
23599 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23600 {
23601     
23602     Roo.apply(this, config);
23603     
23604     // default disabled, based on 'good practice'..
23605     this.disable = this.disable || {};
23606     Roo.applyIf(this.disable, {
23607         fontSize : true,
23608         colors : true,
23609         specialElements : true
23610     });
23611     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23612     
23613     this.editor = config.editor;
23614     this.editorcore = config.editor.editorcore;
23615     
23616     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23617     
23618     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23619     // dont call parent... till later.
23620 }
23621 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23622      
23623     bar : true,
23624     
23625     editor : false,
23626     editorcore : false,
23627     
23628     
23629     formats : [
23630         "p" ,  
23631         "h1","h2","h3","h4","h5","h6", 
23632         "pre", "code", 
23633         "abbr", "acronym", "address", "cite", "samp", "var",
23634         'div','span'
23635     ],
23636     
23637     onRender : function(ct, position)
23638     {
23639        // Roo.log("Call onRender: " + this.xtype);
23640         
23641        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23642        Roo.log(this.el);
23643        this.el.dom.style.marginBottom = '0';
23644        var _this = this;
23645        var editorcore = this.editorcore;
23646        var editor= this.editor;
23647        
23648        var children = [];
23649        var btn = function(id,cmd , toggle, handler, html){
23650        
23651             var  event = toggle ? 'toggle' : 'click';
23652        
23653             var a = {
23654                 size : 'sm',
23655                 xtype: 'Button',
23656                 xns: Roo.bootstrap,
23657                 glyphicon : id,
23658                 cmd : id || cmd,
23659                 enableToggle:toggle !== false,
23660                 html : html || '',
23661                 pressed : toggle ? false : null,
23662                 listeners : {}
23663             };
23664             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23665                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23666             };
23667             children.push(a);
23668             return a;
23669        }
23670        
23671     //    var cb_box = function...
23672         
23673         var style = {
23674                 xtype: 'Button',
23675                 size : 'sm',
23676                 xns: Roo.bootstrap,
23677                 glyphicon : 'font',
23678                 //html : 'submit'
23679                 menu : {
23680                     xtype: 'Menu',
23681                     xns: Roo.bootstrap,
23682                     items:  []
23683                 }
23684         };
23685         Roo.each(this.formats, function(f) {
23686             style.menu.items.push({
23687                 xtype :'MenuItem',
23688                 xns: Roo.bootstrap,
23689                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23690                 tagname : f,
23691                 listeners : {
23692                     click : function()
23693                     {
23694                         editorcore.insertTag(this.tagname);
23695                         editor.focus();
23696                     }
23697                 }
23698                 
23699             });
23700         });
23701         children.push(style);   
23702         
23703         btn('bold',false,true);
23704         btn('italic',false,true);
23705         btn('align-left', 'justifyleft',true);
23706         btn('align-center', 'justifycenter',true);
23707         btn('align-right' , 'justifyright',true);
23708         btn('link', false, false, function(btn) {
23709             //Roo.log("create link?");
23710             var url = prompt(this.createLinkText, this.defaultLinkValue);
23711             if(url && url != 'http:/'+'/'){
23712                 this.editorcore.relayCmd('createlink', url);
23713             }
23714         }),
23715         btn('list','insertunorderedlist',true);
23716         btn('pencil', false,true, function(btn){
23717                 Roo.log(this);
23718                 this.toggleSourceEdit(btn.pressed);
23719         });
23720         
23721         if (this.editor.btns.length > 0) {
23722             for (var i = 0; i<this.editor.btns.length; i++) {
23723                 children.push(this.editor.btns[i]);
23724             }
23725         }
23726         
23727         /*
23728         var cog = {
23729                 xtype: 'Button',
23730                 size : 'sm',
23731                 xns: Roo.bootstrap,
23732                 glyphicon : 'cog',
23733                 //html : 'submit'
23734                 menu : {
23735                     xtype: 'Menu',
23736                     xns: Roo.bootstrap,
23737                     items:  []
23738                 }
23739         };
23740         
23741         cog.menu.items.push({
23742             xtype :'MenuItem',
23743             xns: Roo.bootstrap,
23744             html : Clean styles,
23745             tagname : f,
23746             listeners : {
23747                 click : function()
23748                 {
23749                     editorcore.insertTag(this.tagname);
23750                     editor.focus();
23751                 }
23752             }
23753             
23754         });
23755        */
23756         
23757          
23758        this.xtype = 'NavSimplebar';
23759         
23760         for(var i=0;i< children.length;i++) {
23761             
23762             this.buttons.add(this.addxtypeChild(children[i]));
23763             
23764         }
23765         
23766         editor.on('editorevent', this.updateToolbar, this);
23767     },
23768     onBtnClick : function(id)
23769     {
23770        this.editorcore.relayCmd(id);
23771        this.editorcore.focus();
23772     },
23773     
23774     /**
23775      * Protected method that will not generally be called directly. It triggers
23776      * a toolbar update by reading the markup state of the current selection in the editor.
23777      */
23778     updateToolbar: function(){
23779
23780         if(!this.editorcore.activated){
23781             this.editor.onFirstFocus(); // is this neeed?
23782             return;
23783         }
23784
23785         var btns = this.buttons; 
23786         var doc = this.editorcore.doc;
23787         btns.get('bold').setActive(doc.queryCommandState('bold'));
23788         btns.get('italic').setActive(doc.queryCommandState('italic'));
23789         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23790         
23791         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23792         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23793         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23794         
23795         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23796         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23797          /*
23798         
23799         var ans = this.editorcore.getAllAncestors();
23800         if (this.formatCombo) {
23801             
23802             
23803             var store = this.formatCombo.store;
23804             this.formatCombo.setValue("");
23805             for (var i =0; i < ans.length;i++) {
23806                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23807                     // select it..
23808                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23809                     break;
23810                 }
23811             }
23812         }
23813         
23814         
23815         
23816         // hides menus... - so this cant be on a menu...
23817         Roo.bootstrap.MenuMgr.hideAll();
23818         */
23819         Roo.bootstrap.MenuMgr.hideAll();
23820         //this.editorsyncValue();
23821     },
23822     onFirstFocus: function() {
23823         this.buttons.each(function(item){
23824            item.enable();
23825         });
23826     },
23827     toggleSourceEdit : function(sourceEditMode){
23828         
23829           
23830         if(sourceEditMode){
23831             Roo.log("disabling buttons");
23832            this.buttons.each( function(item){
23833                 if(item.cmd != 'pencil'){
23834                     item.disable();
23835                 }
23836             });
23837           
23838         }else{
23839             Roo.log("enabling buttons");
23840             if(this.editorcore.initialized){
23841                 this.buttons.each( function(item){
23842                     item.enable();
23843                 });
23844             }
23845             
23846         }
23847         Roo.log("calling toggole on editor");
23848         // tell the editor that it's been pressed..
23849         this.editor.toggleSourceEdit(sourceEditMode);
23850        
23851     }
23852 });
23853
23854
23855
23856
23857
23858 /**
23859  * @class Roo.bootstrap.Table.AbstractSelectionModel
23860  * @extends Roo.util.Observable
23861  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23862  * implemented by descendant classes.  This class should not be directly instantiated.
23863  * @constructor
23864  */
23865 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23866     this.locked = false;
23867     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23868 };
23869
23870
23871 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23872     /** @ignore Called by the grid automatically. Do not call directly. */
23873     init : function(grid){
23874         this.grid = grid;
23875         this.initEvents();
23876     },
23877
23878     /**
23879      * Locks the selections.
23880      */
23881     lock : function(){
23882         this.locked = true;
23883     },
23884
23885     /**
23886      * Unlocks the selections.
23887      */
23888     unlock : function(){
23889         this.locked = false;
23890     },
23891
23892     /**
23893      * Returns true if the selections are locked.
23894      * @return {Boolean}
23895      */
23896     isLocked : function(){
23897         return this.locked;
23898     }
23899 });
23900 /**
23901  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23902  * @class Roo.bootstrap.Table.RowSelectionModel
23903  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23904  * It supports multiple selections and keyboard selection/navigation. 
23905  * @constructor
23906  * @param {Object} config
23907  */
23908
23909 Roo.bootstrap.Table.RowSelectionModel = function(config){
23910     Roo.apply(this, config);
23911     this.selections = new Roo.util.MixedCollection(false, function(o){
23912         return o.id;
23913     });
23914
23915     this.last = false;
23916     this.lastActive = false;
23917
23918     this.addEvents({
23919         /**
23920              * @event selectionchange
23921              * Fires when the selection changes
23922              * @param {SelectionModel} this
23923              */
23924             "selectionchange" : true,
23925         /**
23926              * @event afterselectionchange
23927              * Fires after the selection changes (eg. by key press or clicking)
23928              * @param {SelectionModel} this
23929              */
23930             "afterselectionchange" : true,
23931         /**
23932              * @event beforerowselect
23933              * Fires when a row is selected being selected, return false to cancel.
23934              * @param {SelectionModel} this
23935              * @param {Number} rowIndex The selected index
23936              * @param {Boolean} keepExisting False if other selections will be cleared
23937              */
23938             "beforerowselect" : true,
23939         /**
23940              * @event rowselect
23941              * Fires when a row is selected.
23942              * @param {SelectionModel} this
23943              * @param {Number} rowIndex The selected index
23944              * @param {Roo.data.Record} r The record
23945              */
23946             "rowselect" : true,
23947         /**
23948              * @event rowdeselect
23949              * Fires when a row is deselected.
23950              * @param {SelectionModel} this
23951              * @param {Number} rowIndex The selected index
23952              */
23953         "rowdeselect" : true
23954     });
23955     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23956     this.locked = false;
23957  };
23958
23959 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23960     /**
23961      * @cfg {Boolean} singleSelect
23962      * True to allow selection of only one row at a time (defaults to false)
23963      */
23964     singleSelect : false,
23965
23966     // private
23967     initEvents : function()
23968     {
23969
23970         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23971         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23972         //}else{ // allow click to work like normal
23973          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23974         //}
23975         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23976         this.grid.on("rowclick", this.handleMouseDown, this);
23977         
23978         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23979             "up" : function(e){
23980                 if(!e.shiftKey){
23981                     this.selectPrevious(e.shiftKey);
23982                 }else if(this.last !== false && this.lastActive !== false){
23983                     var last = this.last;
23984                     this.selectRange(this.last,  this.lastActive-1);
23985                     this.grid.getView().focusRow(this.lastActive);
23986                     if(last !== false){
23987                         this.last = last;
23988                     }
23989                 }else{
23990                     this.selectFirstRow();
23991                 }
23992                 this.fireEvent("afterselectionchange", this);
23993             },
23994             "down" : function(e){
23995                 if(!e.shiftKey){
23996                     this.selectNext(e.shiftKey);
23997                 }else if(this.last !== false && this.lastActive !== false){
23998                     var last = this.last;
23999                     this.selectRange(this.last,  this.lastActive+1);
24000                     this.grid.getView().focusRow(this.lastActive);
24001                     if(last !== false){
24002                         this.last = last;
24003                     }
24004                 }else{
24005                     this.selectFirstRow();
24006                 }
24007                 this.fireEvent("afterselectionchange", this);
24008             },
24009             scope: this
24010         });
24011         this.grid.store.on('load', function(){
24012             this.selections.clear();
24013         },this);
24014         /*
24015         var view = this.grid.view;
24016         view.on("refresh", this.onRefresh, this);
24017         view.on("rowupdated", this.onRowUpdated, this);
24018         view.on("rowremoved", this.onRemove, this);
24019         */
24020     },
24021
24022     // private
24023     onRefresh : function()
24024     {
24025         var ds = this.grid.store, i, v = this.grid.view;
24026         var s = this.selections;
24027         s.each(function(r){
24028             if((i = ds.indexOfId(r.id)) != -1){
24029                 v.onRowSelect(i);
24030             }else{
24031                 s.remove(r);
24032             }
24033         });
24034     },
24035
24036     // private
24037     onRemove : function(v, index, r){
24038         this.selections.remove(r);
24039     },
24040
24041     // private
24042     onRowUpdated : function(v, index, r){
24043         if(this.isSelected(r)){
24044             v.onRowSelect(index);
24045         }
24046     },
24047
24048     /**
24049      * Select records.
24050      * @param {Array} records The records to select
24051      * @param {Boolean} keepExisting (optional) True to keep existing selections
24052      */
24053     selectRecords : function(records, keepExisting)
24054     {
24055         if(!keepExisting){
24056             this.clearSelections();
24057         }
24058             var ds = this.grid.store;
24059         for(var i = 0, len = records.length; i < len; i++){
24060             this.selectRow(ds.indexOf(records[i]), true);
24061         }
24062     },
24063
24064     /**
24065      * Gets the number of selected rows.
24066      * @return {Number}
24067      */
24068     getCount : function(){
24069         return this.selections.length;
24070     },
24071
24072     /**
24073      * Selects the first row in the grid.
24074      */
24075     selectFirstRow : function(){
24076         this.selectRow(0);
24077     },
24078
24079     /**
24080      * Select the last row.
24081      * @param {Boolean} keepExisting (optional) True to keep existing selections
24082      */
24083     selectLastRow : function(keepExisting){
24084         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24085         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24086     },
24087
24088     /**
24089      * Selects the row immediately following the last selected row.
24090      * @param {Boolean} keepExisting (optional) True to keep existing selections
24091      */
24092     selectNext : function(keepExisting)
24093     {
24094             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24095             this.selectRow(this.last+1, keepExisting);
24096             this.grid.getView().focusRow(this.last);
24097         }
24098     },
24099
24100     /**
24101      * Selects the row that precedes the last selected row.
24102      * @param {Boolean} keepExisting (optional) True to keep existing selections
24103      */
24104     selectPrevious : function(keepExisting){
24105         if(this.last){
24106             this.selectRow(this.last-1, keepExisting);
24107             this.grid.getView().focusRow(this.last);
24108         }
24109     },
24110
24111     /**
24112      * Returns the selected records
24113      * @return {Array} Array of selected records
24114      */
24115     getSelections : function(){
24116         return [].concat(this.selections.items);
24117     },
24118
24119     /**
24120      * Returns the first selected record.
24121      * @return {Record}
24122      */
24123     getSelected : function(){
24124         return this.selections.itemAt(0);
24125     },
24126
24127
24128     /**
24129      * Clears all selections.
24130      */
24131     clearSelections : function(fast)
24132     {
24133         if(this.locked) {
24134             return;
24135         }
24136         if(fast !== true){
24137                 var ds = this.grid.store;
24138             var s = this.selections;
24139             s.each(function(r){
24140                 this.deselectRow(ds.indexOfId(r.id));
24141             }, this);
24142             s.clear();
24143         }else{
24144             this.selections.clear();
24145         }
24146         this.last = false;
24147     },
24148
24149
24150     /**
24151      * Selects all rows.
24152      */
24153     selectAll : function(){
24154         if(this.locked) {
24155             return;
24156         }
24157         this.selections.clear();
24158         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24159             this.selectRow(i, true);
24160         }
24161     },
24162
24163     /**
24164      * Returns True if there is a selection.
24165      * @return {Boolean}
24166      */
24167     hasSelection : function(){
24168         return this.selections.length > 0;
24169     },
24170
24171     /**
24172      * Returns True if the specified row is selected.
24173      * @param {Number/Record} record The record or index of the record to check
24174      * @return {Boolean}
24175      */
24176     isSelected : function(index){
24177             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24178         return (r && this.selections.key(r.id) ? true : false);
24179     },
24180
24181     /**
24182      * Returns True if the specified record id is selected.
24183      * @param {String} id The id of record to check
24184      * @return {Boolean}
24185      */
24186     isIdSelected : function(id){
24187         return (this.selections.key(id) ? true : false);
24188     },
24189
24190
24191     // private
24192     handleMouseDBClick : function(e, t){
24193         
24194     },
24195     // private
24196     handleMouseDown : function(e, t)
24197     {
24198             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24199         if(this.isLocked() || rowIndex < 0 ){
24200             return;
24201         };
24202         if(e.shiftKey && this.last !== false){
24203             var last = this.last;
24204             this.selectRange(last, rowIndex, e.ctrlKey);
24205             this.last = last; // reset the last
24206             t.focus();
24207     
24208         }else{
24209             var isSelected = this.isSelected(rowIndex);
24210             //Roo.log("select row:" + rowIndex);
24211             if(isSelected){
24212                 this.deselectRow(rowIndex);
24213             } else {
24214                         this.selectRow(rowIndex, true);
24215             }
24216     
24217             /*
24218                 if(e.button !== 0 && isSelected){
24219                 alert('rowIndex 2: ' + rowIndex);
24220                     view.focusRow(rowIndex);
24221                 }else if(e.ctrlKey && isSelected){
24222                     this.deselectRow(rowIndex);
24223                 }else if(!isSelected){
24224                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24225                     view.focusRow(rowIndex);
24226                 }
24227             */
24228         }
24229         this.fireEvent("afterselectionchange", this);
24230     },
24231     // private
24232     handleDragableRowClick :  function(grid, rowIndex, e) 
24233     {
24234         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24235             this.selectRow(rowIndex, false);
24236             grid.view.focusRow(rowIndex);
24237              this.fireEvent("afterselectionchange", this);
24238         }
24239     },
24240     
24241     /**
24242      * Selects multiple rows.
24243      * @param {Array} rows Array of the indexes of the row to select
24244      * @param {Boolean} keepExisting (optional) True to keep existing selections
24245      */
24246     selectRows : function(rows, keepExisting){
24247         if(!keepExisting){
24248             this.clearSelections();
24249         }
24250         for(var i = 0, len = rows.length; i < len; i++){
24251             this.selectRow(rows[i], true);
24252         }
24253     },
24254
24255     /**
24256      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24257      * @param {Number} startRow The index of the first row in the range
24258      * @param {Number} endRow The index of the last row in the range
24259      * @param {Boolean} keepExisting (optional) True to retain existing selections
24260      */
24261     selectRange : function(startRow, endRow, keepExisting){
24262         if(this.locked) {
24263             return;
24264         }
24265         if(!keepExisting){
24266             this.clearSelections();
24267         }
24268         if(startRow <= endRow){
24269             for(var i = startRow; i <= endRow; i++){
24270                 this.selectRow(i, true);
24271             }
24272         }else{
24273             for(var i = startRow; i >= endRow; i--){
24274                 this.selectRow(i, true);
24275             }
24276         }
24277     },
24278
24279     /**
24280      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24281      * @param {Number} startRow The index of the first row in the range
24282      * @param {Number} endRow The index of the last row in the range
24283      */
24284     deselectRange : function(startRow, endRow, preventViewNotify){
24285         if(this.locked) {
24286             return;
24287         }
24288         for(var i = startRow; i <= endRow; i++){
24289             this.deselectRow(i, preventViewNotify);
24290         }
24291     },
24292
24293     /**
24294      * Selects a row.
24295      * @param {Number} row The index of the row to select
24296      * @param {Boolean} keepExisting (optional) True to keep existing selections
24297      */
24298     selectRow : function(index, keepExisting, preventViewNotify)
24299     {
24300             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24301             return;
24302         }
24303         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24304             if(!keepExisting || this.singleSelect){
24305                 this.clearSelections();
24306             }
24307             
24308             var r = this.grid.store.getAt(index);
24309             //console.log('selectRow - record id :' + r.id);
24310             
24311             this.selections.add(r);
24312             this.last = this.lastActive = index;
24313             if(!preventViewNotify){
24314                 var proxy = new Roo.Element(
24315                                 this.grid.getRowDom(index)
24316                 );
24317                 proxy.addClass('bg-info info');
24318             }
24319             this.fireEvent("rowselect", this, index, r);
24320             this.fireEvent("selectionchange", this);
24321         }
24322     },
24323
24324     /**
24325      * Deselects a row.
24326      * @param {Number} row The index of the row to deselect
24327      */
24328     deselectRow : function(index, preventViewNotify)
24329     {
24330         if(this.locked) {
24331             return;
24332         }
24333         if(this.last == index){
24334             this.last = false;
24335         }
24336         if(this.lastActive == index){
24337             this.lastActive = false;
24338         }
24339         
24340         var r = this.grid.store.getAt(index);
24341         if (!r) {
24342             return;
24343         }
24344         
24345         this.selections.remove(r);
24346         //.console.log('deselectRow - record id :' + r.id);
24347         if(!preventViewNotify){
24348         
24349             var proxy = new Roo.Element(
24350                 this.grid.getRowDom(index)
24351             );
24352             proxy.removeClass('bg-info info');
24353         }
24354         this.fireEvent("rowdeselect", this, index);
24355         this.fireEvent("selectionchange", this);
24356     },
24357
24358     // private
24359     restoreLast : function(){
24360         if(this._last){
24361             this.last = this._last;
24362         }
24363     },
24364
24365     // private
24366     acceptsNav : function(row, col, cm){
24367         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24368     },
24369
24370     // private
24371     onEditorKey : function(field, e){
24372         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24373         if(k == e.TAB){
24374             e.stopEvent();
24375             ed.completeEdit();
24376             if(e.shiftKey){
24377                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24378             }else{
24379                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24380             }
24381         }else if(k == e.ENTER && !e.ctrlKey){
24382             e.stopEvent();
24383             ed.completeEdit();
24384             if(e.shiftKey){
24385                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24386             }else{
24387                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24388             }
24389         }else if(k == e.ESC){
24390             ed.cancelEdit();
24391         }
24392         if(newCell){
24393             g.startEditing(newCell[0], newCell[1]);
24394         }
24395     }
24396 });
24397 /*
24398  * Based on:
24399  * Ext JS Library 1.1.1
24400  * Copyright(c) 2006-2007, Ext JS, LLC.
24401  *
24402  * Originally Released Under LGPL - original licence link has changed is not relivant.
24403  *
24404  * Fork - LGPL
24405  * <script type="text/javascript">
24406  */
24407  
24408 /**
24409  * @class Roo.bootstrap.PagingToolbar
24410  * @extends Roo.bootstrap.NavSimplebar
24411  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24412  * @constructor
24413  * Create a new PagingToolbar
24414  * @param {Object} config The config object
24415  * @param {Roo.data.Store} store
24416  */
24417 Roo.bootstrap.PagingToolbar = function(config)
24418 {
24419     // old args format still supported... - xtype is prefered..
24420         // created from xtype...
24421     
24422     this.ds = config.dataSource;
24423     
24424     if (config.store && !this.ds) {
24425         this.store= Roo.factory(config.store, Roo.data);
24426         this.ds = this.store;
24427         this.ds.xmodule = this.xmodule || false;
24428     }
24429     
24430     this.toolbarItems = [];
24431     if (config.items) {
24432         this.toolbarItems = config.items;
24433     }
24434     
24435     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24436     
24437     this.cursor = 0;
24438     
24439     if (this.ds) { 
24440         this.bind(this.ds);
24441     }
24442     
24443     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24444     
24445 };
24446
24447 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24448     /**
24449      * @cfg {Roo.data.Store} dataSource
24450      * The underlying data store providing the paged data
24451      */
24452     /**
24453      * @cfg {String/HTMLElement/Element} container
24454      * container The id or element that will contain the toolbar
24455      */
24456     /**
24457      * @cfg {Boolean} displayInfo
24458      * True to display the displayMsg (defaults to false)
24459      */
24460     /**
24461      * @cfg {Number} pageSize
24462      * The number of records to display per page (defaults to 20)
24463      */
24464     pageSize: 20,
24465     /**
24466      * @cfg {String} displayMsg
24467      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24468      */
24469     displayMsg : 'Displaying {0} - {1} of {2}',
24470     /**
24471      * @cfg {String} emptyMsg
24472      * The message to display when no records are found (defaults to "No data to display")
24473      */
24474     emptyMsg : 'No data to display',
24475     /**
24476      * Customizable piece of the default paging text (defaults to "Page")
24477      * @type String
24478      */
24479     beforePageText : "Page",
24480     /**
24481      * Customizable piece of the default paging text (defaults to "of %0")
24482      * @type String
24483      */
24484     afterPageText : "of {0}",
24485     /**
24486      * Customizable piece of the default paging text (defaults to "First Page")
24487      * @type String
24488      */
24489     firstText : "First Page",
24490     /**
24491      * Customizable piece of the default paging text (defaults to "Previous Page")
24492      * @type String
24493      */
24494     prevText : "Previous Page",
24495     /**
24496      * Customizable piece of the default paging text (defaults to "Next Page")
24497      * @type String
24498      */
24499     nextText : "Next Page",
24500     /**
24501      * Customizable piece of the default paging text (defaults to "Last Page")
24502      * @type String
24503      */
24504     lastText : "Last Page",
24505     /**
24506      * Customizable piece of the default paging text (defaults to "Refresh")
24507      * @type String
24508      */
24509     refreshText : "Refresh",
24510
24511     buttons : false,
24512     // private
24513     onRender : function(ct, position) 
24514     {
24515         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24516         this.navgroup.parentId = this.id;
24517         this.navgroup.onRender(this.el, null);
24518         // add the buttons to the navgroup
24519         
24520         if(this.displayInfo){
24521             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24522             this.displayEl = this.el.select('.x-paging-info', true).first();
24523 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24524 //            this.displayEl = navel.el.select('span',true).first();
24525         }
24526         
24527         var _this = this;
24528         
24529         if(this.buttons){
24530             Roo.each(_this.buttons, function(e){ // this might need to use render????
24531                Roo.factory(e).onRender(_this.el, null);
24532             });
24533         }
24534             
24535         Roo.each(_this.toolbarItems, function(e) {
24536             _this.navgroup.addItem(e);
24537         });
24538         
24539         
24540         this.first = this.navgroup.addItem({
24541             tooltip: this.firstText,
24542             cls: "prev",
24543             icon : 'fa fa-backward',
24544             disabled: true,
24545             preventDefault: true,
24546             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24547         });
24548         
24549         this.prev =  this.navgroup.addItem({
24550             tooltip: this.prevText,
24551             cls: "prev",
24552             icon : 'fa fa-step-backward',
24553             disabled: true,
24554             preventDefault: true,
24555             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24556         });
24557     //this.addSeparator();
24558         
24559         
24560         var field = this.navgroup.addItem( {
24561             tagtype : 'span',
24562             cls : 'x-paging-position',
24563             
24564             html : this.beforePageText  +
24565                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24566                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24567          } ); //?? escaped?
24568         
24569         this.field = field.el.select('input', true).first();
24570         this.field.on("keydown", this.onPagingKeydown, this);
24571         this.field.on("focus", function(){this.dom.select();});
24572     
24573     
24574         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24575         //this.field.setHeight(18);
24576         //this.addSeparator();
24577         this.next = this.navgroup.addItem({
24578             tooltip: this.nextText,
24579             cls: "next",
24580             html : ' <i class="fa fa-step-forward">',
24581             disabled: true,
24582             preventDefault: true,
24583             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24584         });
24585         this.last = this.navgroup.addItem({
24586             tooltip: this.lastText,
24587             icon : 'fa fa-forward',
24588             cls: "next",
24589             disabled: true,
24590             preventDefault: true,
24591             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24592         });
24593     //this.addSeparator();
24594         this.loading = this.navgroup.addItem({
24595             tooltip: this.refreshText,
24596             icon: 'fa fa-refresh',
24597             preventDefault: true,
24598             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24599         });
24600         
24601     },
24602
24603     // private
24604     updateInfo : function(){
24605         if(this.displayEl){
24606             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24607             var msg = count == 0 ?
24608                 this.emptyMsg :
24609                 String.format(
24610                     this.displayMsg,
24611                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24612                 );
24613             this.displayEl.update(msg);
24614         }
24615     },
24616
24617     // private
24618     onLoad : function(ds, r, o)
24619     {
24620         this.cursor = o.params.start ? o.params.start : 0;
24621         
24622         var d = this.getPageData(),
24623             ap = d.activePage,
24624             ps = d.pages;
24625         
24626         
24627         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24628         this.field.dom.value = ap;
24629         this.first.setDisabled(ap == 1);
24630         this.prev.setDisabled(ap == 1);
24631         this.next.setDisabled(ap == ps);
24632         this.last.setDisabled(ap == ps);
24633         this.loading.enable();
24634         this.updateInfo();
24635     },
24636
24637     // private
24638     getPageData : function(){
24639         var total = this.ds.getTotalCount();
24640         return {
24641             total : total,
24642             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24643             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24644         };
24645     },
24646
24647     // private
24648     onLoadError : function(){
24649         this.loading.enable();
24650     },
24651
24652     // private
24653     onPagingKeydown : function(e){
24654         var k = e.getKey();
24655         var d = this.getPageData();
24656         if(k == e.RETURN){
24657             var v = this.field.dom.value, pageNum;
24658             if(!v || isNaN(pageNum = parseInt(v, 10))){
24659                 this.field.dom.value = d.activePage;
24660                 return;
24661             }
24662             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24663             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24664             e.stopEvent();
24665         }
24666         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))
24667         {
24668           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24669           this.field.dom.value = pageNum;
24670           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24671           e.stopEvent();
24672         }
24673         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24674         {
24675           var v = this.field.dom.value, pageNum; 
24676           var increment = (e.shiftKey) ? 10 : 1;
24677           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24678                 increment *= -1;
24679           }
24680           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24681             this.field.dom.value = d.activePage;
24682             return;
24683           }
24684           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24685           {
24686             this.field.dom.value = parseInt(v, 10) + increment;
24687             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24688             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24689           }
24690           e.stopEvent();
24691         }
24692     },
24693
24694     // private
24695     beforeLoad : function(){
24696         if(this.loading){
24697             this.loading.disable();
24698         }
24699     },
24700
24701     // private
24702     onClick : function(which){
24703         
24704         var ds = this.ds;
24705         if (!ds) {
24706             return;
24707         }
24708         
24709         switch(which){
24710             case "first":
24711                 ds.load({params:{start: 0, limit: this.pageSize}});
24712             break;
24713             case "prev":
24714                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24715             break;
24716             case "next":
24717                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24718             break;
24719             case "last":
24720                 var total = ds.getTotalCount();
24721                 var extra = total % this.pageSize;
24722                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24723                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24724             break;
24725             case "refresh":
24726                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24727             break;
24728         }
24729     },
24730
24731     /**
24732      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24733      * @param {Roo.data.Store} store The data store to unbind
24734      */
24735     unbind : function(ds){
24736         ds.un("beforeload", this.beforeLoad, this);
24737         ds.un("load", this.onLoad, this);
24738         ds.un("loadexception", this.onLoadError, this);
24739         ds.un("remove", this.updateInfo, this);
24740         ds.un("add", this.updateInfo, this);
24741         this.ds = undefined;
24742     },
24743
24744     /**
24745      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24746      * @param {Roo.data.Store} store The data store to bind
24747      */
24748     bind : function(ds){
24749         ds.on("beforeload", this.beforeLoad, this);
24750         ds.on("load", this.onLoad, this);
24751         ds.on("loadexception", this.onLoadError, this);
24752         ds.on("remove", this.updateInfo, this);
24753         ds.on("add", this.updateInfo, this);
24754         this.ds = ds;
24755     }
24756 });/*
24757  * - LGPL
24758  *
24759  * element
24760  * 
24761  */
24762
24763 /**
24764  * @class Roo.bootstrap.MessageBar
24765  * @extends Roo.bootstrap.Component
24766  * Bootstrap MessageBar class
24767  * @cfg {String} html contents of the MessageBar
24768  * @cfg {String} weight (info | success | warning | danger) default info
24769  * @cfg {String} beforeClass insert the bar before the given class
24770  * @cfg {Boolean} closable (true | false) default false
24771  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24772  * 
24773  * @constructor
24774  * Create a new Element
24775  * @param {Object} config The config object
24776  */
24777
24778 Roo.bootstrap.MessageBar = function(config){
24779     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24780 };
24781
24782 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24783     
24784     html: '',
24785     weight: 'info',
24786     closable: false,
24787     fixed: false,
24788     beforeClass: 'bootstrap-sticky-wrap',
24789     
24790     getAutoCreate : function(){
24791         
24792         var cfg = {
24793             tag: 'div',
24794             cls: 'alert alert-dismissable alert-' + this.weight,
24795             cn: [
24796                 {
24797                     tag: 'span',
24798                     cls: 'message',
24799                     html: this.html || ''
24800                 }
24801             ]
24802         };
24803         
24804         if(this.fixed){
24805             cfg.cls += ' alert-messages-fixed';
24806         }
24807         
24808         if(this.closable){
24809             cfg.cn.push({
24810                 tag: 'button',
24811                 cls: 'close',
24812                 html: 'x'
24813             });
24814         }
24815         
24816         return cfg;
24817     },
24818     
24819     onRender : function(ct, position)
24820     {
24821         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24822         
24823         if(!this.el){
24824             var cfg = Roo.apply({},  this.getAutoCreate());
24825             cfg.id = Roo.id();
24826             
24827             if (this.cls) {
24828                 cfg.cls += ' ' + this.cls;
24829             }
24830             if (this.style) {
24831                 cfg.style = this.style;
24832             }
24833             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24834             
24835             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24836         }
24837         
24838         this.el.select('>button.close').on('click', this.hide, this);
24839         
24840     },
24841     
24842     show : function()
24843     {
24844         if (!this.rendered) {
24845             this.render();
24846         }
24847         
24848         this.el.show();
24849         
24850         this.fireEvent('show', this);
24851         
24852     },
24853     
24854     hide : function()
24855     {
24856         if (!this.rendered) {
24857             this.render();
24858         }
24859         
24860         this.el.hide();
24861         
24862         this.fireEvent('hide', this);
24863     },
24864     
24865     update : function()
24866     {
24867 //        var e = this.el.dom.firstChild;
24868 //        
24869 //        if(this.closable){
24870 //            e = e.nextSibling;
24871 //        }
24872 //        
24873 //        e.data = this.html || '';
24874
24875         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24876     }
24877    
24878 });
24879
24880  
24881
24882      /*
24883  * - LGPL
24884  *
24885  * Graph
24886  * 
24887  */
24888
24889
24890 /**
24891  * @class Roo.bootstrap.Graph
24892  * @extends Roo.bootstrap.Component
24893  * Bootstrap Graph class
24894 > Prameters
24895  -sm {number} sm 4
24896  -md {number} md 5
24897  @cfg {String} graphtype  bar | vbar | pie
24898  @cfg {number} g_x coodinator | centre x (pie)
24899  @cfg {number} g_y coodinator | centre y (pie)
24900  @cfg {number} g_r radius (pie)
24901  @cfg {number} g_height height of the chart (respected by all elements in the set)
24902  @cfg {number} g_width width of the chart (respected by all elements in the set)
24903  @cfg {Object} title The title of the chart
24904     
24905  -{Array}  values
24906  -opts (object) options for the chart 
24907      o {
24908      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24909      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24910      o vgutter (number)
24911      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.
24912      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24913      o to
24914      o stretch (boolean)
24915      o }
24916  -opts (object) options for the pie
24917      o{
24918      o cut
24919      o startAngle (number)
24920      o endAngle (number)
24921      } 
24922  *
24923  * @constructor
24924  * Create a new Input
24925  * @param {Object} config The config object
24926  */
24927
24928 Roo.bootstrap.Graph = function(config){
24929     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24930     
24931     this.addEvents({
24932         // img events
24933         /**
24934          * @event click
24935          * The img click event for the img.
24936          * @param {Roo.EventObject} e
24937          */
24938         "click" : true
24939     });
24940 };
24941
24942 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24943     
24944     sm: 4,
24945     md: 5,
24946     graphtype: 'bar',
24947     g_height: 250,
24948     g_width: 400,
24949     g_x: 50,
24950     g_y: 50,
24951     g_r: 30,
24952     opts:{
24953         //g_colors: this.colors,
24954         g_type: 'soft',
24955         g_gutter: '20%'
24956
24957     },
24958     title : false,
24959
24960     getAutoCreate : function(){
24961         
24962         var cfg = {
24963             tag: 'div',
24964             html : null
24965         };
24966         
24967         
24968         return  cfg;
24969     },
24970
24971     onRender : function(ct,position){
24972         
24973         
24974         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24975         
24976         if (typeof(Raphael) == 'undefined') {
24977             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24978             return;
24979         }
24980         
24981         this.raphael = Raphael(this.el.dom);
24982         
24983                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24984                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24985                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24986                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24987                 /*
24988                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24989                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24990                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24991                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24992                 
24993                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24994                 r.barchart(330, 10, 300, 220, data1);
24995                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24996                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24997                 */
24998                 
24999                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25000                 // r.barchart(30, 30, 560, 250,  xdata, {
25001                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25002                 //     axis : "0 0 1 1",
25003                 //     axisxlabels :  xdata
25004                 //     //yvalues : cols,
25005                    
25006                 // });
25007 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25008 //        
25009 //        this.load(null,xdata,{
25010 //                axis : "0 0 1 1",
25011 //                axisxlabels :  xdata
25012 //                });
25013
25014     },
25015
25016     load : function(graphtype,xdata,opts)
25017     {
25018         this.raphael.clear();
25019         if(!graphtype) {
25020             graphtype = this.graphtype;
25021         }
25022         if(!opts){
25023             opts = this.opts;
25024         }
25025         var r = this.raphael,
25026             fin = function () {
25027                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25028             },
25029             fout = function () {
25030                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25031             },
25032             pfin = function() {
25033                 this.sector.stop();
25034                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25035
25036                 if (this.label) {
25037                     this.label[0].stop();
25038                     this.label[0].attr({ r: 7.5 });
25039                     this.label[1].attr({ "font-weight": 800 });
25040                 }
25041             },
25042             pfout = function() {
25043                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25044
25045                 if (this.label) {
25046                     this.label[0].animate({ r: 5 }, 500, "bounce");
25047                     this.label[1].attr({ "font-weight": 400 });
25048                 }
25049             };
25050
25051         switch(graphtype){
25052             case 'bar':
25053                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25054                 break;
25055             case 'hbar':
25056                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25057                 break;
25058             case 'pie':
25059 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25060 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25061 //            
25062                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25063                 
25064                 break;
25065
25066         }
25067         
25068         if(this.title){
25069             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25070         }
25071         
25072     },
25073     
25074     setTitle: function(o)
25075     {
25076         this.title = o;
25077     },
25078     
25079     initEvents: function() {
25080         
25081         if(!this.href){
25082             this.el.on('click', this.onClick, this);
25083         }
25084     },
25085     
25086     onClick : function(e)
25087     {
25088         Roo.log('img onclick');
25089         this.fireEvent('click', this, e);
25090     }
25091    
25092 });
25093
25094  
25095 /*
25096  * - LGPL
25097  *
25098  * numberBox
25099  * 
25100  */
25101 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25102
25103 /**
25104  * @class Roo.bootstrap.dash.NumberBox
25105  * @extends Roo.bootstrap.Component
25106  * Bootstrap NumberBox class
25107  * @cfg {String} headline Box headline
25108  * @cfg {String} content Box content
25109  * @cfg {String} icon Box icon
25110  * @cfg {String} footer Footer text
25111  * @cfg {String} fhref Footer href
25112  * 
25113  * @constructor
25114  * Create a new NumberBox
25115  * @param {Object} config The config object
25116  */
25117
25118
25119 Roo.bootstrap.dash.NumberBox = function(config){
25120     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25121     
25122 };
25123
25124 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25125     
25126     headline : '',
25127     content : '',
25128     icon : '',
25129     footer : '',
25130     fhref : '',
25131     ficon : '',
25132     
25133     getAutoCreate : function(){
25134         
25135         var cfg = {
25136             tag : 'div',
25137             cls : 'small-box ',
25138             cn : [
25139                 {
25140                     tag : 'div',
25141                     cls : 'inner',
25142                     cn :[
25143                         {
25144                             tag : 'h3',
25145                             cls : 'roo-headline',
25146                             html : this.headline
25147                         },
25148                         {
25149                             tag : 'p',
25150                             cls : 'roo-content',
25151                             html : this.content
25152                         }
25153                     ]
25154                 }
25155             ]
25156         };
25157         
25158         if(this.icon){
25159             cfg.cn.push({
25160                 tag : 'div',
25161                 cls : 'icon',
25162                 cn :[
25163                     {
25164                         tag : 'i',
25165                         cls : 'ion ' + this.icon
25166                     }
25167                 ]
25168             });
25169         }
25170         
25171         if(this.footer){
25172             var footer = {
25173                 tag : 'a',
25174                 cls : 'small-box-footer',
25175                 href : this.fhref || '#',
25176                 html : this.footer
25177             };
25178             
25179             cfg.cn.push(footer);
25180             
25181         }
25182         
25183         return  cfg;
25184     },
25185
25186     onRender : function(ct,position){
25187         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25188
25189
25190        
25191                 
25192     },
25193
25194     setHeadline: function (value)
25195     {
25196         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25197     },
25198     
25199     setFooter: function (value, href)
25200     {
25201         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25202         
25203         if(href){
25204             this.el.select('a.small-box-footer',true).first().attr('href', href);
25205         }
25206         
25207     },
25208
25209     setContent: function (value)
25210     {
25211         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25212     },
25213
25214     initEvents: function() 
25215     {   
25216         
25217     }
25218     
25219 });
25220
25221  
25222 /*
25223  * - LGPL
25224  *
25225  * TabBox
25226  * 
25227  */
25228 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25229
25230 /**
25231  * @class Roo.bootstrap.dash.TabBox
25232  * @extends Roo.bootstrap.Component
25233  * Bootstrap TabBox class
25234  * @cfg {String} title Title of the TabBox
25235  * @cfg {String} icon Icon of the TabBox
25236  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25237  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25238  * 
25239  * @constructor
25240  * Create a new TabBox
25241  * @param {Object} config The config object
25242  */
25243
25244
25245 Roo.bootstrap.dash.TabBox = function(config){
25246     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25247     this.addEvents({
25248         // raw events
25249         /**
25250          * @event addpane
25251          * When a pane is added
25252          * @param {Roo.bootstrap.dash.TabPane} pane
25253          */
25254         "addpane" : true,
25255         /**
25256          * @event activatepane
25257          * When a pane is activated
25258          * @param {Roo.bootstrap.dash.TabPane} pane
25259          */
25260         "activatepane" : true
25261         
25262          
25263     });
25264     
25265     this.panes = [];
25266 };
25267
25268 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25269
25270     title : '',
25271     icon : false,
25272     showtabs : true,
25273     tabScrollable : false,
25274     
25275     getChildContainer : function()
25276     {
25277         return this.el.select('.tab-content', true).first();
25278     },
25279     
25280     getAutoCreate : function(){
25281         
25282         var header = {
25283             tag: 'li',
25284             cls: 'pull-left header',
25285             html: this.title,
25286             cn : []
25287         };
25288         
25289         if(this.icon){
25290             header.cn.push({
25291                 tag: 'i',
25292                 cls: 'fa ' + this.icon
25293             });
25294         }
25295         
25296         var h = {
25297             tag: 'ul',
25298             cls: 'nav nav-tabs pull-right',
25299             cn: [
25300                 header
25301             ]
25302         };
25303         
25304         if(this.tabScrollable){
25305             h = {
25306                 tag: 'div',
25307                 cls: 'tab-header',
25308                 cn: [
25309                     {
25310                         tag: 'ul',
25311                         cls: 'nav nav-tabs pull-right',
25312                         cn: [
25313                             header
25314                         ]
25315                     }
25316                 ]
25317             };
25318         }
25319         
25320         var cfg = {
25321             tag: 'div',
25322             cls: 'nav-tabs-custom',
25323             cn: [
25324                 h,
25325                 {
25326                     tag: 'div',
25327                     cls: 'tab-content no-padding',
25328                     cn: []
25329                 }
25330             ]
25331         };
25332
25333         return  cfg;
25334     },
25335     initEvents : function()
25336     {
25337         //Roo.log('add add pane handler');
25338         this.on('addpane', this.onAddPane, this);
25339     },
25340      /**
25341      * Updates the box title
25342      * @param {String} html to set the title to.
25343      */
25344     setTitle : function(value)
25345     {
25346         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25347     },
25348     onAddPane : function(pane)
25349     {
25350         this.panes.push(pane);
25351         //Roo.log('addpane');
25352         //Roo.log(pane);
25353         // tabs are rendere left to right..
25354         if(!this.showtabs){
25355             return;
25356         }
25357         
25358         var ctr = this.el.select('.nav-tabs', true).first();
25359          
25360          
25361         var existing = ctr.select('.nav-tab',true);
25362         var qty = existing.getCount();;
25363         
25364         
25365         var tab = ctr.createChild({
25366             tag : 'li',
25367             cls : 'nav-tab' + (qty ? '' : ' active'),
25368             cn : [
25369                 {
25370                     tag : 'a',
25371                     href:'#',
25372                     html : pane.title
25373                 }
25374             ]
25375         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25376         pane.tab = tab;
25377         
25378         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25379         if (!qty) {
25380             pane.el.addClass('active');
25381         }
25382         
25383                 
25384     },
25385     onTabClick : function(ev,un,ob,pane)
25386     {
25387         //Roo.log('tab - prev default');
25388         ev.preventDefault();
25389         
25390         
25391         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25392         pane.tab.addClass('active');
25393         //Roo.log(pane.title);
25394         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25395         // technically we should have a deactivate event.. but maybe add later.
25396         // and it should not de-activate the selected tab...
25397         this.fireEvent('activatepane', pane);
25398         pane.el.addClass('active');
25399         pane.fireEvent('activate');
25400         
25401         
25402     },
25403     
25404     getActivePane : function()
25405     {
25406         var r = false;
25407         Roo.each(this.panes, function(p) {
25408             if(p.el.hasClass('active')){
25409                 r = p;
25410                 return false;
25411             }
25412             
25413             return;
25414         });
25415         
25416         return r;
25417     }
25418     
25419     
25420 });
25421
25422  
25423 /*
25424  * - LGPL
25425  *
25426  * Tab pane
25427  * 
25428  */
25429 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25430 /**
25431  * @class Roo.bootstrap.TabPane
25432  * @extends Roo.bootstrap.Component
25433  * Bootstrap TabPane class
25434  * @cfg {Boolean} active (false | true) Default false
25435  * @cfg {String} title title of panel
25436
25437  * 
25438  * @constructor
25439  * Create a new TabPane
25440  * @param {Object} config The config object
25441  */
25442
25443 Roo.bootstrap.dash.TabPane = function(config){
25444     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25445     
25446     this.addEvents({
25447         // raw events
25448         /**
25449          * @event activate
25450          * When a pane is activated
25451          * @param {Roo.bootstrap.dash.TabPane} pane
25452          */
25453         "activate" : true
25454          
25455     });
25456 };
25457
25458 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25459     
25460     active : false,
25461     title : '',
25462     
25463     // the tabBox that this is attached to.
25464     tab : false,
25465      
25466     getAutoCreate : function() 
25467     {
25468         var cfg = {
25469             tag: 'div',
25470             cls: 'tab-pane'
25471         };
25472         
25473         if(this.active){
25474             cfg.cls += ' active';
25475         }
25476         
25477         return cfg;
25478     },
25479     initEvents  : function()
25480     {
25481         //Roo.log('trigger add pane handler');
25482         this.parent().fireEvent('addpane', this)
25483     },
25484     
25485      /**
25486      * Updates the tab title 
25487      * @param {String} html to set the title to.
25488      */
25489     setTitle: function(str)
25490     {
25491         if (!this.tab) {
25492             return;
25493         }
25494         this.title = str;
25495         this.tab.select('a', true).first().dom.innerHTML = str;
25496         
25497     }
25498     
25499     
25500     
25501 });
25502
25503  
25504
25505
25506  /*
25507  * - LGPL
25508  *
25509  * menu
25510  * 
25511  */
25512 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25513
25514 /**
25515  * @class Roo.bootstrap.menu.Menu
25516  * @extends Roo.bootstrap.Component
25517  * Bootstrap Menu class - container for Menu
25518  * @cfg {String} html Text of the menu
25519  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25520  * @cfg {String} icon Font awesome icon
25521  * @cfg {String} pos Menu align to (top | bottom) default bottom
25522  * 
25523  * 
25524  * @constructor
25525  * Create a new Menu
25526  * @param {Object} config The config object
25527  */
25528
25529
25530 Roo.bootstrap.menu.Menu = function(config){
25531     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25532     
25533     this.addEvents({
25534         /**
25535          * @event beforeshow
25536          * Fires before this menu is displayed
25537          * @param {Roo.bootstrap.menu.Menu} this
25538          */
25539         beforeshow : true,
25540         /**
25541          * @event beforehide
25542          * Fires before this menu is hidden
25543          * @param {Roo.bootstrap.menu.Menu} this
25544          */
25545         beforehide : true,
25546         /**
25547          * @event show
25548          * Fires after this menu is displayed
25549          * @param {Roo.bootstrap.menu.Menu} this
25550          */
25551         show : true,
25552         /**
25553          * @event hide
25554          * Fires after this menu is hidden
25555          * @param {Roo.bootstrap.menu.Menu} this
25556          */
25557         hide : true,
25558         /**
25559          * @event click
25560          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25561          * @param {Roo.bootstrap.menu.Menu} this
25562          * @param {Roo.EventObject} e
25563          */
25564         click : true
25565     });
25566     
25567 };
25568
25569 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25570     
25571     submenu : false,
25572     html : '',
25573     weight : 'default',
25574     icon : false,
25575     pos : 'bottom',
25576     
25577     
25578     getChildContainer : function() {
25579         if(this.isSubMenu){
25580             return this.el;
25581         }
25582         
25583         return this.el.select('ul.dropdown-menu', true).first();  
25584     },
25585     
25586     getAutoCreate : function()
25587     {
25588         var text = [
25589             {
25590                 tag : 'span',
25591                 cls : 'roo-menu-text',
25592                 html : this.html
25593             }
25594         ];
25595         
25596         if(this.icon){
25597             text.unshift({
25598                 tag : 'i',
25599                 cls : 'fa ' + this.icon
25600             })
25601         }
25602         
25603         
25604         var cfg = {
25605             tag : 'div',
25606             cls : 'btn-group',
25607             cn : [
25608                 {
25609                     tag : 'button',
25610                     cls : 'dropdown-button btn btn-' + this.weight,
25611                     cn : text
25612                 },
25613                 {
25614                     tag : 'button',
25615                     cls : 'dropdown-toggle btn btn-' + this.weight,
25616                     cn : [
25617                         {
25618                             tag : 'span',
25619                             cls : 'caret'
25620                         }
25621                     ]
25622                 },
25623                 {
25624                     tag : 'ul',
25625                     cls : 'dropdown-menu'
25626                 }
25627             ]
25628             
25629         };
25630         
25631         if(this.pos == 'top'){
25632             cfg.cls += ' dropup';
25633         }
25634         
25635         if(this.isSubMenu){
25636             cfg = {
25637                 tag : 'ul',
25638                 cls : 'dropdown-menu'
25639             }
25640         }
25641         
25642         return cfg;
25643     },
25644     
25645     onRender : function(ct, position)
25646     {
25647         this.isSubMenu = ct.hasClass('dropdown-submenu');
25648         
25649         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25650     },
25651     
25652     initEvents : function() 
25653     {
25654         if(this.isSubMenu){
25655             return;
25656         }
25657         
25658         this.hidden = true;
25659         
25660         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25661         this.triggerEl.on('click', this.onTriggerPress, this);
25662         
25663         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25664         this.buttonEl.on('click', this.onClick, this);
25665         
25666     },
25667     
25668     list : function()
25669     {
25670         if(this.isSubMenu){
25671             return this.el;
25672         }
25673         
25674         return this.el.select('ul.dropdown-menu', true).first();
25675     },
25676     
25677     onClick : function(e)
25678     {
25679         this.fireEvent("click", this, e);
25680     },
25681     
25682     onTriggerPress  : function(e)
25683     {   
25684         if (this.isVisible()) {
25685             this.hide();
25686         } else {
25687             this.show();
25688         }
25689     },
25690     
25691     isVisible : function(){
25692         return !this.hidden;
25693     },
25694     
25695     show : function()
25696     {
25697         this.fireEvent("beforeshow", this);
25698         
25699         this.hidden = false;
25700         this.el.addClass('open');
25701         
25702         Roo.get(document).on("mouseup", this.onMouseUp, this);
25703         
25704         this.fireEvent("show", this);
25705         
25706         
25707     },
25708     
25709     hide : function()
25710     {
25711         this.fireEvent("beforehide", this);
25712         
25713         this.hidden = true;
25714         this.el.removeClass('open');
25715         
25716         Roo.get(document).un("mouseup", this.onMouseUp);
25717         
25718         this.fireEvent("hide", this);
25719     },
25720     
25721     onMouseUp : function()
25722     {
25723         this.hide();
25724     }
25725     
25726 });
25727
25728  
25729  /*
25730  * - LGPL
25731  *
25732  * menu item
25733  * 
25734  */
25735 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25736
25737 /**
25738  * @class Roo.bootstrap.menu.Item
25739  * @extends Roo.bootstrap.Component
25740  * Bootstrap MenuItem class
25741  * @cfg {Boolean} submenu (true | false) default false
25742  * @cfg {String} html text of the item
25743  * @cfg {String} href the link
25744  * @cfg {Boolean} disable (true | false) default false
25745  * @cfg {Boolean} preventDefault (true | false) default true
25746  * @cfg {String} icon Font awesome icon
25747  * @cfg {String} pos Submenu align to (left | right) default right 
25748  * 
25749  * 
25750  * @constructor
25751  * Create a new Item
25752  * @param {Object} config The config object
25753  */
25754
25755
25756 Roo.bootstrap.menu.Item = function(config){
25757     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25758     this.addEvents({
25759         /**
25760          * @event mouseover
25761          * Fires when the mouse is hovering over this menu
25762          * @param {Roo.bootstrap.menu.Item} this
25763          * @param {Roo.EventObject} e
25764          */
25765         mouseover : true,
25766         /**
25767          * @event mouseout
25768          * Fires when the mouse exits this menu
25769          * @param {Roo.bootstrap.menu.Item} this
25770          * @param {Roo.EventObject} e
25771          */
25772         mouseout : true,
25773         // raw events
25774         /**
25775          * @event click
25776          * The raw click event for the entire grid.
25777          * @param {Roo.EventObject} e
25778          */
25779         click : true
25780     });
25781 };
25782
25783 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25784     
25785     submenu : false,
25786     href : '',
25787     html : '',
25788     preventDefault: true,
25789     disable : false,
25790     icon : false,
25791     pos : 'right',
25792     
25793     getAutoCreate : function()
25794     {
25795         var text = [
25796             {
25797                 tag : 'span',
25798                 cls : 'roo-menu-item-text',
25799                 html : this.html
25800             }
25801         ];
25802         
25803         if(this.icon){
25804             text.unshift({
25805                 tag : 'i',
25806                 cls : 'fa ' + this.icon
25807             })
25808         }
25809         
25810         var cfg = {
25811             tag : 'li',
25812             cn : [
25813                 {
25814                     tag : 'a',
25815                     href : this.href || '#',
25816                     cn : text
25817                 }
25818             ]
25819         };
25820         
25821         if(this.disable){
25822             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25823         }
25824         
25825         if(this.submenu){
25826             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25827             
25828             if(this.pos == 'left'){
25829                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25830             }
25831         }
25832         
25833         return cfg;
25834     },
25835     
25836     initEvents : function() 
25837     {
25838         this.el.on('mouseover', this.onMouseOver, this);
25839         this.el.on('mouseout', this.onMouseOut, this);
25840         
25841         this.el.select('a', true).first().on('click', this.onClick, this);
25842         
25843     },
25844     
25845     onClick : function(e)
25846     {
25847         if(this.preventDefault){
25848             e.preventDefault();
25849         }
25850         
25851         this.fireEvent("click", this, e);
25852     },
25853     
25854     onMouseOver : function(e)
25855     {
25856         if(this.submenu && this.pos == 'left'){
25857             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25858         }
25859         
25860         this.fireEvent("mouseover", this, e);
25861     },
25862     
25863     onMouseOut : function(e)
25864     {
25865         this.fireEvent("mouseout", this, e);
25866     }
25867 });
25868
25869  
25870
25871  /*
25872  * - LGPL
25873  *
25874  * menu separator
25875  * 
25876  */
25877 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25878
25879 /**
25880  * @class Roo.bootstrap.menu.Separator
25881  * @extends Roo.bootstrap.Component
25882  * Bootstrap Separator class
25883  * 
25884  * @constructor
25885  * Create a new Separator
25886  * @param {Object} config The config object
25887  */
25888
25889
25890 Roo.bootstrap.menu.Separator = function(config){
25891     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25892 };
25893
25894 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25895     
25896     getAutoCreate : function(){
25897         var cfg = {
25898             tag : 'li',
25899             cls: 'divider'
25900         };
25901         
25902         return cfg;
25903     }
25904    
25905 });
25906
25907  
25908
25909  /*
25910  * - LGPL
25911  *
25912  * Tooltip
25913  * 
25914  */
25915
25916 /**
25917  * @class Roo.bootstrap.Tooltip
25918  * Bootstrap Tooltip class
25919  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25920  * to determine which dom element triggers the tooltip.
25921  * 
25922  * It needs to add support for additional attributes like tooltip-position
25923  * 
25924  * @constructor
25925  * Create a new Toolti
25926  * @param {Object} config The config object
25927  */
25928
25929 Roo.bootstrap.Tooltip = function(config){
25930     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25931     
25932     this.alignment = Roo.bootstrap.Tooltip.alignment;
25933     
25934     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25935         this.alignment = config.alignment;
25936     }
25937     
25938 };
25939
25940 Roo.apply(Roo.bootstrap.Tooltip, {
25941     /**
25942      * @function init initialize tooltip monitoring.
25943      * @static
25944      */
25945     currentEl : false,
25946     currentTip : false,
25947     currentRegion : false,
25948     
25949     //  init : delay?
25950     
25951     init : function()
25952     {
25953         Roo.get(document).on('mouseover', this.enter ,this);
25954         Roo.get(document).on('mouseout', this.leave, this);
25955          
25956         
25957         this.currentTip = new Roo.bootstrap.Tooltip();
25958     },
25959     
25960     enter : function(ev)
25961     {
25962         var dom = ev.getTarget();
25963         
25964         //Roo.log(['enter',dom]);
25965         var el = Roo.fly(dom);
25966         if (this.currentEl) {
25967             //Roo.log(dom);
25968             //Roo.log(this.currentEl);
25969             //Roo.log(this.currentEl.contains(dom));
25970             if (this.currentEl == el) {
25971                 return;
25972             }
25973             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25974                 return;
25975             }
25976
25977         }
25978         
25979         if (this.currentTip.el) {
25980             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25981         }    
25982         //Roo.log(ev);
25983         
25984         if(!el || el.dom == document){
25985             return;
25986         }
25987         
25988         var bindEl = el;
25989         
25990         // you can not look for children, as if el is the body.. then everythign is the child..
25991         if (!el.attr('tooltip')) { //
25992             if (!el.select("[tooltip]").elements.length) {
25993                 return;
25994             }
25995             // is the mouse over this child...?
25996             bindEl = el.select("[tooltip]").first();
25997             var xy = ev.getXY();
25998             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25999                 //Roo.log("not in region.");
26000                 return;
26001             }
26002             //Roo.log("child element over..");
26003             
26004         }
26005         this.currentEl = bindEl;
26006         this.currentTip.bind(bindEl);
26007         this.currentRegion = Roo.lib.Region.getRegion(dom);
26008         this.currentTip.enter();
26009         
26010     },
26011     leave : function(ev)
26012     {
26013         var dom = ev.getTarget();
26014         //Roo.log(['leave',dom]);
26015         if (!this.currentEl) {
26016             return;
26017         }
26018         
26019         
26020         if (dom != this.currentEl.dom) {
26021             return;
26022         }
26023         var xy = ev.getXY();
26024         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26025             return;
26026         }
26027         // only activate leave if mouse cursor is outside... bounding box..
26028         
26029         
26030         
26031         
26032         if (this.currentTip) {
26033             this.currentTip.leave();
26034         }
26035         //Roo.log('clear currentEl');
26036         this.currentEl = false;
26037         
26038         
26039     },
26040     alignment : {
26041         'left' : ['r-l', [-2,0], 'right'],
26042         'right' : ['l-r', [2,0], 'left'],
26043         'bottom' : ['t-b', [0,2], 'top'],
26044         'top' : [ 'b-t', [0,-2], 'bottom']
26045     }
26046     
26047 });
26048
26049
26050 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26051     
26052     
26053     bindEl : false,
26054     
26055     delay : null, // can be { show : 300 , hide: 500}
26056     
26057     timeout : null,
26058     
26059     hoverState : null, //???
26060     
26061     placement : 'bottom', 
26062     
26063     alignment : false,
26064     
26065     getAutoCreate : function(){
26066     
26067         var cfg = {
26068            cls : 'tooltip',
26069            role : 'tooltip',
26070            cn : [
26071                 {
26072                     cls : 'tooltip-arrow'
26073                 },
26074                 {
26075                     cls : 'tooltip-inner'
26076                 }
26077            ]
26078         };
26079         
26080         return cfg;
26081     },
26082     bind : function(el)
26083     {
26084         this.bindEl = el;
26085     },
26086       
26087     
26088     enter : function () {
26089        
26090         if (this.timeout != null) {
26091             clearTimeout(this.timeout);
26092         }
26093         
26094         this.hoverState = 'in';
26095          //Roo.log("enter - show");
26096         if (!this.delay || !this.delay.show) {
26097             this.show();
26098             return;
26099         }
26100         var _t = this;
26101         this.timeout = setTimeout(function () {
26102             if (_t.hoverState == 'in') {
26103                 _t.show();
26104             }
26105         }, this.delay.show);
26106     },
26107     leave : function()
26108     {
26109         clearTimeout(this.timeout);
26110     
26111         this.hoverState = 'out';
26112          if (!this.delay || !this.delay.hide) {
26113             this.hide();
26114             return;
26115         }
26116        
26117         var _t = this;
26118         this.timeout = setTimeout(function () {
26119             //Roo.log("leave - timeout");
26120             
26121             if (_t.hoverState == 'out') {
26122                 _t.hide();
26123                 Roo.bootstrap.Tooltip.currentEl = false;
26124             }
26125         }, delay);
26126     },
26127     
26128     show : function (msg)
26129     {
26130         if (!this.el) {
26131             this.render(document.body);
26132         }
26133         // set content.
26134         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26135         
26136         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26137         
26138         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26139         
26140         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26141         
26142         var placement = typeof this.placement == 'function' ?
26143             this.placement.call(this, this.el, on_el) :
26144             this.placement;
26145             
26146         var autoToken = /\s?auto?\s?/i;
26147         var autoPlace = autoToken.test(placement);
26148         if (autoPlace) {
26149             placement = placement.replace(autoToken, '') || 'top';
26150         }
26151         
26152         //this.el.detach()
26153         //this.el.setXY([0,0]);
26154         this.el.show();
26155         //this.el.dom.style.display='block';
26156         
26157         //this.el.appendTo(on_el);
26158         
26159         var p = this.getPosition();
26160         var box = this.el.getBox();
26161         
26162         if (autoPlace) {
26163             // fixme..
26164         }
26165         
26166         var align = this.alignment[placement];
26167         
26168         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26169         
26170         if(placement == 'top' || placement == 'bottom'){
26171             if(xy[0] < 0){
26172                 placement = 'right';
26173             }
26174             
26175             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26176                 placement = 'left';
26177             }
26178             
26179             var scroll = Roo.select('body', true).first().getScroll();
26180             
26181             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26182                 placement = 'top';
26183             }
26184             
26185         }
26186         
26187         this.el.alignTo(this.bindEl, align[0],align[1]);
26188         //var arrow = this.el.select('.arrow',true).first();
26189         //arrow.set(align[2], 
26190         
26191         this.el.addClass(placement);
26192         
26193         this.el.addClass('in fade');
26194         
26195         this.hoverState = null;
26196         
26197         if (this.el.hasClass('fade')) {
26198             // fade it?
26199         }
26200         
26201     },
26202     hide : function()
26203     {
26204          
26205         if (!this.el) {
26206             return;
26207         }
26208         //this.el.setXY([0,0]);
26209         this.el.removeClass('in');
26210         //this.el.hide();
26211         
26212     }
26213     
26214 });
26215  
26216
26217  /*
26218  * - LGPL
26219  *
26220  * Location Picker
26221  * 
26222  */
26223
26224 /**
26225  * @class Roo.bootstrap.LocationPicker
26226  * @extends Roo.bootstrap.Component
26227  * Bootstrap LocationPicker class
26228  * @cfg {Number} latitude Position when init default 0
26229  * @cfg {Number} longitude Position when init default 0
26230  * @cfg {Number} zoom default 15
26231  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26232  * @cfg {Boolean} mapTypeControl default false
26233  * @cfg {Boolean} disableDoubleClickZoom default false
26234  * @cfg {Boolean} scrollwheel default true
26235  * @cfg {Boolean} streetViewControl default false
26236  * @cfg {Number} radius default 0
26237  * @cfg {String} locationName
26238  * @cfg {Boolean} draggable default true
26239  * @cfg {Boolean} enableAutocomplete default false
26240  * @cfg {Boolean} enableReverseGeocode default true
26241  * @cfg {String} markerTitle
26242  * 
26243  * @constructor
26244  * Create a new LocationPicker
26245  * @param {Object} config The config object
26246  */
26247
26248
26249 Roo.bootstrap.LocationPicker = function(config){
26250     
26251     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26252     
26253     this.addEvents({
26254         /**
26255          * @event initial
26256          * Fires when the picker initialized.
26257          * @param {Roo.bootstrap.LocationPicker} this
26258          * @param {Google Location} location
26259          */
26260         initial : true,
26261         /**
26262          * @event positionchanged
26263          * Fires when the picker position changed.
26264          * @param {Roo.bootstrap.LocationPicker} this
26265          * @param {Google Location} location
26266          */
26267         positionchanged : true,
26268         /**
26269          * @event resize
26270          * Fires when the map resize.
26271          * @param {Roo.bootstrap.LocationPicker} this
26272          */
26273         resize : true,
26274         /**
26275          * @event show
26276          * Fires when the map show.
26277          * @param {Roo.bootstrap.LocationPicker} this
26278          */
26279         show : true,
26280         /**
26281          * @event hide
26282          * Fires when the map hide.
26283          * @param {Roo.bootstrap.LocationPicker} this
26284          */
26285         hide : true,
26286         /**
26287          * @event mapClick
26288          * Fires when click the map.
26289          * @param {Roo.bootstrap.LocationPicker} this
26290          * @param {Map event} e
26291          */
26292         mapClick : true,
26293         /**
26294          * @event mapRightClick
26295          * Fires when right click the map.
26296          * @param {Roo.bootstrap.LocationPicker} this
26297          * @param {Map event} e
26298          */
26299         mapRightClick : true,
26300         /**
26301          * @event markerClick
26302          * Fires when click the marker.
26303          * @param {Roo.bootstrap.LocationPicker} this
26304          * @param {Map event} e
26305          */
26306         markerClick : true,
26307         /**
26308          * @event markerRightClick
26309          * Fires when right click the marker.
26310          * @param {Roo.bootstrap.LocationPicker} this
26311          * @param {Map event} e
26312          */
26313         markerRightClick : true,
26314         /**
26315          * @event OverlayViewDraw
26316          * Fires when OverlayView Draw
26317          * @param {Roo.bootstrap.LocationPicker} this
26318          */
26319         OverlayViewDraw : true,
26320         /**
26321          * @event OverlayViewOnAdd
26322          * Fires when OverlayView Draw
26323          * @param {Roo.bootstrap.LocationPicker} this
26324          */
26325         OverlayViewOnAdd : true,
26326         /**
26327          * @event OverlayViewOnRemove
26328          * Fires when OverlayView Draw
26329          * @param {Roo.bootstrap.LocationPicker} this
26330          */
26331         OverlayViewOnRemove : true,
26332         /**
26333          * @event OverlayViewShow
26334          * Fires when OverlayView Draw
26335          * @param {Roo.bootstrap.LocationPicker} this
26336          * @param {Pixel} cpx
26337          */
26338         OverlayViewShow : true,
26339         /**
26340          * @event OverlayViewHide
26341          * Fires when OverlayView Draw
26342          * @param {Roo.bootstrap.LocationPicker} this
26343          */
26344         OverlayViewHide : true,
26345         /**
26346          * @event loadexception
26347          * Fires when load google lib failed.
26348          * @param {Roo.bootstrap.LocationPicker} this
26349          */
26350         loadexception : true
26351     });
26352         
26353 };
26354
26355 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26356     
26357     gMapContext: false,
26358     
26359     latitude: 0,
26360     longitude: 0,
26361     zoom: 15,
26362     mapTypeId: false,
26363     mapTypeControl: false,
26364     disableDoubleClickZoom: false,
26365     scrollwheel: true,
26366     streetViewControl: false,
26367     radius: 0,
26368     locationName: '',
26369     draggable: true,
26370     enableAutocomplete: false,
26371     enableReverseGeocode: true,
26372     markerTitle: '',
26373     
26374     getAutoCreate: function()
26375     {
26376
26377         var cfg = {
26378             tag: 'div',
26379             cls: 'roo-location-picker'
26380         };
26381         
26382         return cfg
26383     },
26384     
26385     initEvents: function(ct, position)
26386     {       
26387         if(!this.el.getWidth() || this.isApplied()){
26388             return;
26389         }
26390         
26391         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26392         
26393         this.initial();
26394     },
26395     
26396     initial: function()
26397     {
26398         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26399             this.fireEvent('loadexception', this);
26400             return;
26401         }
26402         
26403         if(!this.mapTypeId){
26404             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26405         }
26406         
26407         this.gMapContext = this.GMapContext();
26408         
26409         this.initOverlayView();
26410         
26411         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26412         
26413         var _this = this;
26414                 
26415         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26416             _this.setPosition(_this.gMapContext.marker.position);
26417         });
26418         
26419         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26420             _this.fireEvent('mapClick', this, event);
26421             
26422         });
26423
26424         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26425             _this.fireEvent('mapRightClick', this, event);
26426             
26427         });
26428         
26429         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26430             _this.fireEvent('markerClick', this, event);
26431             
26432         });
26433
26434         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26435             _this.fireEvent('markerRightClick', this, event);
26436             
26437         });
26438         
26439         this.setPosition(this.gMapContext.location);
26440         
26441         this.fireEvent('initial', this, this.gMapContext.location);
26442     },
26443     
26444     initOverlayView: function()
26445     {
26446         var _this = this;
26447         
26448         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26449             
26450             draw: function()
26451             {
26452                 _this.fireEvent('OverlayViewDraw', _this);
26453             },
26454             
26455             onAdd: function()
26456             {
26457                 _this.fireEvent('OverlayViewOnAdd', _this);
26458             },
26459             
26460             onRemove: function()
26461             {
26462                 _this.fireEvent('OverlayViewOnRemove', _this);
26463             },
26464             
26465             show: function(cpx)
26466             {
26467                 _this.fireEvent('OverlayViewShow', _this, cpx);
26468             },
26469             
26470             hide: function()
26471             {
26472                 _this.fireEvent('OverlayViewHide', _this);
26473             }
26474             
26475         });
26476     },
26477     
26478     fromLatLngToContainerPixel: function(event)
26479     {
26480         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26481     },
26482     
26483     isApplied: function() 
26484     {
26485         return this.getGmapContext() == false ? false : true;
26486     },
26487     
26488     getGmapContext: function() 
26489     {
26490         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26491     },
26492     
26493     GMapContext: function() 
26494     {
26495         var position = new google.maps.LatLng(this.latitude, this.longitude);
26496         
26497         var _map = new google.maps.Map(this.el.dom, {
26498             center: position,
26499             zoom: this.zoom,
26500             mapTypeId: this.mapTypeId,
26501             mapTypeControl: this.mapTypeControl,
26502             disableDoubleClickZoom: this.disableDoubleClickZoom,
26503             scrollwheel: this.scrollwheel,
26504             streetViewControl: this.streetViewControl,
26505             locationName: this.locationName,
26506             draggable: this.draggable,
26507             enableAutocomplete: this.enableAutocomplete,
26508             enableReverseGeocode: this.enableReverseGeocode
26509         });
26510         
26511         var _marker = new google.maps.Marker({
26512             position: position,
26513             map: _map,
26514             title: this.markerTitle,
26515             draggable: this.draggable
26516         });
26517         
26518         return {
26519             map: _map,
26520             marker: _marker,
26521             circle: null,
26522             location: position,
26523             radius: this.radius,
26524             locationName: this.locationName,
26525             addressComponents: {
26526                 formatted_address: null,
26527                 addressLine1: null,
26528                 addressLine2: null,
26529                 streetName: null,
26530                 streetNumber: null,
26531                 city: null,
26532                 district: null,
26533                 state: null,
26534                 stateOrProvince: null
26535             },
26536             settings: this,
26537             domContainer: this.el.dom,
26538             geodecoder: new google.maps.Geocoder()
26539         };
26540     },
26541     
26542     drawCircle: function(center, radius, options) 
26543     {
26544         if (this.gMapContext.circle != null) {
26545             this.gMapContext.circle.setMap(null);
26546         }
26547         if (radius > 0) {
26548             radius *= 1;
26549             options = Roo.apply({}, options, {
26550                 strokeColor: "#0000FF",
26551                 strokeOpacity: .35,
26552                 strokeWeight: 2,
26553                 fillColor: "#0000FF",
26554                 fillOpacity: .2
26555             });
26556             
26557             options.map = this.gMapContext.map;
26558             options.radius = radius;
26559             options.center = center;
26560             this.gMapContext.circle = new google.maps.Circle(options);
26561             return this.gMapContext.circle;
26562         }
26563         
26564         return null;
26565     },
26566     
26567     setPosition: function(location) 
26568     {
26569         this.gMapContext.location = location;
26570         this.gMapContext.marker.setPosition(location);
26571         this.gMapContext.map.panTo(location);
26572         this.drawCircle(location, this.gMapContext.radius, {});
26573         
26574         var _this = this;
26575         
26576         if (this.gMapContext.settings.enableReverseGeocode) {
26577             this.gMapContext.geodecoder.geocode({
26578                 latLng: this.gMapContext.location
26579             }, function(results, status) {
26580                 
26581                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26582                     _this.gMapContext.locationName = results[0].formatted_address;
26583                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26584                     
26585                     _this.fireEvent('positionchanged', this, location);
26586                 }
26587             });
26588             
26589             return;
26590         }
26591         
26592         this.fireEvent('positionchanged', this, location);
26593     },
26594     
26595     resize: function()
26596     {
26597         google.maps.event.trigger(this.gMapContext.map, "resize");
26598         
26599         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26600         
26601         this.fireEvent('resize', this);
26602     },
26603     
26604     setPositionByLatLng: function(latitude, longitude)
26605     {
26606         this.setPosition(new google.maps.LatLng(latitude, longitude));
26607     },
26608     
26609     getCurrentPosition: function() 
26610     {
26611         return {
26612             latitude: this.gMapContext.location.lat(),
26613             longitude: this.gMapContext.location.lng()
26614         };
26615     },
26616     
26617     getAddressName: function() 
26618     {
26619         return this.gMapContext.locationName;
26620     },
26621     
26622     getAddressComponents: function() 
26623     {
26624         return this.gMapContext.addressComponents;
26625     },
26626     
26627     address_component_from_google_geocode: function(address_components) 
26628     {
26629         var result = {};
26630         
26631         for (var i = 0; i < address_components.length; i++) {
26632             var component = address_components[i];
26633             if (component.types.indexOf("postal_code") >= 0) {
26634                 result.postalCode = component.short_name;
26635             } else if (component.types.indexOf("street_number") >= 0) {
26636                 result.streetNumber = component.short_name;
26637             } else if (component.types.indexOf("route") >= 0) {
26638                 result.streetName = component.short_name;
26639             } else if (component.types.indexOf("neighborhood") >= 0) {
26640                 result.city = component.short_name;
26641             } else if (component.types.indexOf("locality") >= 0) {
26642                 result.city = component.short_name;
26643             } else if (component.types.indexOf("sublocality") >= 0) {
26644                 result.district = component.short_name;
26645             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26646                 result.stateOrProvince = component.short_name;
26647             } else if (component.types.indexOf("country") >= 0) {
26648                 result.country = component.short_name;
26649             }
26650         }
26651         
26652         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26653         result.addressLine2 = "";
26654         return result;
26655     },
26656     
26657     setZoomLevel: function(zoom)
26658     {
26659         this.gMapContext.map.setZoom(zoom);
26660     },
26661     
26662     show: function()
26663     {
26664         if(!this.el){
26665             return;
26666         }
26667         
26668         this.el.show();
26669         
26670         this.resize();
26671         
26672         this.fireEvent('show', this);
26673     },
26674     
26675     hide: function()
26676     {
26677         if(!this.el){
26678             return;
26679         }
26680         
26681         this.el.hide();
26682         
26683         this.fireEvent('hide', this);
26684     }
26685     
26686 });
26687
26688 Roo.apply(Roo.bootstrap.LocationPicker, {
26689     
26690     OverlayView : function(map, options)
26691     {
26692         options = options || {};
26693         
26694         this.setMap(map);
26695     }
26696     
26697     
26698 });/*
26699  * - LGPL
26700  *
26701  * Alert
26702  * 
26703  */
26704
26705 /**
26706  * @class Roo.bootstrap.Alert
26707  * @extends Roo.bootstrap.Component
26708  * Bootstrap Alert class
26709  * @cfg {String} title The title of alert
26710  * @cfg {String} html The content of alert
26711  * @cfg {String} weight (  success | info | warning | danger )
26712  * @cfg {String} faicon font-awesomeicon
26713  * 
26714  * @constructor
26715  * Create a new alert
26716  * @param {Object} config The config object
26717  */
26718
26719
26720 Roo.bootstrap.Alert = function(config){
26721     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26722     
26723 };
26724
26725 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26726     
26727     title: '',
26728     html: '',
26729     weight: false,
26730     faicon: false,
26731     
26732     getAutoCreate : function()
26733     {
26734         
26735         var cfg = {
26736             tag : 'div',
26737             cls : 'alert',
26738             cn : [
26739                 {
26740                     tag : 'i',
26741                     cls : 'roo-alert-icon'
26742                     
26743                 },
26744                 {
26745                     tag : 'b',
26746                     cls : 'roo-alert-title',
26747                     html : this.title
26748                 },
26749                 {
26750                     tag : 'span',
26751                     cls : 'roo-alert-text',
26752                     html : this.html
26753                 }
26754             ]
26755         };
26756         
26757         if(this.faicon){
26758             cfg.cn[0].cls += ' fa ' + this.faicon;
26759         }
26760         
26761         if(this.weight){
26762             cfg.cls += ' alert-' + this.weight;
26763         }
26764         
26765         return cfg;
26766     },
26767     
26768     initEvents: function() 
26769     {
26770         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26771     },
26772     
26773     setTitle : function(str)
26774     {
26775         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26776     },
26777     
26778     setText : function(str)
26779     {
26780         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26781     },
26782     
26783     setWeight : function(weight)
26784     {
26785         if(this.weight){
26786             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26787         }
26788         
26789         this.weight = weight;
26790         
26791         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26792     },
26793     
26794     setIcon : function(icon)
26795     {
26796         if(this.faicon){
26797             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26798         }
26799         
26800         this.faicon = icon;
26801         
26802         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26803     },
26804     
26805     hide: function() 
26806     {
26807         this.el.hide();   
26808     },
26809     
26810     show: function() 
26811     {  
26812         this.el.show();   
26813     }
26814     
26815 });
26816
26817  
26818 /*
26819 * Licence: LGPL
26820 */
26821
26822 /**
26823  * @class Roo.bootstrap.UploadCropbox
26824  * @extends Roo.bootstrap.Component
26825  * Bootstrap UploadCropbox class
26826  * @cfg {String} emptyText show when image has been loaded
26827  * @cfg {String} rotateNotify show when image too small to rotate
26828  * @cfg {Number} errorTimeout default 3000
26829  * @cfg {Number} minWidth default 300
26830  * @cfg {Number} minHeight default 300
26831  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26832  * @cfg {Boolean} isDocument (true|false) default false
26833  * @cfg {String} url action url
26834  * @cfg {String} paramName default 'imageUpload'
26835  * @cfg {String} method default POST
26836  * @cfg {Boolean} loadMask (true|false) default true
26837  * @cfg {Boolean} loadingText default 'Loading...'
26838  * 
26839  * @constructor
26840  * Create a new UploadCropbox
26841  * @param {Object} config The config object
26842  */
26843
26844 Roo.bootstrap.UploadCropbox = function(config){
26845     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26846     
26847     this.addEvents({
26848         /**
26849          * @event beforeselectfile
26850          * Fire before select file
26851          * @param {Roo.bootstrap.UploadCropbox} this
26852          */
26853         "beforeselectfile" : true,
26854         /**
26855          * @event initial
26856          * Fire after initEvent
26857          * @param {Roo.bootstrap.UploadCropbox} this
26858          */
26859         "initial" : true,
26860         /**
26861          * @event crop
26862          * Fire after initEvent
26863          * @param {Roo.bootstrap.UploadCropbox} this
26864          * @param {String} data
26865          */
26866         "crop" : true,
26867         /**
26868          * @event prepare
26869          * Fire when preparing the file data
26870          * @param {Roo.bootstrap.UploadCropbox} this
26871          * @param {Object} file
26872          */
26873         "prepare" : true,
26874         /**
26875          * @event exception
26876          * Fire when get exception
26877          * @param {Roo.bootstrap.UploadCropbox} this
26878          * @param {XMLHttpRequest} xhr
26879          */
26880         "exception" : true,
26881         /**
26882          * @event beforeloadcanvas
26883          * Fire before load the canvas
26884          * @param {Roo.bootstrap.UploadCropbox} this
26885          * @param {String} src
26886          */
26887         "beforeloadcanvas" : true,
26888         /**
26889          * @event trash
26890          * Fire when trash image
26891          * @param {Roo.bootstrap.UploadCropbox} this
26892          */
26893         "trash" : true,
26894         /**
26895          * @event download
26896          * Fire when download the image
26897          * @param {Roo.bootstrap.UploadCropbox} this
26898          */
26899         "download" : true,
26900         /**
26901          * @event footerbuttonclick
26902          * Fire when footerbuttonclick
26903          * @param {Roo.bootstrap.UploadCropbox} this
26904          * @param {String} type
26905          */
26906         "footerbuttonclick" : true,
26907         /**
26908          * @event resize
26909          * Fire when resize
26910          * @param {Roo.bootstrap.UploadCropbox} this
26911          */
26912         "resize" : true,
26913         /**
26914          * @event rotate
26915          * Fire when rotate the image
26916          * @param {Roo.bootstrap.UploadCropbox} this
26917          * @param {String} pos
26918          */
26919         "rotate" : true,
26920         /**
26921          * @event inspect
26922          * Fire when inspect the file
26923          * @param {Roo.bootstrap.UploadCropbox} this
26924          * @param {Object} file
26925          */
26926         "inspect" : true,
26927         /**
26928          * @event upload
26929          * Fire when xhr upload the file
26930          * @param {Roo.bootstrap.UploadCropbox} this
26931          * @param {Object} data
26932          */
26933         "upload" : true,
26934         /**
26935          * @event arrange
26936          * Fire when arrange the file data
26937          * @param {Roo.bootstrap.UploadCropbox} this
26938          * @param {Object} formData
26939          */
26940         "arrange" : true
26941     });
26942     
26943     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26944 };
26945
26946 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26947     
26948     emptyText : 'Click to upload image',
26949     rotateNotify : 'Image is too small to rotate',
26950     errorTimeout : 3000,
26951     scale : 0,
26952     baseScale : 1,
26953     rotate : 0,
26954     dragable : false,
26955     pinching : false,
26956     mouseX : 0,
26957     mouseY : 0,
26958     cropData : false,
26959     minWidth : 300,
26960     minHeight : 300,
26961     file : false,
26962     exif : {},
26963     baseRotate : 1,
26964     cropType : 'image/jpeg',
26965     buttons : false,
26966     canvasLoaded : false,
26967     isDocument : false,
26968     method : 'POST',
26969     paramName : 'imageUpload',
26970     loadMask : true,
26971     loadingText : 'Loading...',
26972     maskEl : false,
26973     
26974     getAutoCreate : function()
26975     {
26976         var cfg = {
26977             tag : 'div',
26978             cls : 'roo-upload-cropbox',
26979             cn : [
26980                 {
26981                     tag : 'input',
26982                     cls : 'roo-upload-cropbox-selector',
26983                     type : 'file'
26984                 },
26985                 {
26986                     tag : 'div',
26987                     cls : 'roo-upload-cropbox-body',
26988                     style : 'cursor:pointer',
26989                     cn : [
26990                         {
26991                             tag : 'div',
26992                             cls : 'roo-upload-cropbox-preview'
26993                         },
26994                         {
26995                             tag : 'div',
26996                             cls : 'roo-upload-cropbox-thumb'
26997                         },
26998                         {
26999                             tag : 'div',
27000                             cls : 'roo-upload-cropbox-empty-notify',
27001                             html : this.emptyText
27002                         },
27003                         {
27004                             tag : 'div',
27005                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27006                             html : this.rotateNotify
27007                         }
27008                     ]
27009                 },
27010                 {
27011                     tag : 'div',
27012                     cls : 'roo-upload-cropbox-footer',
27013                     cn : {
27014                         tag : 'div',
27015                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27016                         cn : []
27017                     }
27018                 }
27019             ]
27020         };
27021         
27022         return cfg;
27023     },
27024     
27025     onRender : function(ct, position)
27026     {
27027         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27028         
27029         if (this.buttons.length) {
27030             
27031             Roo.each(this.buttons, function(bb) {
27032                 
27033                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27034                 
27035                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27036                 
27037             }, this);
27038         }
27039         
27040         if(this.loadMask){
27041             this.maskEl = this.el;
27042         }
27043     },
27044     
27045     initEvents : function()
27046     {
27047         this.urlAPI = (window.createObjectURL && window) || 
27048                                 (window.URL && URL.revokeObjectURL && URL) || 
27049                                 (window.webkitURL && webkitURL);
27050                         
27051         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27052         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27053         
27054         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27055         this.selectorEl.hide();
27056         
27057         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27058         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27059         
27060         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27061         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27062         this.thumbEl.hide();
27063         
27064         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27065         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27066         
27067         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27068         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27069         this.errorEl.hide();
27070         
27071         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27072         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27073         this.footerEl.hide();
27074         
27075         this.setThumbBoxSize();
27076         
27077         this.bind();
27078         
27079         this.resize();
27080         
27081         this.fireEvent('initial', this);
27082     },
27083
27084     bind : function()
27085     {
27086         var _this = this;
27087         
27088         window.addEventListener("resize", function() { _this.resize(); } );
27089         
27090         this.bodyEl.on('click', this.beforeSelectFile, this);
27091         
27092         if(Roo.isTouch){
27093             this.bodyEl.on('touchstart', this.onTouchStart, this);
27094             this.bodyEl.on('touchmove', this.onTouchMove, this);
27095             this.bodyEl.on('touchend', this.onTouchEnd, this);
27096         }
27097         
27098         if(!Roo.isTouch){
27099             this.bodyEl.on('mousedown', this.onMouseDown, this);
27100             this.bodyEl.on('mousemove', this.onMouseMove, this);
27101             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27102             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27103             Roo.get(document).on('mouseup', this.onMouseUp, this);
27104         }
27105         
27106         this.selectorEl.on('change', this.onFileSelected, this);
27107     },
27108     
27109     reset : function()
27110     {    
27111         this.scale = 0;
27112         this.baseScale = 1;
27113         this.rotate = 0;
27114         this.baseRotate = 1;
27115         this.dragable = false;
27116         this.pinching = false;
27117         this.mouseX = 0;
27118         this.mouseY = 0;
27119         this.cropData = false;
27120         this.notifyEl.dom.innerHTML = this.emptyText;
27121         
27122         this.selectorEl.dom.value = '';
27123         
27124     },
27125     
27126     resize : function()
27127     {
27128         if(this.fireEvent('resize', this) != false){
27129             this.setThumbBoxPosition();
27130             this.setCanvasPosition();
27131         }
27132     },
27133     
27134     onFooterButtonClick : function(e, el, o, type)
27135     {
27136         switch (type) {
27137             case 'rotate-left' :
27138                 this.onRotateLeft(e);
27139                 break;
27140             case 'rotate-right' :
27141                 this.onRotateRight(e);
27142                 break;
27143             case 'picture' :
27144                 this.beforeSelectFile(e);
27145                 break;
27146             case 'trash' :
27147                 this.trash(e);
27148                 break;
27149             case 'crop' :
27150                 this.crop(e);
27151                 break;
27152             case 'download' :
27153                 this.download(e);
27154                 break;
27155             default :
27156                 break;
27157         }
27158         
27159         this.fireEvent('footerbuttonclick', this, type);
27160     },
27161     
27162     beforeSelectFile : function(e)
27163     {
27164         e.preventDefault();
27165         
27166         if(this.fireEvent('beforeselectfile', this) != false){
27167             this.selectorEl.dom.click();
27168         }
27169     },
27170     
27171     onFileSelected : function(e)
27172     {
27173         e.preventDefault();
27174         
27175         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27176             return;
27177         }
27178         
27179         var file = this.selectorEl.dom.files[0];
27180         
27181         if(this.fireEvent('inspect', this, file) != false){
27182             this.prepare(file);
27183         }
27184         
27185     },
27186     
27187     trash : function(e)
27188     {
27189         this.fireEvent('trash', this);
27190     },
27191     
27192     download : function(e)
27193     {
27194         this.fireEvent('download', this);
27195     },
27196     
27197     loadCanvas : function(src)
27198     {   
27199         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27200             
27201             this.reset();
27202             
27203             this.imageEl = document.createElement('img');
27204             
27205             var _this = this;
27206             
27207             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27208             
27209             this.imageEl.src = src;
27210         }
27211     },
27212     
27213     onLoadCanvas : function()
27214     {   
27215         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27216         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27217         
27218         this.bodyEl.un('click', this.beforeSelectFile, this);
27219         
27220         this.notifyEl.hide();
27221         this.thumbEl.show();
27222         this.footerEl.show();
27223         
27224         this.baseRotateLevel();
27225         
27226         if(this.isDocument){
27227             this.setThumbBoxSize();
27228         }
27229         
27230         this.setThumbBoxPosition();
27231         
27232         this.baseScaleLevel();
27233         
27234         this.draw();
27235         
27236         this.resize();
27237         
27238         this.canvasLoaded = true;
27239         
27240         if(this.loadMask){
27241             this.maskEl.unmask();
27242         }
27243         
27244     },
27245     
27246     setCanvasPosition : function()
27247     {   
27248         if(!this.canvasEl){
27249             return;
27250         }
27251         
27252         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27253         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27254         
27255         this.previewEl.setLeft(pw);
27256         this.previewEl.setTop(ph);
27257         
27258     },
27259     
27260     onMouseDown : function(e)
27261     {   
27262         e.stopEvent();
27263         
27264         this.dragable = true;
27265         this.pinching = false;
27266         
27267         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27268             this.dragable = false;
27269             return;
27270         }
27271         
27272         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27273         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27274         
27275     },
27276     
27277     onMouseMove : function(e)
27278     {   
27279         e.stopEvent();
27280         
27281         if(!this.canvasLoaded){
27282             return;
27283         }
27284         
27285         if (!this.dragable){
27286             return;
27287         }
27288         
27289         var minX = Math.ceil(this.thumbEl.getLeft(true));
27290         var minY = Math.ceil(this.thumbEl.getTop(true));
27291         
27292         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27293         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27294         
27295         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27296         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27297         
27298         x = x - this.mouseX;
27299         y = y - this.mouseY;
27300         
27301         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27302         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27303         
27304         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27305         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27306         
27307         this.previewEl.setLeft(bgX);
27308         this.previewEl.setTop(bgY);
27309         
27310         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27311         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27312     },
27313     
27314     onMouseUp : function(e)
27315     {   
27316         e.stopEvent();
27317         
27318         this.dragable = false;
27319     },
27320     
27321     onMouseWheel : function(e)
27322     {   
27323         e.stopEvent();
27324         
27325         this.startScale = this.scale;
27326         
27327         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27328         
27329         if(!this.zoomable()){
27330             this.scale = this.startScale;
27331             return;
27332         }
27333         
27334         this.draw();
27335         
27336         return;
27337     },
27338     
27339     zoomable : function()
27340     {
27341         var minScale = this.thumbEl.getWidth() / this.minWidth;
27342         
27343         if(this.minWidth < this.minHeight){
27344             minScale = this.thumbEl.getHeight() / this.minHeight;
27345         }
27346         
27347         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27348         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27349         
27350         if(
27351                 this.isDocument &&
27352                 (this.rotate == 0 || this.rotate == 180) && 
27353                 (
27354                     width > this.imageEl.OriginWidth || 
27355                     height > this.imageEl.OriginHeight ||
27356                     (width < this.minWidth && height < this.minHeight)
27357                 )
27358         ){
27359             return false;
27360         }
27361         
27362         if(
27363                 this.isDocument &&
27364                 (this.rotate == 90 || this.rotate == 270) && 
27365                 (
27366                     width > this.imageEl.OriginWidth || 
27367                     height > this.imageEl.OriginHeight ||
27368                     (width < this.minHeight && height < this.minWidth)
27369                 )
27370         ){
27371             return false;
27372         }
27373         
27374         if(
27375                 !this.isDocument &&
27376                 (this.rotate == 0 || this.rotate == 180) && 
27377                 (
27378                     width < this.minWidth || 
27379                     width > this.imageEl.OriginWidth || 
27380                     height < this.minHeight || 
27381                     height > this.imageEl.OriginHeight
27382                 )
27383         ){
27384             return false;
27385         }
27386         
27387         if(
27388                 !this.isDocument &&
27389                 (this.rotate == 90 || this.rotate == 270) && 
27390                 (
27391                     width < this.minHeight || 
27392                     width > this.imageEl.OriginWidth || 
27393                     height < this.minWidth || 
27394                     height > this.imageEl.OriginHeight
27395                 )
27396         ){
27397             return false;
27398         }
27399         
27400         return true;
27401         
27402     },
27403     
27404     onRotateLeft : function(e)
27405     {   
27406         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27407             
27408             var minScale = this.thumbEl.getWidth() / this.minWidth;
27409             
27410             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27411             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27412             
27413             this.startScale = this.scale;
27414             
27415             while (this.getScaleLevel() < minScale){
27416             
27417                 this.scale = this.scale + 1;
27418                 
27419                 if(!this.zoomable()){
27420                     break;
27421                 }
27422                 
27423                 if(
27424                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27425                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27426                 ){
27427                     continue;
27428                 }
27429                 
27430                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27431
27432                 this.draw();
27433                 
27434                 return;
27435             }
27436             
27437             this.scale = this.startScale;
27438             
27439             this.onRotateFail();
27440             
27441             return false;
27442         }
27443         
27444         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27445
27446         if(this.isDocument){
27447             this.setThumbBoxSize();
27448             this.setThumbBoxPosition();
27449             this.setCanvasPosition();
27450         }
27451         
27452         this.draw();
27453         
27454         this.fireEvent('rotate', this, 'left');
27455         
27456     },
27457     
27458     onRotateRight : function(e)
27459     {
27460         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27461             
27462             var minScale = this.thumbEl.getWidth() / this.minWidth;
27463         
27464             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27465             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27466             
27467             this.startScale = this.scale;
27468             
27469             while (this.getScaleLevel() < minScale){
27470             
27471                 this.scale = this.scale + 1;
27472                 
27473                 if(!this.zoomable()){
27474                     break;
27475                 }
27476                 
27477                 if(
27478                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27479                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27480                 ){
27481                     continue;
27482                 }
27483                 
27484                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27485
27486                 this.draw();
27487                 
27488                 return;
27489             }
27490             
27491             this.scale = this.startScale;
27492             
27493             this.onRotateFail();
27494             
27495             return false;
27496         }
27497         
27498         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27499
27500         if(this.isDocument){
27501             this.setThumbBoxSize();
27502             this.setThumbBoxPosition();
27503             this.setCanvasPosition();
27504         }
27505         
27506         this.draw();
27507         
27508         this.fireEvent('rotate', this, 'right');
27509     },
27510     
27511     onRotateFail : function()
27512     {
27513         this.errorEl.show(true);
27514         
27515         var _this = this;
27516         
27517         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27518     },
27519     
27520     draw : function()
27521     {
27522         this.previewEl.dom.innerHTML = '';
27523         
27524         var canvasEl = document.createElement("canvas");
27525         
27526         var contextEl = canvasEl.getContext("2d");
27527         
27528         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27529         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27530         var center = this.imageEl.OriginWidth / 2;
27531         
27532         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27533             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27534             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27535             center = this.imageEl.OriginHeight / 2;
27536         }
27537         
27538         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27539         
27540         contextEl.translate(center, center);
27541         contextEl.rotate(this.rotate * Math.PI / 180);
27542
27543         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27544         
27545         this.canvasEl = document.createElement("canvas");
27546         
27547         this.contextEl = this.canvasEl.getContext("2d");
27548         
27549         switch (this.rotate) {
27550             case 0 :
27551                 
27552                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27553                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27554                 
27555                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27556                 
27557                 break;
27558             case 90 : 
27559                 
27560                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27561                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27562                 
27563                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27564                     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);
27565                     break;
27566                 }
27567                 
27568                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27569                 
27570                 break;
27571             case 180 :
27572                 
27573                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27574                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27575                 
27576                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27577                     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);
27578                     break;
27579                 }
27580                 
27581                 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);
27582                 
27583                 break;
27584             case 270 :
27585                 
27586                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27587                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27588         
27589                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27590                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27591                     break;
27592                 }
27593                 
27594                 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);
27595                 
27596                 break;
27597             default : 
27598                 break;
27599         }
27600         
27601         this.previewEl.appendChild(this.canvasEl);
27602         
27603         this.setCanvasPosition();
27604     },
27605     
27606     crop : function()
27607     {
27608         if(!this.canvasLoaded){
27609             return;
27610         }
27611         
27612         var imageCanvas = document.createElement("canvas");
27613         
27614         var imageContext = imageCanvas.getContext("2d");
27615         
27616         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27617         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27618         
27619         var center = imageCanvas.width / 2;
27620         
27621         imageContext.translate(center, center);
27622         
27623         imageContext.rotate(this.rotate * Math.PI / 180);
27624         
27625         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27626         
27627         var canvas = document.createElement("canvas");
27628         
27629         var context = canvas.getContext("2d");
27630                 
27631         canvas.width = this.minWidth;
27632         canvas.height = this.minHeight;
27633
27634         switch (this.rotate) {
27635             case 0 :
27636                 
27637                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27638                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27639                 
27640                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27641                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27642                 
27643                 var targetWidth = this.minWidth - 2 * x;
27644                 var targetHeight = this.minHeight - 2 * y;
27645                 
27646                 var scale = 1;
27647                 
27648                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27649                     scale = targetWidth / width;
27650                 }
27651                 
27652                 if(x > 0 && y == 0){
27653                     scale = targetHeight / height;
27654                 }
27655                 
27656                 if(x > 0 && y > 0){
27657                     scale = targetWidth / width;
27658                     
27659                     if(width < height){
27660                         scale = targetHeight / height;
27661                     }
27662                 }
27663                 
27664                 context.scale(scale, scale);
27665                 
27666                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27667                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27668
27669                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27670                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27671
27672                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27673                 
27674                 break;
27675             case 90 : 
27676                 
27677                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27678                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27679                 
27680                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27681                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27682                 
27683                 var targetWidth = this.minWidth - 2 * x;
27684                 var targetHeight = this.minHeight - 2 * y;
27685                 
27686                 var scale = 1;
27687                 
27688                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27689                     scale = targetWidth / width;
27690                 }
27691                 
27692                 if(x > 0 && y == 0){
27693                     scale = targetHeight / height;
27694                 }
27695                 
27696                 if(x > 0 && y > 0){
27697                     scale = targetWidth / width;
27698                     
27699                     if(width < height){
27700                         scale = targetHeight / height;
27701                     }
27702                 }
27703                 
27704                 context.scale(scale, scale);
27705                 
27706                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27707                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27708
27709                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27710                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27711                 
27712                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27713                 
27714                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27715                 
27716                 break;
27717             case 180 :
27718                 
27719                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27720                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27721                 
27722                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27723                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27724                 
27725                 var targetWidth = this.minWidth - 2 * x;
27726                 var targetHeight = this.minHeight - 2 * y;
27727                 
27728                 var scale = 1;
27729                 
27730                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27731                     scale = targetWidth / width;
27732                 }
27733                 
27734                 if(x > 0 && y == 0){
27735                     scale = targetHeight / height;
27736                 }
27737                 
27738                 if(x > 0 && y > 0){
27739                     scale = targetWidth / width;
27740                     
27741                     if(width < height){
27742                         scale = targetHeight / height;
27743                     }
27744                 }
27745                 
27746                 context.scale(scale, scale);
27747                 
27748                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27749                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27750
27751                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27752                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27753
27754                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27755                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27756                 
27757                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27758                 
27759                 break;
27760             case 270 :
27761                 
27762                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27763                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27764                 
27765                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27766                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27767                 
27768                 var targetWidth = this.minWidth - 2 * x;
27769                 var targetHeight = this.minHeight - 2 * y;
27770                 
27771                 var scale = 1;
27772                 
27773                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27774                     scale = targetWidth / width;
27775                 }
27776                 
27777                 if(x > 0 && y == 0){
27778                     scale = targetHeight / height;
27779                 }
27780                 
27781                 if(x > 0 && y > 0){
27782                     scale = targetWidth / width;
27783                     
27784                     if(width < height){
27785                         scale = targetHeight / height;
27786                     }
27787                 }
27788                 
27789                 context.scale(scale, scale);
27790                 
27791                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27792                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27793
27794                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27795                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27796                 
27797                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27798                 
27799                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27800                 
27801                 break;
27802             default : 
27803                 break;
27804         }
27805         
27806         this.cropData = canvas.toDataURL(this.cropType);
27807         
27808         if(this.fireEvent('crop', this, this.cropData) !== false){
27809             this.process(this.file, this.cropData);
27810         }
27811         
27812         return;
27813         
27814     },
27815     
27816     setThumbBoxSize : function()
27817     {
27818         var width, height;
27819         
27820         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27821             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27822             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27823             
27824             this.minWidth = width;
27825             this.minHeight = height;
27826             
27827             if(this.rotate == 90 || this.rotate == 270){
27828                 this.minWidth = height;
27829                 this.minHeight = width;
27830             }
27831         }
27832         
27833         height = 300;
27834         width = Math.ceil(this.minWidth * height / this.minHeight);
27835         
27836         if(this.minWidth > this.minHeight){
27837             width = 300;
27838             height = Math.ceil(this.minHeight * width / this.minWidth);
27839         }
27840         
27841         this.thumbEl.setStyle({
27842             width : width + 'px',
27843             height : height + 'px'
27844         });
27845
27846         return;
27847             
27848     },
27849     
27850     setThumbBoxPosition : function()
27851     {
27852         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27853         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27854         
27855         this.thumbEl.setLeft(x);
27856         this.thumbEl.setTop(y);
27857         
27858     },
27859     
27860     baseRotateLevel : function()
27861     {
27862         this.baseRotate = 1;
27863         
27864         if(
27865                 typeof(this.exif) != 'undefined' &&
27866                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27867                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27868         ){
27869             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27870         }
27871         
27872         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27873         
27874     },
27875     
27876     baseScaleLevel : function()
27877     {
27878         var width, height;
27879         
27880         if(this.isDocument){
27881             
27882             if(this.baseRotate == 6 || this.baseRotate == 8){
27883             
27884                 height = this.thumbEl.getHeight();
27885                 this.baseScale = height / this.imageEl.OriginWidth;
27886
27887                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27888                     width = this.thumbEl.getWidth();
27889                     this.baseScale = width / this.imageEl.OriginHeight;
27890                 }
27891
27892                 return;
27893             }
27894
27895             height = this.thumbEl.getHeight();
27896             this.baseScale = height / this.imageEl.OriginHeight;
27897
27898             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27899                 width = this.thumbEl.getWidth();
27900                 this.baseScale = width / this.imageEl.OriginWidth;
27901             }
27902
27903             return;
27904         }
27905         
27906         if(this.baseRotate == 6 || this.baseRotate == 8){
27907             
27908             width = this.thumbEl.getHeight();
27909             this.baseScale = width / this.imageEl.OriginHeight;
27910             
27911             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27912                 height = this.thumbEl.getWidth();
27913                 this.baseScale = height / this.imageEl.OriginHeight;
27914             }
27915             
27916             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27917                 height = this.thumbEl.getWidth();
27918                 this.baseScale = height / this.imageEl.OriginHeight;
27919                 
27920                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27921                     width = this.thumbEl.getHeight();
27922                     this.baseScale = width / this.imageEl.OriginWidth;
27923                 }
27924             }
27925             
27926             return;
27927         }
27928         
27929         width = this.thumbEl.getWidth();
27930         this.baseScale = width / this.imageEl.OriginWidth;
27931         
27932         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27933             height = this.thumbEl.getHeight();
27934             this.baseScale = height / this.imageEl.OriginHeight;
27935         }
27936         
27937         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27938             
27939             height = this.thumbEl.getHeight();
27940             this.baseScale = height / this.imageEl.OriginHeight;
27941             
27942             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27943                 width = this.thumbEl.getWidth();
27944                 this.baseScale = width / this.imageEl.OriginWidth;
27945             }
27946             
27947         }
27948         
27949         return;
27950     },
27951     
27952     getScaleLevel : function()
27953     {
27954         return this.baseScale * Math.pow(1.1, this.scale);
27955     },
27956     
27957     onTouchStart : function(e)
27958     {
27959         if(!this.canvasLoaded){
27960             this.beforeSelectFile(e);
27961             return;
27962         }
27963         
27964         var touches = e.browserEvent.touches;
27965         
27966         if(!touches){
27967             return;
27968         }
27969         
27970         if(touches.length == 1){
27971             this.onMouseDown(e);
27972             return;
27973         }
27974         
27975         if(touches.length != 2){
27976             return;
27977         }
27978         
27979         var coords = [];
27980         
27981         for(var i = 0, finger; finger = touches[i]; i++){
27982             coords.push(finger.pageX, finger.pageY);
27983         }
27984         
27985         var x = Math.pow(coords[0] - coords[2], 2);
27986         var y = Math.pow(coords[1] - coords[3], 2);
27987         
27988         this.startDistance = Math.sqrt(x + y);
27989         
27990         this.startScale = this.scale;
27991         
27992         this.pinching = true;
27993         this.dragable = false;
27994         
27995     },
27996     
27997     onTouchMove : function(e)
27998     {
27999         if(!this.pinching && !this.dragable){
28000             return;
28001         }
28002         
28003         var touches = e.browserEvent.touches;
28004         
28005         if(!touches){
28006             return;
28007         }
28008         
28009         if(this.dragable){
28010             this.onMouseMove(e);
28011             return;
28012         }
28013         
28014         var coords = [];
28015         
28016         for(var i = 0, finger; finger = touches[i]; i++){
28017             coords.push(finger.pageX, finger.pageY);
28018         }
28019         
28020         var x = Math.pow(coords[0] - coords[2], 2);
28021         var y = Math.pow(coords[1] - coords[3], 2);
28022         
28023         this.endDistance = Math.sqrt(x + y);
28024         
28025         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28026         
28027         if(!this.zoomable()){
28028             this.scale = this.startScale;
28029             return;
28030         }
28031         
28032         this.draw();
28033         
28034     },
28035     
28036     onTouchEnd : function(e)
28037     {
28038         this.pinching = false;
28039         this.dragable = false;
28040         
28041     },
28042     
28043     process : function(file, crop)
28044     {
28045         if(this.loadMask){
28046             this.maskEl.mask(this.loadingText);
28047         }
28048         
28049         this.xhr = new XMLHttpRequest();
28050         
28051         file.xhr = this.xhr;
28052
28053         this.xhr.open(this.method, this.url, true);
28054         
28055         var headers = {
28056             "Accept": "application/json",
28057             "Cache-Control": "no-cache",
28058             "X-Requested-With": "XMLHttpRequest"
28059         };
28060         
28061         for (var headerName in headers) {
28062             var headerValue = headers[headerName];
28063             if (headerValue) {
28064                 this.xhr.setRequestHeader(headerName, headerValue);
28065             }
28066         }
28067         
28068         var _this = this;
28069         
28070         this.xhr.onload = function()
28071         {
28072             _this.xhrOnLoad(_this.xhr);
28073         }
28074         
28075         this.xhr.onerror = function()
28076         {
28077             _this.xhrOnError(_this.xhr);
28078         }
28079         
28080         var formData = new FormData();
28081
28082         formData.append('returnHTML', 'NO');
28083         
28084         if(crop){
28085             formData.append('crop', crop);
28086         }
28087         
28088         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28089             formData.append(this.paramName, file, file.name);
28090         }
28091         
28092         if(typeof(file.filename) != 'undefined'){
28093             formData.append('filename', file.filename);
28094         }
28095         
28096         if(typeof(file.mimetype) != 'undefined'){
28097             formData.append('mimetype', file.mimetype);
28098         }
28099         
28100         if(this.fireEvent('arrange', this, formData) != false){
28101             this.xhr.send(formData);
28102         };
28103     },
28104     
28105     xhrOnLoad : function(xhr)
28106     {
28107         if(this.loadMask){
28108             this.maskEl.unmask();
28109         }
28110         
28111         if (xhr.readyState !== 4) {
28112             this.fireEvent('exception', this, xhr);
28113             return;
28114         }
28115
28116         var response = Roo.decode(xhr.responseText);
28117         
28118         if(!response.success){
28119             this.fireEvent('exception', this, xhr);
28120             return;
28121         }
28122         
28123         var response = Roo.decode(xhr.responseText);
28124         
28125         this.fireEvent('upload', this, response);
28126         
28127     },
28128     
28129     xhrOnError : function()
28130     {
28131         if(this.loadMask){
28132             this.maskEl.unmask();
28133         }
28134         
28135         Roo.log('xhr on error');
28136         
28137         var response = Roo.decode(xhr.responseText);
28138           
28139         Roo.log(response);
28140         
28141     },
28142     
28143     prepare : function(file)
28144     {   
28145         if(this.loadMask){
28146             this.maskEl.mask(this.loadingText);
28147         }
28148         
28149         this.file = false;
28150         this.exif = {};
28151         
28152         if(typeof(file) === 'string'){
28153             this.loadCanvas(file);
28154             return;
28155         }
28156         
28157         if(!file || !this.urlAPI){
28158             return;
28159         }
28160         
28161         this.file = file;
28162         this.cropType = file.type;
28163         
28164         var _this = this;
28165         
28166         if(this.fireEvent('prepare', this, this.file) != false){
28167             
28168             var reader = new FileReader();
28169             
28170             reader.onload = function (e) {
28171                 if (e.target.error) {
28172                     Roo.log(e.target.error);
28173                     return;
28174                 }
28175                 
28176                 var buffer = e.target.result,
28177                     dataView = new DataView(buffer),
28178                     offset = 2,
28179                     maxOffset = dataView.byteLength - 4,
28180                     markerBytes,
28181                     markerLength;
28182                 
28183                 if (dataView.getUint16(0) === 0xffd8) {
28184                     while (offset < maxOffset) {
28185                         markerBytes = dataView.getUint16(offset);
28186                         
28187                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28188                             markerLength = dataView.getUint16(offset + 2) + 2;
28189                             if (offset + markerLength > dataView.byteLength) {
28190                                 Roo.log('Invalid meta data: Invalid segment size.');
28191                                 break;
28192                             }
28193                             
28194                             if(markerBytes == 0xffe1){
28195                                 _this.parseExifData(
28196                                     dataView,
28197                                     offset,
28198                                     markerLength
28199                                 );
28200                             }
28201                             
28202                             offset += markerLength;
28203                             
28204                             continue;
28205                         }
28206                         
28207                         break;
28208                     }
28209                     
28210                 }
28211                 
28212                 var url = _this.urlAPI.createObjectURL(_this.file);
28213                 
28214                 _this.loadCanvas(url);
28215                 
28216                 return;
28217             }
28218             
28219             reader.readAsArrayBuffer(this.file);
28220             
28221         }
28222         
28223     },
28224     
28225     parseExifData : function(dataView, offset, length)
28226     {
28227         var tiffOffset = offset + 10,
28228             littleEndian,
28229             dirOffset;
28230     
28231         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28232             // No Exif data, might be XMP data instead
28233             return;
28234         }
28235         
28236         // Check for the ASCII code for "Exif" (0x45786966):
28237         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28238             // No Exif data, might be XMP data instead
28239             return;
28240         }
28241         if (tiffOffset + 8 > dataView.byteLength) {
28242             Roo.log('Invalid Exif data: Invalid segment size.');
28243             return;
28244         }
28245         // Check for the two null bytes:
28246         if (dataView.getUint16(offset + 8) !== 0x0000) {
28247             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28248             return;
28249         }
28250         // Check the byte alignment:
28251         switch (dataView.getUint16(tiffOffset)) {
28252         case 0x4949:
28253             littleEndian = true;
28254             break;
28255         case 0x4D4D:
28256             littleEndian = false;
28257             break;
28258         default:
28259             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28260             return;
28261         }
28262         // Check for the TIFF tag marker (0x002A):
28263         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28264             Roo.log('Invalid Exif data: Missing TIFF marker.');
28265             return;
28266         }
28267         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28268         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28269         
28270         this.parseExifTags(
28271             dataView,
28272             tiffOffset,
28273             tiffOffset + dirOffset,
28274             littleEndian
28275         );
28276     },
28277     
28278     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28279     {
28280         var tagsNumber,
28281             dirEndOffset,
28282             i;
28283         if (dirOffset + 6 > dataView.byteLength) {
28284             Roo.log('Invalid Exif data: Invalid directory offset.');
28285             return;
28286         }
28287         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28288         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28289         if (dirEndOffset + 4 > dataView.byteLength) {
28290             Roo.log('Invalid Exif data: Invalid directory size.');
28291             return;
28292         }
28293         for (i = 0; i < tagsNumber; i += 1) {
28294             this.parseExifTag(
28295                 dataView,
28296                 tiffOffset,
28297                 dirOffset + 2 + 12 * i, // tag offset
28298                 littleEndian
28299             );
28300         }
28301         // Return the offset to the next directory:
28302         return dataView.getUint32(dirEndOffset, littleEndian);
28303     },
28304     
28305     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28306     {
28307         var tag = dataView.getUint16(offset, littleEndian);
28308         
28309         this.exif[tag] = this.getExifValue(
28310             dataView,
28311             tiffOffset,
28312             offset,
28313             dataView.getUint16(offset + 2, littleEndian), // tag type
28314             dataView.getUint32(offset + 4, littleEndian), // tag length
28315             littleEndian
28316         );
28317     },
28318     
28319     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28320     {
28321         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28322             tagSize,
28323             dataOffset,
28324             values,
28325             i,
28326             str,
28327             c;
28328     
28329         if (!tagType) {
28330             Roo.log('Invalid Exif data: Invalid tag type.');
28331             return;
28332         }
28333         
28334         tagSize = tagType.size * length;
28335         // Determine if the value is contained in the dataOffset bytes,
28336         // or if the value at the dataOffset is a pointer to the actual data:
28337         dataOffset = tagSize > 4 ?
28338                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28339         if (dataOffset + tagSize > dataView.byteLength) {
28340             Roo.log('Invalid Exif data: Invalid data offset.');
28341             return;
28342         }
28343         if (length === 1) {
28344             return tagType.getValue(dataView, dataOffset, littleEndian);
28345         }
28346         values = [];
28347         for (i = 0; i < length; i += 1) {
28348             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28349         }
28350         
28351         if (tagType.ascii) {
28352             str = '';
28353             // Concatenate the chars:
28354             for (i = 0; i < values.length; i += 1) {
28355                 c = values[i];
28356                 // Ignore the terminating NULL byte(s):
28357                 if (c === '\u0000') {
28358                     break;
28359                 }
28360                 str += c;
28361             }
28362             return str;
28363         }
28364         return values;
28365     }
28366     
28367 });
28368
28369 Roo.apply(Roo.bootstrap.UploadCropbox, {
28370     tags : {
28371         'Orientation': 0x0112
28372     },
28373     
28374     Orientation: {
28375             1: 0, //'top-left',
28376 //            2: 'top-right',
28377             3: 180, //'bottom-right',
28378 //            4: 'bottom-left',
28379 //            5: 'left-top',
28380             6: 90, //'right-top',
28381 //            7: 'right-bottom',
28382             8: 270 //'left-bottom'
28383     },
28384     
28385     exifTagTypes : {
28386         // byte, 8-bit unsigned int:
28387         1: {
28388             getValue: function (dataView, dataOffset) {
28389                 return dataView.getUint8(dataOffset);
28390             },
28391             size: 1
28392         },
28393         // ascii, 8-bit byte:
28394         2: {
28395             getValue: function (dataView, dataOffset) {
28396                 return String.fromCharCode(dataView.getUint8(dataOffset));
28397             },
28398             size: 1,
28399             ascii: true
28400         },
28401         // short, 16 bit int:
28402         3: {
28403             getValue: function (dataView, dataOffset, littleEndian) {
28404                 return dataView.getUint16(dataOffset, littleEndian);
28405             },
28406             size: 2
28407         },
28408         // long, 32 bit int:
28409         4: {
28410             getValue: function (dataView, dataOffset, littleEndian) {
28411                 return dataView.getUint32(dataOffset, littleEndian);
28412             },
28413             size: 4
28414         },
28415         // rational = two long values, first is numerator, second is denominator:
28416         5: {
28417             getValue: function (dataView, dataOffset, littleEndian) {
28418                 return dataView.getUint32(dataOffset, littleEndian) /
28419                     dataView.getUint32(dataOffset + 4, littleEndian);
28420             },
28421             size: 8
28422         },
28423         // slong, 32 bit signed int:
28424         9: {
28425             getValue: function (dataView, dataOffset, littleEndian) {
28426                 return dataView.getInt32(dataOffset, littleEndian);
28427             },
28428             size: 4
28429         },
28430         // srational, two slongs, first is numerator, second is denominator:
28431         10: {
28432             getValue: function (dataView, dataOffset, littleEndian) {
28433                 return dataView.getInt32(dataOffset, littleEndian) /
28434                     dataView.getInt32(dataOffset + 4, littleEndian);
28435             },
28436             size: 8
28437         }
28438     },
28439     
28440     footer : {
28441         STANDARD : [
28442             {
28443                 tag : 'div',
28444                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28445                 action : 'rotate-left',
28446                 cn : [
28447                     {
28448                         tag : 'button',
28449                         cls : 'btn btn-default',
28450                         html : '<i class="fa fa-undo"></i>'
28451                     }
28452                 ]
28453             },
28454             {
28455                 tag : 'div',
28456                 cls : 'btn-group roo-upload-cropbox-picture',
28457                 action : 'picture',
28458                 cn : [
28459                     {
28460                         tag : 'button',
28461                         cls : 'btn btn-default',
28462                         html : '<i class="fa fa-picture-o"></i>'
28463                     }
28464                 ]
28465             },
28466             {
28467                 tag : 'div',
28468                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28469                 action : 'rotate-right',
28470                 cn : [
28471                     {
28472                         tag : 'button',
28473                         cls : 'btn btn-default',
28474                         html : '<i class="fa fa-repeat"></i>'
28475                     }
28476                 ]
28477             }
28478         ],
28479         DOCUMENT : [
28480             {
28481                 tag : 'div',
28482                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28483                 action : 'rotate-left',
28484                 cn : [
28485                     {
28486                         tag : 'button',
28487                         cls : 'btn btn-default',
28488                         html : '<i class="fa fa-undo"></i>'
28489                     }
28490                 ]
28491             },
28492             {
28493                 tag : 'div',
28494                 cls : 'btn-group roo-upload-cropbox-download',
28495                 action : 'download',
28496                 cn : [
28497                     {
28498                         tag : 'button',
28499                         cls : 'btn btn-default',
28500                         html : '<i class="fa fa-download"></i>'
28501                     }
28502                 ]
28503             },
28504             {
28505                 tag : 'div',
28506                 cls : 'btn-group roo-upload-cropbox-crop',
28507                 action : 'crop',
28508                 cn : [
28509                     {
28510                         tag : 'button',
28511                         cls : 'btn btn-default',
28512                         html : '<i class="fa fa-crop"></i>'
28513                     }
28514                 ]
28515             },
28516             {
28517                 tag : 'div',
28518                 cls : 'btn-group roo-upload-cropbox-trash',
28519                 action : 'trash',
28520                 cn : [
28521                     {
28522                         tag : 'button',
28523                         cls : 'btn btn-default',
28524                         html : '<i class="fa fa-trash"></i>'
28525                     }
28526                 ]
28527             },
28528             {
28529                 tag : 'div',
28530                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28531                 action : 'rotate-right',
28532                 cn : [
28533                     {
28534                         tag : 'button',
28535                         cls : 'btn btn-default',
28536                         html : '<i class="fa fa-repeat"></i>'
28537                     }
28538                 ]
28539             }
28540         ],
28541         ROTATOR : [
28542             {
28543                 tag : 'div',
28544                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28545                 action : 'rotate-left',
28546                 cn : [
28547                     {
28548                         tag : 'button',
28549                         cls : 'btn btn-default',
28550                         html : '<i class="fa fa-undo"></i>'
28551                     }
28552                 ]
28553             },
28554             {
28555                 tag : 'div',
28556                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28557                 action : 'rotate-right',
28558                 cn : [
28559                     {
28560                         tag : 'button',
28561                         cls : 'btn btn-default',
28562                         html : '<i class="fa fa-repeat"></i>'
28563                     }
28564                 ]
28565             }
28566         ]
28567     }
28568 });
28569
28570 /*
28571 * Licence: LGPL
28572 */
28573
28574 /**
28575  * @class Roo.bootstrap.DocumentManager
28576  * @extends Roo.bootstrap.Component
28577  * Bootstrap DocumentManager class
28578  * @cfg {String} paramName default 'imageUpload'
28579  * @cfg {String} toolTipName default 'filename'
28580  * @cfg {String} method default POST
28581  * @cfg {String} url action url
28582  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28583  * @cfg {Boolean} multiple multiple upload default true
28584  * @cfg {Number} thumbSize default 300
28585  * @cfg {String} fieldLabel
28586  * @cfg {Number} labelWidth default 4
28587  * @cfg {String} labelAlign (left|top) default left
28588  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28589 * @cfg {Number} labellg set the width of label (1-12)
28590  * @cfg {Number} labelmd set the width of label (1-12)
28591  * @cfg {Number} labelsm set the width of label (1-12)
28592  * @cfg {Number} labelxs set the width of label (1-12)
28593  * 
28594  * @constructor
28595  * Create a new DocumentManager
28596  * @param {Object} config The config object
28597  */
28598
28599 Roo.bootstrap.DocumentManager = function(config){
28600     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28601     
28602     this.files = [];
28603     this.delegates = [];
28604     
28605     this.addEvents({
28606         /**
28607          * @event initial
28608          * Fire when initial the DocumentManager
28609          * @param {Roo.bootstrap.DocumentManager} this
28610          */
28611         "initial" : true,
28612         /**
28613          * @event inspect
28614          * inspect selected file
28615          * @param {Roo.bootstrap.DocumentManager} this
28616          * @param {File} file
28617          */
28618         "inspect" : true,
28619         /**
28620          * @event exception
28621          * Fire when xhr load exception
28622          * @param {Roo.bootstrap.DocumentManager} this
28623          * @param {XMLHttpRequest} xhr
28624          */
28625         "exception" : true,
28626         /**
28627          * @event afterupload
28628          * Fire when xhr load exception
28629          * @param {Roo.bootstrap.DocumentManager} this
28630          * @param {XMLHttpRequest} xhr
28631          */
28632         "afterupload" : true,
28633         /**
28634          * @event prepare
28635          * prepare the form data
28636          * @param {Roo.bootstrap.DocumentManager} this
28637          * @param {Object} formData
28638          */
28639         "prepare" : true,
28640         /**
28641          * @event remove
28642          * Fire when remove the file
28643          * @param {Roo.bootstrap.DocumentManager} this
28644          * @param {Object} file
28645          */
28646         "remove" : true,
28647         /**
28648          * @event refresh
28649          * Fire after refresh the file
28650          * @param {Roo.bootstrap.DocumentManager} this
28651          */
28652         "refresh" : true,
28653         /**
28654          * @event click
28655          * Fire after click the image
28656          * @param {Roo.bootstrap.DocumentManager} this
28657          * @param {Object} file
28658          */
28659         "click" : true,
28660         /**
28661          * @event edit
28662          * Fire when upload a image and editable set to true
28663          * @param {Roo.bootstrap.DocumentManager} this
28664          * @param {Object} file
28665          */
28666         "edit" : true,
28667         /**
28668          * @event beforeselectfile
28669          * Fire before select file
28670          * @param {Roo.bootstrap.DocumentManager} this
28671          */
28672         "beforeselectfile" : true,
28673         /**
28674          * @event process
28675          * Fire before process file
28676          * @param {Roo.bootstrap.DocumentManager} this
28677          * @param {Object} file
28678          */
28679         "process" : true,
28680         /**
28681          * @event previewrendered
28682          * Fire when preview rendered
28683          * @param {Roo.bootstrap.DocumentManager} this
28684          * @param {Object} file
28685          */
28686         "previewrendered" : true
28687         
28688     });
28689 };
28690
28691 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28692     
28693     boxes : 0,
28694     inputName : '',
28695     thumbSize : 300,
28696     multiple : true,
28697     files : false,
28698     method : 'POST',
28699     url : '',
28700     paramName : 'imageUpload',
28701     toolTipName : 'filename',
28702     fieldLabel : '',
28703     labelWidth : 4,
28704     labelAlign : 'left',
28705     editable : true,
28706     delegates : false,
28707     xhr : false, 
28708     
28709     labellg : 0,
28710     labelmd : 0,
28711     labelsm : 0,
28712     labelxs : 0,
28713     
28714     getAutoCreate : function()
28715     {   
28716         var managerWidget = {
28717             tag : 'div',
28718             cls : 'roo-document-manager',
28719             cn : [
28720                 {
28721                     tag : 'input',
28722                     cls : 'roo-document-manager-selector',
28723                     type : 'file'
28724                 },
28725                 {
28726                     tag : 'div',
28727                     cls : 'roo-document-manager-uploader',
28728                     cn : [
28729                         {
28730                             tag : 'div',
28731                             cls : 'roo-document-manager-upload-btn',
28732                             html : '<i class="fa fa-plus"></i>'
28733                         }
28734                     ]
28735                     
28736                 }
28737             ]
28738         };
28739         
28740         var content = [
28741             {
28742                 tag : 'div',
28743                 cls : 'column col-md-12',
28744                 cn : managerWidget
28745             }
28746         ];
28747         
28748         if(this.fieldLabel.length){
28749             
28750             content = [
28751                 {
28752                     tag : 'div',
28753                     cls : 'column col-md-12',
28754                     html : this.fieldLabel
28755                 },
28756                 {
28757                     tag : 'div',
28758                     cls : 'column col-md-12',
28759                     cn : managerWidget
28760                 }
28761             ];
28762
28763             if(this.labelAlign == 'left'){
28764                 content = [
28765                     {
28766                         tag : 'div',
28767                         cls : 'column',
28768                         html : this.fieldLabel
28769                     },
28770                     {
28771                         tag : 'div',
28772                         cls : 'column',
28773                         cn : managerWidget
28774                     }
28775                 ];
28776                 
28777                 if(this.labelWidth > 12){
28778                     content[0].style = "width: " + this.labelWidth + 'px';
28779                 }
28780
28781                 if(this.labelWidth < 13 && this.labelmd == 0){
28782                     this.labelmd = this.labelWidth;
28783                 }
28784
28785                 if(this.labellg > 0){
28786                     content[0].cls += ' col-lg-' + this.labellg;
28787                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28788                 }
28789
28790                 if(this.labelmd > 0){
28791                     content[0].cls += ' col-md-' + this.labelmd;
28792                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28793                 }
28794
28795                 if(this.labelsm > 0){
28796                     content[0].cls += ' col-sm-' + this.labelsm;
28797                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28798                 }
28799
28800                 if(this.labelxs > 0){
28801                     content[0].cls += ' col-xs-' + this.labelxs;
28802                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28803                 }
28804                 
28805             }
28806         }
28807         
28808         var cfg = {
28809             tag : 'div',
28810             cls : 'row clearfix',
28811             cn : content
28812         };
28813         
28814         return cfg;
28815         
28816     },
28817     
28818     initEvents : function()
28819     {
28820         this.managerEl = this.el.select('.roo-document-manager', true).first();
28821         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28822         
28823         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28824         this.selectorEl.hide();
28825         
28826         if(this.multiple){
28827             this.selectorEl.attr('multiple', 'multiple');
28828         }
28829         
28830         this.selectorEl.on('change', this.onFileSelected, this);
28831         
28832         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28833         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28834         
28835         this.uploader.on('click', this.onUploaderClick, this);
28836         
28837         this.renderProgressDialog();
28838         
28839         var _this = this;
28840         
28841         window.addEventListener("resize", function() { _this.refresh(); } );
28842         
28843         this.fireEvent('initial', this);
28844     },
28845     
28846     renderProgressDialog : function()
28847     {
28848         var _this = this;
28849         
28850         this.progressDialog = new Roo.bootstrap.Modal({
28851             cls : 'roo-document-manager-progress-dialog',
28852             allow_close : false,
28853             title : '',
28854             buttons : [
28855                 {
28856                     name  :'cancel',
28857                     weight : 'danger',
28858                     html : 'Cancel'
28859                 }
28860             ], 
28861             listeners : { 
28862                 btnclick : function() {
28863                     _this.uploadCancel();
28864                     this.hide();
28865                 }
28866             }
28867         });
28868          
28869         this.progressDialog.render(Roo.get(document.body));
28870          
28871         this.progress = new Roo.bootstrap.Progress({
28872             cls : 'roo-document-manager-progress',
28873             active : true,
28874             striped : true
28875         });
28876         
28877         this.progress.render(this.progressDialog.getChildContainer());
28878         
28879         this.progressBar = new Roo.bootstrap.ProgressBar({
28880             cls : 'roo-document-manager-progress-bar',
28881             aria_valuenow : 0,
28882             aria_valuemin : 0,
28883             aria_valuemax : 12,
28884             panel : 'success'
28885         });
28886         
28887         this.progressBar.render(this.progress.getChildContainer());
28888     },
28889     
28890     onUploaderClick : function(e)
28891     {
28892         e.preventDefault();
28893      
28894         if(this.fireEvent('beforeselectfile', this) != false){
28895             this.selectorEl.dom.click();
28896         }
28897         
28898     },
28899     
28900     onFileSelected : function(e)
28901     {
28902         e.preventDefault();
28903         
28904         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28905             return;
28906         }
28907         
28908         Roo.each(this.selectorEl.dom.files, function(file){
28909             if(this.fireEvent('inspect', this, file) != false){
28910                 this.files.push(file);
28911             }
28912         }, this);
28913         
28914         this.queue();
28915         
28916     },
28917     
28918     queue : function()
28919     {
28920         this.selectorEl.dom.value = '';
28921         
28922         if(!this.files || !this.files.length){
28923             return;
28924         }
28925         
28926         if(this.boxes > 0 && this.files.length > this.boxes){
28927             this.files = this.files.slice(0, this.boxes);
28928         }
28929         
28930         this.uploader.show();
28931         
28932         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28933             this.uploader.hide();
28934         }
28935         
28936         var _this = this;
28937         
28938         var files = [];
28939         
28940         var docs = [];
28941         
28942         Roo.each(this.files, function(file){
28943             
28944             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28945                 var f = this.renderPreview(file);
28946                 files.push(f);
28947                 return;
28948             }
28949             
28950             if(file.type.indexOf('image') != -1){
28951                 this.delegates.push(
28952                     (function(){
28953                         _this.process(file);
28954                     }).createDelegate(this)
28955                 );
28956         
28957                 return;
28958             }
28959             
28960             docs.push(
28961                 (function(){
28962                     _this.process(file);
28963                 }).createDelegate(this)
28964             );
28965             
28966         }, this);
28967         
28968         this.files = files;
28969         
28970         this.delegates = this.delegates.concat(docs);
28971         
28972         if(!this.delegates.length){
28973             this.refresh();
28974             return;
28975         }
28976         
28977         this.progressBar.aria_valuemax = this.delegates.length;
28978         
28979         this.arrange();
28980         
28981         return;
28982     },
28983     
28984     arrange : function()
28985     {
28986         if(!this.delegates.length){
28987             this.progressDialog.hide();
28988             this.refresh();
28989             return;
28990         }
28991         
28992         var delegate = this.delegates.shift();
28993         
28994         this.progressDialog.show();
28995         
28996         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28997         
28998         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28999         
29000         delegate();
29001     },
29002     
29003     refresh : function()
29004     {
29005         this.uploader.show();
29006         
29007         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29008             this.uploader.hide();
29009         }
29010         
29011         Roo.isTouch ? this.closable(false) : this.closable(true);
29012         
29013         this.fireEvent('refresh', this);
29014     },
29015     
29016     onRemove : function(e, el, o)
29017     {
29018         e.preventDefault();
29019         
29020         this.fireEvent('remove', this, o);
29021         
29022     },
29023     
29024     remove : function(o)
29025     {
29026         var files = [];
29027         
29028         Roo.each(this.files, function(file){
29029             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29030                 files.push(file);
29031                 return;
29032             }
29033
29034             o.target.remove();
29035
29036         }, this);
29037         
29038         this.files = files;
29039         
29040         this.refresh();
29041     },
29042     
29043     clear : function()
29044     {
29045         Roo.each(this.files, function(file){
29046             if(!file.target){
29047                 return;
29048             }
29049             
29050             file.target.remove();
29051
29052         }, this);
29053         
29054         this.files = [];
29055         
29056         this.refresh();
29057     },
29058     
29059     onClick : function(e, el, o)
29060     {
29061         e.preventDefault();
29062         
29063         this.fireEvent('click', this, o);
29064         
29065     },
29066     
29067     closable : function(closable)
29068     {
29069         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29070             
29071             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29072             
29073             if(closable){
29074                 el.show();
29075                 return;
29076             }
29077             
29078             el.hide();
29079             
29080         }, this);
29081     },
29082     
29083     xhrOnLoad : function(xhr)
29084     {
29085         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29086             el.remove();
29087         }, this);
29088         
29089         if (xhr.readyState !== 4) {
29090             this.arrange();
29091             this.fireEvent('exception', this, xhr);
29092             return;
29093         }
29094
29095         var response = Roo.decode(xhr.responseText);
29096         
29097         if(!response.success){
29098             this.arrange();
29099             this.fireEvent('exception', this, xhr);
29100             return;
29101         }
29102         
29103         var file = this.renderPreview(response.data);
29104         
29105         this.files.push(file);
29106         
29107         this.arrange();
29108         
29109         this.fireEvent('afterupload', this, xhr);
29110         
29111     },
29112     
29113     xhrOnError : function(xhr)
29114     {
29115         Roo.log('xhr on error');
29116         
29117         var response = Roo.decode(xhr.responseText);
29118           
29119         Roo.log(response);
29120         
29121         this.arrange();
29122     },
29123     
29124     process : function(file)
29125     {
29126         if(this.fireEvent('process', this, file) !== false){
29127             if(this.editable && file.type.indexOf('image') != -1){
29128                 this.fireEvent('edit', this, file);
29129                 return;
29130             }
29131
29132             this.uploadStart(file, false);
29133
29134             return;
29135         }
29136         
29137     },
29138     
29139     uploadStart : function(file, crop)
29140     {
29141         this.xhr = new XMLHttpRequest();
29142         
29143         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29144             this.arrange();
29145             return;
29146         }
29147         
29148         file.xhr = this.xhr;
29149             
29150         this.managerEl.createChild({
29151             tag : 'div',
29152             cls : 'roo-document-manager-loading',
29153             cn : [
29154                 {
29155                     tag : 'div',
29156                     tooltip : file.name,
29157                     cls : 'roo-document-manager-thumb',
29158                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29159                 }
29160             ]
29161
29162         });
29163
29164         this.xhr.open(this.method, this.url, true);
29165         
29166         var headers = {
29167             "Accept": "application/json",
29168             "Cache-Control": "no-cache",
29169             "X-Requested-With": "XMLHttpRequest"
29170         };
29171         
29172         for (var headerName in headers) {
29173             var headerValue = headers[headerName];
29174             if (headerValue) {
29175                 this.xhr.setRequestHeader(headerName, headerValue);
29176             }
29177         }
29178         
29179         var _this = this;
29180         
29181         this.xhr.onload = function()
29182         {
29183             _this.xhrOnLoad(_this.xhr);
29184         }
29185         
29186         this.xhr.onerror = function()
29187         {
29188             _this.xhrOnError(_this.xhr);
29189         }
29190         
29191         var formData = new FormData();
29192
29193         formData.append('returnHTML', 'NO');
29194         
29195         if(crop){
29196             formData.append('crop', crop);
29197         }
29198         
29199         formData.append(this.paramName, file, file.name);
29200         
29201         var options = {
29202             file : file, 
29203             manually : false
29204         };
29205         
29206         if(this.fireEvent('prepare', this, formData, options) != false){
29207             
29208             if(options.manually){
29209                 return;
29210             }
29211             
29212             this.xhr.send(formData);
29213             return;
29214         };
29215         
29216         this.uploadCancel();
29217     },
29218     
29219     uploadCancel : function()
29220     {
29221         if (this.xhr) {
29222             this.xhr.abort();
29223         }
29224         
29225         this.delegates = [];
29226         
29227         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29228             el.remove();
29229         }, this);
29230         
29231         this.arrange();
29232     },
29233     
29234     renderPreview : function(file)
29235     {
29236         if(typeof(file.target) != 'undefined' && file.target){
29237             return file;
29238         }
29239         
29240         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29241         
29242         var previewEl = this.managerEl.createChild({
29243             tag : 'div',
29244             cls : 'roo-document-manager-preview',
29245             cn : [
29246                 {
29247                     tag : 'div',
29248                     tooltip : file[this.toolTipName],
29249                     cls : 'roo-document-manager-thumb',
29250                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29251                 },
29252                 {
29253                     tag : 'button',
29254                     cls : 'close',
29255                     html : '<i class="fa fa-times-circle"></i>'
29256                 }
29257             ]
29258         });
29259
29260         var close = previewEl.select('button.close', true).first();
29261
29262         close.on('click', this.onRemove, this, file);
29263
29264         file.target = previewEl;
29265
29266         var image = previewEl.select('img', true).first();
29267         
29268         var _this = this;
29269         
29270         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29271         
29272         image.on('click', this.onClick, this, file);
29273         
29274         this.fireEvent('previewrendered', this, file);
29275         
29276         return file;
29277         
29278     },
29279     
29280     onPreviewLoad : function(file, image)
29281     {
29282         if(typeof(file.target) == 'undefined' || !file.target){
29283             return;
29284         }
29285         
29286         var width = image.dom.naturalWidth || image.dom.width;
29287         var height = image.dom.naturalHeight || image.dom.height;
29288         
29289         if(width > height){
29290             file.target.addClass('wide');
29291             return;
29292         }
29293         
29294         file.target.addClass('tall');
29295         return;
29296         
29297     },
29298     
29299     uploadFromSource : function(file, crop)
29300     {
29301         this.xhr = new XMLHttpRequest();
29302         
29303         this.managerEl.createChild({
29304             tag : 'div',
29305             cls : 'roo-document-manager-loading',
29306             cn : [
29307                 {
29308                     tag : 'div',
29309                     tooltip : file.name,
29310                     cls : 'roo-document-manager-thumb',
29311                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29312                 }
29313             ]
29314
29315         });
29316
29317         this.xhr.open(this.method, this.url, true);
29318         
29319         var headers = {
29320             "Accept": "application/json",
29321             "Cache-Control": "no-cache",
29322             "X-Requested-With": "XMLHttpRequest"
29323         };
29324         
29325         for (var headerName in headers) {
29326             var headerValue = headers[headerName];
29327             if (headerValue) {
29328                 this.xhr.setRequestHeader(headerName, headerValue);
29329             }
29330         }
29331         
29332         var _this = this;
29333         
29334         this.xhr.onload = function()
29335         {
29336             _this.xhrOnLoad(_this.xhr);
29337         }
29338         
29339         this.xhr.onerror = function()
29340         {
29341             _this.xhrOnError(_this.xhr);
29342         }
29343         
29344         var formData = new FormData();
29345
29346         formData.append('returnHTML', 'NO');
29347         
29348         formData.append('crop', crop);
29349         
29350         if(typeof(file.filename) != 'undefined'){
29351             formData.append('filename', file.filename);
29352         }
29353         
29354         if(typeof(file.mimetype) != 'undefined'){
29355             formData.append('mimetype', file.mimetype);
29356         }
29357         
29358         Roo.log(formData);
29359         
29360         if(this.fireEvent('prepare', this, formData) != false){
29361             this.xhr.send(formData);
29362         };
29363     }
29364 });
29365
29366 /*
29367 * Licence: LGPL
29368 */
29369
29370 /**
29371  * @class Roo.bootstrap.DocumentViewer
29372  * @extends Roo.bootstrap.Component
29373  * Bootstrap DocumentViewer class
29374  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29375  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29376  * 
29377  * @constructor
29378  * Create a new DocumentViewer
29379  * @param {Object} config The config object
29380  */
29381
29382 Roo.bootstrap.DocumentViewer = function(config){
29383     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29384     
29385     this.addEvents({
29386         /**
29387          * @event initial
29388          * Fire after initEvent
29389          * @param {Roo.bootstrap.DocumentViewer} this
29390          */
29391         "initial" : true,
29392         /**
29393          * @event click
29394          * Fire after click
29395          * @param {Roo.bootstrap.DocumentViewer} this
29396          */
29397         "click" : true,
29398         /**
29399          * @event download
29400          * Fire after download button
29401          * @param {Roo.bootstrap.DocumentViewer} this
29402          */
29403         "download" : true,
29404         /**
29405          * @event trash
29406          * Fire after trash button
29407          * @param {Roo.bootstrap.DocumentViewer} this
29408          */
29409         "trash" : true
29410         
29411     });
29412 };
29413
29414 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29415     
29416     showDownload : true,
29417     
29418     showTrash : true,
29419     
29420     getAutoCreate : function()
29421     {
29422         var cfg = {
29423             tag : 'div',
29424             cls : 'roo-document-viewer',
29425             cn : [
29426                 {
29427                     tag : 'div',
29428                     cls : 'roo-document-viewer-body',
29429                     cn : [
29430                         {
29431                             tag : 'div',
29432                             cls : 'roo-document-viewer-thumb',
29433                             cn : [
29434                                 {
29435                                     tag : 'img',
29436                                     cls : 'roo-document-viewer-image'
29437                                 }
29438                             ]
29439                         }
29440                     ]
29441                 },
29442                 {
29443                     tag : 'div',
29444                     cls : 'roo-document-viewer-footer',
29445                     cn : {
29446                         tag : 'div',
29447                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29448                         cn : [
29449                             {
29450                                 tag : 'div',
29451                                 cls : 'btn-group roo-document-viewer-download',
29452                                 cn : [
29453                                     {
29454                                         tag : 'button',
29455                                         cls : 'btn btn-default',
29456                                         html : '<i class="fa fa-download"></i>'
29457                                     }
29458                                 ]
29459                             },
29460                             {
29461                                 tag : 'div',
29462                                 cls : 'btn-group roo-document-viewer-trash',
29463                                 cn : [
29464                                     {
29465                                         tag : 'button',
29466                                         cls : 'btn btn-default',
29467                                         html : '<i class="fa fa-trash"></i>'
29468                                     }
29469                                 ]
29470                             }
29471                         ]
29472                     }
29473                 }
29474             ]
29475         };
29476         
29477         return cfg;
29478     },
29479     
29480     initEvents : function()
29481     {
29482         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29483         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29484         
29485         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29486         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29487         
29488         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29489         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29490         
29491         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29492         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29493         
29494         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29495         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29496         
29497         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29498         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29499         
29500         this.bodyEl.on('click', this.onClick, this);
29501         this.downloadBtn.on('click', this.onDownload, this);
29502         this.trashBtn.on('click', this.onTrash, this);
29503         
29504         this.downloadBtn.hide();
29505         this.trashBtn.hide();
29506         
29507         if(this.showDownload){
29508             this.downloadBtn.show();
29509         }
29510         
29511         if(this.showTrash){
29512             this.trashBtn.show();
29513         }
29514         
29515         if(!this.showDownload && !this.showTrash) {
29516             this.footerEl.hide();
29517         }
29518         
29519     },
29520     
29521     initial : function()
29522     {
29523         this.fireEvent('initial', this);
29524         
29525     },
29526     
29527     onClick : function(e)
29528     {
29529         e.preventDefault();
29530         
29531         this.fireEvent('click', this);
29532     },
29533     
29534     onDownload : function(e)
29535     {
29536         e.preventDefault();
29537         
29538         this.fireEvent('download', this);
29539     },
29540     
29541     onTrash : function(e)
29542     {
29543         e.preventDefault();
29544         
29545         this.fireEvent('trash', this);
29546     }
29547     
29548 });
29549 /*
29550  * - LGPL
29551  *
29552  * nav progress bar
29553  * 
29554  */
29555
29556 /**
29557  * @class Roo.bootstrap.NavProgressBar
29558  * @extends Roo.bootstrap.Component
29559  * Bootstrap NavProgressBar class
29560  * 
29561  * @constructor
29562  * Create a new nav progress bar
29563  * @param {Object} config The config object
29564  */
29565
29566 Roo.bootstrap.NavProgressBar = function(config){
29567     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29568
29569     this.bullets = this.bullets || [];
29570    
29571 //    Roo.bootstrap.NavProgressBar.register(this);
29572      this.addEvents({
29573         /**
29574              * @event changed
29575              * Fires when the active item changes
29576              * @param {Roo.bootstrap.NavProgressBar} this
29577              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29578              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29579          */
29580         'changed': true
29581      });
29582     
29583 };
29584
29585 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29586     
29587     bullets : [],
29588     barItems : [],
29589     
29590     getAutoCreate : function()
29591     {
29592         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29593         
29594         cfg = {
29595             tag : 'div',
29596             cls : 'roo-navigation-bar-group',
29597             cn : [
29598                 {
29599                     tag : 'div',
29600                     cls : 'roo-navigation-top-bar'
29601                 },
29602                 {
29603                     tag : 'div',
29604                     cls : 'roo-navigation-bullets-bar',
29605                     cn : [
29606                         {
29607                             tag : 'ul',
29608                             cls : 'roo-navigation-bar'
29609                         }
29610                     ]
29611                 },
29612                 
29613                 {
29614                     tag : 'div',
29615                     cls : 'roo-navigation-bottom-bar'
29616                 }
29617             ]
29618             
29619         };
29620         
29621         return cfg;
29622         
29623     },
29624     
29625     initEvents: function() 
29626     {
29627         
29628     },
29629     
29630     onRender : function(ct, position) 
29631     {
29632         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29633         
29634         if(this.bullets.length){
29635             Roo.each(this.bullets, function(b){
29636                this.addItem(b);
29637             }, this);
29638         }
29639         
29640         this.format();
29641         
29642     },
29643     
29644     addItem : function(cfg)
29645     {
29646         var item = new Roo.bootstrap.NavProgressItem(cfg);
29647         
29648         item.parentId = this.id;
29649         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29650         
29651         if(cfg.html){
29652             var top = new Roo.bootstrap.Element({
29653                 tag : 'div',
29654                 cls : 'roo-navigation-bar-text'
29655             });
29656             
29657             var bottom = new Roo.bootstrap.Element({
29658                 tag : 'div',
29659                 cls : 'roo-navigation-bar-text'
29660             });
29661             
29662             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29663             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29664             
29665             var topText = new Roo.bootstrap.Element({
29666                 tag : 'span',
29667                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29668             });
29669             
29670             var bottomText = new Roo.bootstrap.Element({
29671                 tag : 'span',
29672                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29673             });
29674             
29675             topText.onRender(top.el, null);
29676             bottomText.onRender(bottom.el, null);
29677             
29678             item.topEl = top;
29679             item.bottomEl = bottom;
29680         }
29681         
29682         this.barItems.push(item);
29683         
29684         return item;
29685     },
29686     
29687     getActive : function()
29688     {
29689         var active = false;
29690         
29691         Roo.each(this.barItems, function(v){
29692             
29693             if (!v.isActive()) {
29694                 return;
29695             }
29696             
29697             active = v;
29698             return false;
29699             
29700         });
29701         
29702         return active;
29703     },
29704     
29705     setActiveItem : function(item)
29706     {
29707         var prev = false;
29708         
29709         Roo.each(this.barItems, function(v){
29710             if (v.rid == item.rid) {
29711                 return ;
29712             }
29713             
29714             if (v.isActive()) {
29715                 v.setActive(false);
29716                 prev = v;
29717             }
29718         });
29719
29720         item.setActive(true);
29721         
29722         this.fireEvent('changed', this, item, prev);
29723     },
29724     
29725     getBarItem: function(rid)
29726     {
29727         var ret = false;
29728         
29729         Roo.each(this.barItems, function(e) {
29730             if (e.rid != rid) {
29731                 return;
29732             }
29733             
29734             ret =  e;
29735             return false;
29736         });
29737         
29738         return ret;
29739     },
29740     
29741     indexOfItem : function(item)
29742     {
29743         var index = false;
29744         
29745         Roo.each(this.barItems, function(v, i){
29746             
29747             if (v.rid != item.rid) {
29748                 return;
29749             }
29750             
29751             index = i;
29752             return false
29753         });
29754         
29755         return index;
29756     },
29757     
29758     setActiveNext : function()
29759     {
29760         var i = this.indexOfItem(this.getActive());
29761         
29762         if (i > this.barItems.length) {
29763             return;
29764         }
29765         
29766         this.setActiveItem(this.barItems[i+1]);
29767     },
29768     
29769     setActivePrev : function()
29770     {
29771         var i = this.indexOfItem(this.getActive());
29772         
29773         if (i  < 1) {
29774             return;
29775         }
29776         
29777         this.setActiveItem(this.barItems[i-1]);
29778     },
29779     
29780     format : function()
29781     {
29782         if(!this.barItems.length){
29783             return;
29784         }
29785      
29786         var width = 100 / this.barItems.length;
29787         
29788         Roo.each(this.barItems, function(i){
29789             i.el.setStyle('width', width + '%');
29790             i.topEl.el.setStyle('width', width + '%');
29791             i.bottomEl.el.setStyle('width', width + '%');
29792         }, this);
29793         
29794     }
29795     
29796 });
29797 /*
29798  * - LGPL
29799  *
29800  * Nav Progress Item
29801  * 
29802  */
29803
29804 /**
29805  * @class Roo.bootstrap.NavProgressItem
29806  * @extends Roo.bootstrap.Component
29807  * Bootstrap NavProgressItem class
29808  * @cfg {String} rid the reference id
29809  * @cfg {Boolean} active (true|false) Is item active default false
29810  * @cfg {Boolean} disabled (true|false) Is item active default false
29811  * @cfg {String} html
29812  * @cfg {String} position (top|bottom) text position default bottom
29813  * @cfg {String} icon show icon instead of number
29814  * 
29815  * @constructor
29816  * Create a new NavProgressItem
29817  * @param {Object} config The config object
29818  */
29819 Roo.bootstrap.NavProgressItem = function(config){
29820     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29821     this.addEvents({
29822         // raw events
29823         /**
29824          * @event click
29825          * The raw click event for the entire grid.
29826          * @param {Roo.bootstrap.NavProgressItem} this
29827          * @param {Roo.EventObject} e
29828          */
29829         "click" : true
29830     });
29831    
29832 };
29833
29834 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29835     
29836     rid : '',
29837     active : false,
29838     disabled : false,
29839     html : '',
29840     position : 'bottom',
29841     icon : false,
29842     
29843     getAutoCreate : function()
29844     {
29845         var iconCls = 'roo-navigation-bar-item-icon';
29846         
29847         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29848         
29849         var cfg = {
29850             tag: 'li',
29851             cls: 'roo-navigation-bar-item',
29852             cn : [
29853                 {
29854                     tag : 'i',
29855                     cls : iconCls
29856                 }
29857             ]
29858         };
29859         
29860         if(this.active){
29861             cfg.cls += ' active';
29862         }
29863         if(this.disabled){
29864             cfg.cls += ' disabled';
29865         }
29866         
29867         return cfg;
29868     },
29869     
29870     disable : function()
29871     {
29872         this.setDisabled(true);
29873     },
29874     
29875     enable : function()
29876     {
29877         this.setDisabled(false);
29878     },
29879     
29880     initEvents: function() 
29881     {
29882         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29883         
29884         this.iconEl.on('click', this.onClick, this);
29885     },
29886     
29887     onClick : function(e)
29888     {
29889         e.preventDefault();
29890         
29891         if(this.disabled){
29892             return;
29893         }
29894         
29895         if(this.fireEvent('click', this, e) === false){
29896             return;
29897         };
29898         
29899         this.parent().setActiveItem(this);
29900     },
29901     
29902     isActive: function () 
29903     {
29904         return this.active;
29905     },
29906     
29907     setActive : function(state)
29908     {
29909         if(this.active == state){
29910             return;
29911         }
29912         
29913         this.active = state;
29914         
29915         if (state) {
29916             this.el.addClass('active');
29917             return;
29918         }
29919         
29920         this.el.removeClass('active');
29921         
29922         return;
29923     },
29924     
29925     setDisabled : function(state)
29926     {
29927         if(this.disabled == state){
29928             return;
29929         }
29930         
29931         this.disabled = state;
29932         
29933         if (state) {
29934             this.el.addClass('disabled');
29935             return;
29936         }
29937         
29938         this.el.removeClass('disabled');
29939     },
29940     
29941     tooltipEl : function()
29942     {
29943         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29944     }
29945 });
29946  
29947
29948  /*
29949  * - LGPL
29950  *
29951  * FieldLabel
29952  * 
29953  */
29954
29955 /**
29956  * @class Roo.bootstrap.FieldLabel
29957  * @extends Roo.bootstrap.Component
29958  * Bootstrap FieldLabel class
29959  * @cfg {String} html contents of the element
29960  * @cfg {String} tag tag of the element default label
29961  * @cfg {String} cls class of the element
29962  * @cfg {String} target label target 
29963  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29964  * @cfg {String} invalidClass default "text-warning"
29965  * @cfg {String} validClass default "text-success"
29966  * @cfg {String} iconTooltip default "This field is required"
29967  * @cfg {String} indicatorpos (left|right) default left
29968  * 
29969  * @constructor
29970  * Create a new FieldLabel
29971  * @param {Object} config The config object
29972  */
29973
29974 Roo.bootstrap.FieldLabel = function(config){
29975     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29976     
29977     this.addEvents({
29978             /**
29979              * @event invalid
29980              * Fires after the field has been marked as invalid.
29981              * @param {Roo.form.FieldLabel} this
29982              * @param {String} msg The validation message
29983              */
29984             invalid : true,
29985             /**
29986              * @event valid
29987              * Fires after the field has been validated with no errors.
29988              * @param {Roo.form.FieldLabel} this
29989              */
29990             valid : true
29991         });
29992 };
29993
29994 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29995     
29996     tag: 'label',
29997     cls: '',
29998     html: '',
29999     target: '',
30000     allowBlank : true,
30001     invalidClass : 'has-warning',
30002     validClass : 'has-success',
30003     iconTooltip : 'This field is required',
30004     indicatorpos : 'left',
30005     
30006     getAutoCreate : function(){
30007         
30008         var cfg = {
30009             tag : this.tag,
30010             cls : 'roo-bootstrap-field-label ' + this.cls,
30011             for : this.target,
30012             cn : [
30013                 {
30014                     tag : 'i',
30015                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
30016                     tooltip : this.iconTooltip
30017                 },
30018                 {
30019                     tag : 'span',
30020                     html : this.html
30021                 }
30022             ] 
30023         };
30024         
30025         if(this.indicatorpos == 'right'){
30026             var cfg = {
30027                 tag : this.tag,
30028                 cls : 'roo-bootstrap-field-label ' + this.cls,
30029                 for : this.target,
30030                 cn : [
30031                     {
30032                         tag : 'span',
30033                         html : this.html
30034                     },
30035                     {
30036                         tag : 'i',
30037                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
30038                         tooltip : this.iconTooltip
30039                     }
30040                 ] 
30041             };
30042         }
30043         
30044         return cfg;
30045     },
30046     
30047     initEvents: function() 
30048     {
30049         Roo.bootstrap.Element.superclass.initEvents.call(this);
30050         
30051         this.indicator = this.indicatorEl();
30052         
30053         if(this.indicator){
30054             this.indicator.removeClass('visible');
30055             this.indicator.addClass('invisible');
30056         }
30057         
30058         Roo.bootstrap.FieldLabel.register(this);
30059     },
30060     
30061     indicatorEl : function()
30062     {
30063         var indicator = this.el.select('i.roo-required-indicator',true).first();
30064         
30065         if(!indicator){
30066             return false;
30067         }
30068         
30069         return indicator;
30070         
30071     },
30072     
30073     /**
30074      * Mark this field as valid
30075      */
30076     markValid : function()
30077     {
30078         if(this.indicator){
30079             this.indicator.removeClass('visible');
30080             this.indicator.addClass('invisible');
30081         }
30082         
30083         this.el.removeClass(this.invalidClass);
30084         
30085         this.el.addClass(this.validClass);
30086         
30087         this.fireEvent('valid', this);
30088     },
30089     
30090     /**
30091      * Mark this field as invalid
30092      * @param {String} msg The validation message
30093      */
30094     markInvalid : function(msg)
30095     {
30096         if(this.indicator){
30097             this.indicator.removeClass('invisible');
30098             this.indicator.addClass('visible');
30099         }
30100         
30101         this.el.removeClass(this.validClass);
30102         
30103         this.el.addClass(this.invalidClass);
30104         
30105         this.fireEvent('invalid', this, msg);
30106     }
30107     
30108    
30109 });
30110
30111 Roo.apply(Roo.bootstrap.FieldLabel, {
30112     
30113     groups: {},
30114     
30115      /**
30116     * register a FieldLabel Group
30117     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30118     */
30119     register : function(label)
30120     {
30121         if(this.groups.hasOwnProperty(label.target)){
30122             return;
30123         }
30124      
30125         this.groups[label.target] = label;
30126         
30127     },
30128     /**
30129     * fetch a FieldLabel Group based on the target
30130     * @param {string} target
30131     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30132     */
30133     get: function(target) {
30134         if (typeof(this.groups[target]) == 'undefined') {
30135             return false;
30136         }
30137         
30138         return this.groups[target] ;
30139     }
30140 });
30141
30142  
30143
30144  /*
30145  * - LGPL
30146  *
30147  * page DateSplitField.
30148  * 
30149  */
30150
30151
30152 /**
30153  * @class Roo.bootstrap.DateSplitField
30154  * @extends Roo.bootstrap.Component
30155  * Bootstrap DateSplitField class
30156  * @cfg {string} fieldLabel - the label associated
30157  * @cfg {Number} labelWidth set the width of label (0-12)
30158  * @cfg {String} labelAlign (top|left)
30159  * @cfg {Boolean} dayAllowBlank (true|false) default false
30160  * @cfg {Boolean} monthAllowBlank (true|false) default false
30161  * @cfg {Boolean} yearAllowBlank (true|false) default false
30162  * @cfg {string} dayPlaceholder 
30163  * @cfg {string} monthPlaceholder
30164  * @cfg {string} yearPlaceholder
30165  * @cfg {string} dayFormat default 'd'
30166  * @cfg {string} monthFormat default 'm'
30167  * @cfg {string} yearFormat default 'Y'
30168  * @cfg {Number} labellg set the width of label (1-12)
30169  * @cfg {Number} labelmd set the width of label (1-12)
30170  * @cfg {Number} labelsm set the width of label (1-12)
30171  * @cfg {Number} labelxs set the width of label (1-12)
30172
30173  *     
30174  * @constructor
30175  * Create a new DateSplitField
30176  * @param {Object} config The config object
30177  */
30178
30179 Roo.bootstrap.DateSplitField = function(config){
30180     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30181     
30182     this.addEvents({
30183         // raw events
30184          /**
30185          * @event years
30186          * getting the data of years
30187          * @param {Roo.bootstrap.DateSplitField} this
30188          * @param {Object} years
30189          */
30190         "years" : true,
30191         /**
30192          * @event days
30193          * getting the data of days
30194          * @param {Roo.bootstrap.DateSplitField} this
30195          * @param {Object} days
30196          */
30197         "days" : true,
30198         /**
30199          * @event invalid
30200          * Fires after the field has been marked as invalid.
30201          * @param {Roo.form.Field} this
30202          * @param {String} msg The validation message
30203          */
30204         invalid : true,
30205        /**
30206          * @event valid
30207          * Fires after the field has been validated with no errors.
30208          * @param {Roo.form.Field} this
30209          */
30210         valid : true
30211     });
30212 };
30213
30214 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30215     
30216     fieldLabel : '',
30217     labelAlign : 'top',
30218     labelWidth : 3,
30219     dayAllowBlank : false,
30220     monthAllowBlank : false,
30221     yearAllowBlank : false,
30222     dayPlaceholder : '',
30223     monthPlaceholder : '',
30224     yearPlaceholder : '',
30225     dayFormat : 'd',
30226     monthFormat : 'm',
30227     yearFormat : 'Y',
30228     isFormField : true,
30229     labellg : 0,
30230     labelmd : 0,
30231     labelsm : 0,
30232     labelxs : 0,
30233     
30234     getAutoCreate : function()
30235     {
30236         var cfg = {
30237             tag : 'div',
30238             cls : 'row roo-date-split-field-group',
30239             cn : [
30240                 {
30241                     tag : 'input',
30242                     type : 'hidden',
30243                     cls : 'form-hidden-field roo-date-split-field-group-value',
30244                     name : this.name
30245                 }
30246             ]
30247         };
30248         
30249         var labelCls = 'col-md-12';
30250         var contentCls = 'col-md-4';
30251         
30252         if(this.fieldLabel){
30253             
30254             var label = {
30255                 tag : 'div',
30256                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30257                 cn : [
30258                     {
30259                         tag : 'label',
30260                         html : this.fieldLabel
30261                     }
30262                 ]
30263             };
30264             
30265             if(this.labelAlign == 'left'){
30266             
30267                 if(this.labelWidth > 12){
30268                     label.style = "width: " + this.labelWidth + 'px';
30269                 }
30270
30271                 if(this.labelWidth < 13 && this.labelmd == 0){
30272                     this.labelmd = this.labelWidth;
30273                 }
30274
30275                 if(this.labellg > 0){
30276                     labelCls = ' col-lg-' + this.labellg;
30277                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30278                 }
30279
30280                 if(this.labelmd > 0){
30281                     labelCls = ' col-md-' + this.labelmd;
30282                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30283                 }
30284
30285                 if(this.labelsm > 0){
30286                     labelCls = ' col-sm-' + this.labelsm;
30287                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30288                 }
30289
30290                 if(this.labelxs > 0){
30291                     labelCls = ' col-xs-' + this.labelxs;
30292                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30293                 }
30294             }
30295             
30296             label.cls += ' ' + labelCls;
30297             
30298             cfg.cn.push(label);
30299         }
30300         
30301         Roo.each(['day', 'month', 'year'], function(t){
30302             cfg.cn.push({
30303                 tag : 'div',
30304                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30305             });
30306         }, this);
30307         
30308         return cfg;
30309     },
30310     
30311     inputEl: function ()
30312     {
30313         return this.el.select('.roo-date-split-field-group-value', true).first();
30314     },
30315     
30316     onRender : function(ct, position) 
30317     {
30318         var _this = this;
30319         
30320         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30321         
30322         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30323         
30324         this.dayField = new Roo.bootstrap.ComboBox({
30325             allowBlank : this.dayAllowBlank,
30326             alwaysQuery : true,
30327             displayField : 'value',
30328             editable : false,
30329             fieldLabel : '',
30330             forceSelection : true,
30331             mode : 'local',
30332             placeholder : this.dayPlaceholder,
30333             selectOnFocus : true,
30334             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30335             triggerAction : 'all',
30336             typeAhead : true,
30337             valueField : 'value',
30338             store : new Roo.data.SimpleStore({
30339                 data : (function() {    
30340                     var days = [];
30341                     _this.fireEvent('days', _this, days);
30342                     return days;
30343                 })(),
30344                 fields : [ 'value' ]
30345             }),
30346             listeners : {
30347                 select : function (_self, record, index)
30348                 {
30349                     _this.setValue(_this.getValue());
30350                 }
30351             }
30352         });
30353
30354         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30355         
30356         this.monthField = new Roo.bootstrap.MonthField({
30357             after : '<i class=\"fa fa-calendar\"></i>',
30358             allowBlank : this.monthAllowBlank,
30359             placeholder : this.monthPlaceholder,
30360             readOnly : true,
30361             listeners : {
30362                 render : function (_self)
30363                 {
30364                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30365                         e.preventDefault();
30366                         _self.focus();
30367                     });
30368                 },
30369                 select : function (_self, oldvalue, newvalue)
30370                 {
30371                     _this.setValue(_this.getValue());
30372                 }
30373             }
30374         });
30375         
30376         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30377         
30378         this.yearField = new Roo.bootstrap.ComboBox({
30379             allowBlank : this.yearAllowBlank,
30380             alwaysQuery : true,
30381             displayField : 'value',
30382             editable : false,
30383             fieldLabel : '',
30384             forceSelection : true,
30385             mode : 'local',
30386             placeholder : this.yearPlaceholder,
30387             selectOnFocus : true,
30388             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30389             triggerAction : 'all',
30390             typeAhead : true,
30391             valueField : 'value',
30392             store : new Roo.data.SimpleStore({
30393                 data : (function() {
30394                     var years = [];
30395                     _this.fireEvent('years', _this, years);
30396                     return years;
30397                 })(),
30398                 fields : [ 'value' ]
30399             }),
30400             listeners : {
30401                 select : function (_self, record, index)
30402                 {
30403                     _this.setValue(_this.getValue());
30404                 }
30405             }
30406         });
30407
30408         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30409     },
30410     
30411     setValue : function(v, format)
30412     {
30413         this.inputEl.dom.value = v;
30414         
30415         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30416         
30417         var d = Date.parseDate(v, f);
30418         
30419         if(!d){
30420             this.validate();
30421             return;
30422         }
30423         
30424         this.setDay(d.format(this.dayFormat));
30425         this.setMonth(d.format(this.monthFormat));
30426         this.setYear(d.format(this.yearFormat));
30427         
30428         this.validate();
30429         
30430         return;
30431     },
30432     
30433     setDay : function(v)
30434     {
30435         this.dayField.setValue(v);
30436         this.inputEl.dom.value = this.getValue();
30437         this.validate();
30438         return;
30439     },
30440     
30441     setMonth : function(v)
30442     {
30443         this.monthField.setValue(v, true);
30444         this.inputEl.dom.value = this.getValue();
30445         this.validate();
30446         return;
30447     },
30448     
30449     setYear : function(v)
30450     {
30451         this.yearField.setValue(v);
30452         this.inputEl.dom.value = this.getValue();
30453         this.validate();
30454         return;
30455     },
30456     
30457     getDay : function()
30458     {
30459         return this.dayField.getValue();
30460     },
30461     
30462     getMonth : function()
30463     {
30464         return this.monthField.getValue();
30465     },
30466     
30467     getYear : function()
30468     {
30469         return this.yearField.getValue();
30470     },
30471     
30472     getValue : function()
30473     {
30474         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30475         
30476         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30477         
30478         return date;
30479     },
30480     
30481     reset : function()
30482     {
30483         this.setDay('');
30484         this.setMonth('');
30485         this.setYear('');
30486         this.inputEl.dom.value = '';
30487         this.validate();
30488         return;
30489     },
30490     
30491     validate : function()
30492     {
30493         var d = this.dayField.validate();
30494         var m = this.monthField.validate();
30495         var y = this.yearField.validate();
30496         
30497         var valid = true;
30498         
30499         if(
30500                 (!this.dayAllowBlank && !d) ||
30501                 (!this.monthAllowBlank && !m) ||
30502                 (!this.yearAllowBlank && !y)
30503         ){
30504             valid = false;
30505         }
30506         
30507         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30508             return valid;
30509         }
30510         
30511         if(valid){
30512             this.markValid();
30513             return valid;
30514         }
30515         
30516         this.markInvalid();
30517         
30518         return valid;
30519     },
30520     
30521     markValid : function()
30522     {
30523         
30524         var label = this.el.select('label', true).first();
30525         var icon = this.el.select('i.fa-star', true).first();
30526
30527         if(label && icon){
30528             icon.remove();
30529         }
30530         
30531         this.fireEvent('valid', this);
30532     },
30533     
30534      /**
30535      * Mark this field as invalid
30536      * @param {String} msg The validation message
30537      */
30538     markInvalid : function(msg)
30539     {
30540         
30541         var label = this.el.select('label', true).first();
30542         var icon = this.el.select('i.fa-star', true).first();
30543
30544         if(label && !icon){
30545             this.el.select('.roo-date-split-field-label', true).createChild({
30546                 tag : 'i',
30547                 cls : 'text-danger fa fa-lg fa-star',
30548                 tooltip : 'This field is required',
30549                 style : 'margin-right:5px;'
30550             }, label, true);
30551         }
30552         
30553         this.fireEvent('invalid', this, msg);
30554     },
30555     
30556     clearInvalid : function()
30557     {
30558         var label = this.el.select('label', true).first();
30559         var icon = this.el.select('i.fa-star', true).first();
30560
30561         if(label && icon){
30562             icon.remove();
30563         }
30564         
30565         this.fireEvent('valid', this);
30566     },
30567     
30568     getName: function()
30569     {
30570         return this.name;
30571     }
30572     
30573 });
30574
30575  /**
30576  *
30577  * This is based on 
30578  * http://masonry.desandro.com
30579  *
30580  * The idea is to render all the bricks based on vertical width...
30581  *
30582  * The original code extends 'outlayer' - we might need to use that....
30583  * 
30584  */
30585
30586
30587 /**
30588  * @class Roo.bootstrap.LayoutMasonry
30589  * @extends Roo.bootstrap.Component
30590  * Bootstrap Layout Masonry class
30591  * 
30592  * @constructor
30593  * Create a new Element
30594  * @param {Object} config The config object
30595  */
30596
30597 Roo.bootstrap.LayoutMasonry = function(config){
30598     
30599     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30600     
30601     this.bricks = [];
30602     
30603     Roo.bootstrap.LayoutMasonry.register(this);
30604     
30605     this.addEvents({
30606         // raw events
30607         /**
30608          * @event layout
30609          * Fire after layout the items
30610          * @param {Roo.bootstrap.LayoutMasonry} this
30611          * @param {Roo.EventObject} e
30612          */
30613         "layout" : true
30614     });
30615     
30616 };
30617
30618 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30619     
30620     /**
30621      * @cfg {Boolean} isLayoutInstant = no animation?
30622      */   
30623     isLayoutInstant : false, // needed?
30624    
30625     /**
30626      * @cfg {Number} boxWidth  width of the columns
30627      */   
30628     boxWidth : 450,
30629     
30630       /**
30631      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30632      */   
30633     boxHeight : 0,
30634     
30635     /**
30636      * @cfg {Number} padWidth padding below box..
30637      */   
30638     padWidth : 10, 
30639     
30640     /**
30641      * @cfg {Number} gutter gutter width..
30642      */   
30643     gutter : 10,
30644     
30645      /**
30646      * @cfg {Number} maxCols maximum number of columns
30647      */   
30648     
30649     maxCols: 0,
30650     
30651     /**
30652      * @cfg {Boolean} isAutoInitial defalut true
30653      */   
30654     isAutoInitial : true, 
30655     
30656     containerWidth: 0,
30657     
30658     /**
30659      * @cfg {Boolean} isHorizontal defalut false
30660      */   
30661     isHorizontal : false, 
30662
30663     currentSize : null,
30664     
30665     tag: 'div',
30666     
30667     cls: '',
30668     
30669     bricks: null, //CompositeElement
30670     
30671     cols : 1,
30672     
30673     _isLayoutInited : false,
30674     
30675 //    isAlternative : false, // only use for vertical layout...
30676     
30677     /**
30678      * @cfg {Number} alternativePadWidth padding below box..
30679      */   
30680     alternativePadWidth : 50,
30681     
30682     selectedBrick : [],
30683     
30684     getAutoCreate : function(){
30685         
30686         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30687         
30688         var cfg = {
30689             tag: this.tag,
30690             cls: 'blog-masonary-wrapper ' + this.cls,
30691             cn : {
30692                 cls : 'mas-boxes masonary'
30693             }
30694         };
30695         
30696         return cfg;
30697     },
30698     
30699     getChildContainer: function( )
30700     {
30701         if (this.boxesEl) {
30702             return this.boxesEl;
30703         }
30704         
30705         this.boxesEl = this.el.select('.mas-boxes').first();
30706         
30707         return this.boxesEl;
30708     },
30709     
30710     
30711     initEvents : function()
30712     {
30713         var _this = this;
30714         
30715         if(this.isAutoInitial){
30716             Roo.log('hook children rendered');
30717             this.on('childrenrendered', function() {
30718                 Roo.log('children rendered');
30719                 _this.initial();
30720             } ,this);
30721         }
30722     },
30723     
30724     initial : function()
30725     {
30726         this.selectedBrick = [];
30727         
30728         this.currentSize = this.el.getBox(true);
30729         
30730         Roo.EventManager.onWindowResize(this.resize, this); 
30731
30732         if(!this.isAutoInitial){
30733             this.layout();
30734             return;
30735         }
30736         
30737         this.layout();
30738         
30739         return;
30740         //this.layout.defer(500,this);
30741         
30742     },
30743     
30744     resize : function()
30745     {
30746         var cs = this.el.getBox(true);
30747         
30748         if (
30749                 this.currentSize.width == cs.width && 
30750                 this.currentSize.x == cs.x && 
30751                 this.currentSize.height == cs.height && 
30752                 this.currentSize.y == cs.y 
30753         ) {
30754             Roo.log("no change in with or X or Y");
30755             return;
30756         }
30757         
30758         this.currentSize = cs;
30759         
30760         this.layout();
30761         
30762     },
30763     
30764     layout : function()
30765     {   
30766         this._resetLayout();
30767         
30768         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30769         
30770         this.layoutItems( isInstant );
30771       
30772         this._isLayoutInited = true;
30773         
30774         this.fireEvent('layout', this);
30775         
30776     },
30777     
30778     _resetLayout : function()
30779     {
30780         if(this.isHorizontal){
30781             this.horizontalMeasureColumns();
30782             return;
30783         }
30784         
30785         this.verticalMeasureColumns();
30786         
30787     },
30788     
30789     verticalMeasureColumns : function()
30790     {
30791         this.getContainerWidth();
30792         
30793 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30794 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30795 //            return;
30796 //        }
30797         
30798         var boxWidth = this.boxWidth + this.padWidth;
30799         
30800         if(this.containerWidth < this.boxWidth){
30801             boxWidth = this.containerWidth
30802         }
30803         
30804         var containerWidth = this.containerWidth;
30805         
30806         var cols = Math.floor(containerWidth / boxWidth);
30807         
30808         this.cols = Math.max( cols, 1 );
30809         
30810         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30811         
30812         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30813         
30814         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30815         
30816         this.colWidth = boxWidth + avail - this.padWidth;
30817         
30818         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30819         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30820     },
30821     
30822     horizontalMeasureColumns : function()
30823     {
30824         this.getContainerWidth();
30825         
30826         var boxWidth = this.boxWidth;
30827         
30828         if(this.containerWidth < boxWidth){
30829             boxWidth = this.containerWidth;
30830         }
30831         
30832         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30833         
30834         this.el.setHeight(boxWidth);
30835         
30836     },
30837     
30838     getContainerWidth : function()
30839     {
30840         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30841     },
30842     
30843     layoutItems : function( isInstant )
30844     {
30845         Roo.log(this.bricks);
30846         
30847         var items = Roo.apply([], this.bricks);
30848         
30849         if(this.isHorizontal){
30850             this._horizontalLayoutItems( items , isInstant );
30851             return;
30852         }
30853         
30854 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30855 //            this._verticalAlternativeLayoutItems( items , isInstant );
30856 //            return;
30857 //        }
30858         
30859         this._verticalLayoutItems( items , isInstant );
30860         
30861     },
30862     
30863     _verticalLayoutItems : function ( items , isInstant)
30864     {
30865         if ( !items || !items.length ) {
30866             return;
30867         }
30868         
30869         var standard = [
30870             ['xs', 'xs', 'xs', 'tall'],
30871             ['xs', 'xs', 'tall'],
30872             ['xs', 'xs', 'sm'],
30873             ['xs', 'xs', 'xs'],
30874             ['xs', 'tall'],
30875             ['xs', 'sm'],
30876             ['xs', 'xs'],
30877             ['xs'],
30878             
30879             ['sm', 'xs', 'xs'],
30880             ['sm', 'xs'],
30881             ['sm'],
30882             
30883             ['tall', 'xs', 'xs', 'xs'],
30884             ['tall', 'xs', 'xs'],
30885             ['tall', 'xs'],
30886             ['tall']
30887             
30888         ];
30889         
30890         var queue = [];
30891         
30892         var boxes = [];
30893         
30894         var box = [];
30895         
30896         Roo.each(items, function(item, k){
30897             
30898             switch (item.size) {
30899                 // these layouts take up a full box,
30900                 case 'md' :
30901                 case 'md-left' :
30902                 case 'md-right' :
30903                 case 'wide' :
30904                     
30905                     if(box.length){
30906                         boxes.push(box);
30907                         box = [];
30908                     }
30909                     
30910                     boxes.push([item]);
30911                     
30912                     break;
30913                     
30914                 case 'xs' :
30915                 case 'sm' :
30916                 case 'tall' :
30917                     
30918                     box.push(item);
30919                     
30920                     break;
30921                 default :
30922                     break;
30923                     
30924             }
30925             
30926         }, this);
30927         
30928         if(box.length){
30929             boxes.push(box);
30930             box = [];
30931         }
30932         
30933         var filterPattern = function(box, length)
30934         {
30935             if(!box.length){
30936                 return;
30937             }
30938             
30939             var match = false;
30940             
30941             var pattern = box.slice(0, length);
30942             
30943             var format = [];
30944             
30945             Roo.each(pattern, function(i){
30946                 format.push(i.size);
30947             }, this);
30948             
30949             Roo.each(standard, function(s){
30950                 
30951                 if(String(s) != String(format)){
30952                     return;
30953                 }
30954                 
30955                 match = true;
30956                 return false;
30957                 
30958             }, this);
30959             
30960             if(!match && length == 1){
30961                 return;
30962             }
30963             
30964             if(!match){
30965                 filterPattern(box, length - 1);
30966                 return;
30967             }
30968                 
30969             queue.push(pattern);
30970
30971             box = box.slice(length, box.length);
30972
30973             filterPattern(box, 4);
30974
30975             return;
30976             
30977         }
30978         
30979         Roo.each(boxes, function(box, k){
30980             
30981             if(!box.length){
30982                 return;
30983             }
30984             
30985             if(box.length == 1){
30986                 queue.push(box);
30987                 return;
30988             }
30989             
30990             filterPattern(box, 4);
30991             
30992         }, this);
30993         
30994         this._processVerticalLayoutQueue( queue, isInstant );
30995         
30996     },
30997     
30998 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30999 //    {
31000 //        if ( !items || !items.length ) {
31001 //            return;
31002 //        }
31003 //
31004 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31005 //        
31006 //    },
31007     
31008     _horizontalLayoutItems : function ( items , isInstant)
31009     {
31010         if ( !items || !items.length || items.length < 3) {
31011             return;
31012         }
31013         
31014         items.reverse();
31015         
31016         var eItems = items.slice(0, 3);
31017         
31018         items = items.slice(3, items.length);
31019         
31020         var standard = [
31021             ['xs', 'xs', 'xs', 'wide'],
31022             ['xs', 'xs', 'wide'],
31023             ['xs', 'xs', 'sm'],
31024             ['xs', 'xs', 'xs'],
31025             ['xs', 'wide'],
31026             ['xs', 'sm'],
31027             ['xs', 'xs'],
31028             ['xs'],
31029             
31030             ['sm', 'xs', 'xs'],
31031             ['sm', 'xs'],
31032             ['sm'],
31033             
31034             ['wide', 'xs', 'xs', 'xs'],
31035             ['wide', 'xs', 'xs'],
31036             ['wide', 'xs'],
31037             ['wide'],
31038             
31039             ['wide-thin']
31040         ];
31041         
31042         var queue = [];
31043         
31044         var boxes = [];
31045         
31046         var box = [];
31047         
31048         Roo.each(items, function(item, k){
31049             
31050             switch (item.size) {
31051                 case 'md' :
31052                 case 'md-left' :
31053                 case 'md-right' :
31054                 case 'tall' :
31055                     
31056                     if(box.length){
31057                         boxes.push(box);
31058                         box = [];
31059                     }
31060                     
31061                     boxes.push([item]);
31062                     
31063                     break;
31064                     
31065                 case 'xs' :
31066                 case 'sm' :
31067                 case 'wide' :
31068                 case 'wide-thin' :
31069                     
31070                     box.push(item);
31071                     
31072                     break;
31073                 default :
31074                     break;
31075                     
31076             }
31077             
31078         }, this);
31079         
31080         if(box.length){
31081             boxes.push(box);
31082             box = [];
31083         }
31084         
31085         var filterPattern = function(box, length)
31086         {
31087             if(!box.length){
31088                 return;
31089             }
31090             
31091             var match = false;
31092             
31093             var pattern = box.slice(0, length);
31094             
31095             var format = [];
31096             
31097             Roo.each(pattern, function(i){
31098                 format.push(i.size);
31099             }, this);
31100             
31101             Roo.each(standard, function(s){
31102                 
31103                 if(String(s) != String(format)){
31104                     return;
31105                 }
31106                 
31107                 match = true;
31108                 return false;
31109                 
31110             }, this);
31111             
31112             if(!match && length == 1){
31113                 return;
31114             }
31115             
31116             if(!match){
31117                 filterPattern(box, length - 1);
31118                 return;
31119             }
31120                 
31121             queue.push(pattern);
31122
31123             box = box.slice(length, box.length);
31124
31125             filterPattern(box, 4);
31126
31127             return;
31128             
31129         }
31130         
31131         Roo.each(boxes, function(box, k){
31132             
31133             if(!box.length){
31134                 return;
31135             }
31136             
31137             if(box.length == 1){
31138                 queue.push(box);
31139                 return;
31140             }
31141             
31142             filterPattern(box, 4);
31143             
31144         }, this);
31145         
31146         
31147         var prune = [];
31148         
31149         var pos = this.el.getBox(true);
31150         
31151         var minX = pos.x;
31152         
31153         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31154         
31155         var hit_end = false;
31156         
31157         Roo.each(queue, function(box){
31158             
31159             if(hit_end){
31160                 
31161                 Roo.each(box, function(b){
31162                 
31163                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31164                     b.el.hide();
31165
31166                 }, this);
31167
31168                 return;
31169             }
31170             
31171             var mx = 0;
31172             
31173             Roo.each(box, function(b){
31174                 
31175                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31176                 b.el.show();
31177
31178                 mx = Math.max(mx, b.x);
31179                 
31180             }, this);
31181             
31182             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31183             
31184             if(maxX < minX){
31185                 
31186                 Roo.each(box, function(b){
31187                 
31188                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31189                     b.el.hide();
31190                     
31191                 }, this);
31192                 
31193                 hit_end = true;
31194                 
31195                 return;
31196             }
31197             
31198             prune.push(box);
31199             
31200         }, this);
31201         
31202         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31203     },
31204     
31205     /** Sets position of item in DOM
31206     * @param {Element} item
31207     * @param {Number} x - horizontal position
31208     * @param {Number} y - vertical position
31209     * @param {Boolean} isInstant - disables transitions
31210     */
31211     _processVerticalLayoutQueue : function( queue, isInstant )
31212     {
31213         var pos = this.el.getBox(true);
31214         var x = pos.x;
31215         var y = pos.y;
31216         var maxY = [];
31217         
31218         for (var i = 0; i < this.cols; i++){
31219             maxY[i] = pos.y;
31220         }
31221         
31222         Roo.each(queue, function(box, k){
31223             
31224             var col = k % this.cols;
31225             
31226             Roo.each(box, function(b,kk){
31227                 
31228                 b.el.position('absolute');
31229                 
31230                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31231                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31232                 
31233                 if(b.size == 'md-left' || b.size == 'md-right'){
31234                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31235                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31236                 }
31237                 
31238                 b.el.setWidth(width);
31239                 b.el.setHeight(height);
31240                 // iframe?
31241                 b.el.select('iframe',true).setSize(width,height);
31242                 
31243             }, this);
31244             
31245             for (var i = 0; i < this.cols; i++){
31246                 
31247                 if(maxY[i] < maxY[col]){
31248                     col = i;
31249                     continue;
31250                 }
31251                 
31252                 col = Math.min(col, i);
31253                 
31254             }
31255             
31256             x = pos.x + col * (this.colWidth + this.padWidth);
31257             
31258             y = maxY[col];
31259             
31260             var positions = [];
31261             
31262             switch (box.length){
31263                 case 1 :
31264                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31265                     break;
31266                 case 2 :
31267                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31268                     break;
31269                 case 3 :
31270                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31271                     break;
31272                 case 4 :
31273                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31274                     break;
31275                 default :
31276                     break;
31277             }
31278             
31279             Roo.each(box, function(b,kk){
31280                 
31281                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31282                 
31283                 var sz = b.el.getSize();
31284                 
31285                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31286                 
31287             }, this);
31288             
31289         }, this);
31290         
31291         var mY = 0;
31292         
31293         for (var i = 0; i < this.cols; i++){
31294             mY = Math.max(mY, maxY[i]);
31295         }
31296         
31297         this.el.setHeight(mY - pos.y);
31298         
31299     },
31300     
31301 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31302 //    {
31303 //        var pos = this.el.getBox(true);
31304 //        var x = pos.x;
31305 //        var y = pos.y;
31306 //        var maxX = pos.right;
31307 //        
31308 //        var maxHeight = 0;
31309 //        
31310 //        Roo.each(items, function(item, k){
31311 //            
31312 //            var c = k % 2;
31313 //            
31314 //            item.el.position('absolute');
31315 //                
31316 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31317 //
31318 //            item.el.setWidth(width);
31319 //
31320 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31321 //
31322 //            item.el.setHeight(height);
31323 //            
31324 //            if(c == 0){
31325 //                item.el.setXY([x, y], isInstant ? false : true);
31326 //            } else {
31327 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31328 //            }
31329 //            
31330 //            y = y + height + this.alternativePadWidth;
31331 //            
31332 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31333 //            
31334 //        }, this);
31335 //        
31336 //        this.el.setHeight(maxHeight);
31337 //        
31338 //    },
31339     
31340     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31341     {
31342         var pos = this.el.getBox(true);
31343         
31344         var minX = pos.x;
31345         var minY = pos.y;
31346         
31347         var maxX = pos.right;
31348         
31349         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31350         
31351         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31352         
31353         Roo.each(queue, function(box, k){
31354             
31355             Roo.each(box, function(b, kk){
31356                 
31357                 b.el.position('absolute');
31358                 
31359                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31360                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31361                 
31362                 if(b.size == 'md-left' || b.size == 'md-right'){
31363                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31364                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31365                 }
31366                 
31367                 b.el.setWidth(width);
31368                 b.el.setHeight(height);
31369                 
31370             }, this);
31371             
31372             if(!box.length){
31373                 return;
31374             }
31375             
31376             var positions = [];
31377             
31378             switch (box.length){
31379                 case 1 :
31380                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31381                     break;
31382                 case 2 :
31383                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31384                     break;
31385                 case 3 :
31386                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31387                     break;
31388                 case 4 :
31389                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31390                     break;
31391                 default :
31392                     break;
31393             }
31394             
31395             Roo.each(box, function(b,kk){
31396                 
31397                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31398                 
31399                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31400                 
31401             }, this);
31402             
31403         }, this);
31404         
31405     },
31406     
31407     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31408     {
31409         Roo.each(eItems, function(b,k){
31410             
31411             b.size = (k == 0) ? 'sm' : 'xs';
31412             b.x = (k == 0) ? 2 : 1;
31413             b.y = (k == 0) ? 2 : 1;
31414             
31415             b.el.position('absolute');
31416             
31417             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31418                 
31419             b.el.setWidth(width);
31420             
31421             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31422             
31423             b.el.setHeight(height);
31424             
31425         }, this);
31426
31427         var positions = [];
31428         
31429         positions.push({
31430             x : maxX - this.unitWidth * 2 - this.gutter,
31431             y : minY
31432         });
31433         
31434         positions.push({
31435             x : maxX - this.unitWidth,
31436             y : minY + (this.unitWidth + this.gutter) * 2
31437         });
31438         
31439         positions.push({
31440             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31441             y : minY
31442         });
31443         
31444         Roo.each(eItems, function(b,k){
31445             
31446             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31447
31448         }, this);
31449         
31450     },
31451     
31452     getVerticalOneBoxColPositions : function(x, y, box)
31453     {
31454         var pos = [];
31455         
31456         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31457         
31458         if(box[0].size == 'md-left'){
31459             rand = 0;
31460         }
31461         
31462         if(box[0].size == 'md-right'){
31463             rand = 1;
31464         }
31465         
31466         pos.push({
31467             x : x + (this.unitWidth + this.gutter) * rand,
31468             y : y
31469         });
31470         
31471         return pos;
31472     },
31473     
31474     getVerticalTwoBoxColPositions : function(x, y, box)
31475     {
31476         var pos = [];
31477         
31478         if(box[0].size == 'xs'){
31479             
31480             pos.push({
31481                 x : x,
31482                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31483             });
31484
31485             pos.push({
31486                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31487                 y : y
31488             });
31489             
31490             return pos;
31491             
31492         }
31493         
31494         pos.push({
31495             x : x,
31496             y : y
31497         });
31498
31499         pos.push({
31500             x : x + (this.unitWidth + this.gutter) * 2,
31501             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31502         });
31503         
31504         return pos;
31505         
31506     },
31507     
31508     getVerticalThreeBoxColPositions : function(x, y, box)
31509     {
31510         var pos = [];
31511         
31512         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31513             
31514             pos.push({
31515                 x : x,
31516                 y : y
31517             });
31518
31519             pos.push({
31520                 x : x + (this.unitWidth + this.gutter) * 1,
31521                 y : y
31522             });
31523             
31524             pos.push({
31525                 x : x + (this.unitWidth + this.gutter) * 2,
31526                 y : y
31527             });
31528             
31529             return pos;
31530             
31531         }
31532         
31533         if(box[0].size == 'xs' && box[1].size == 'xs'){
31534             
31535             pos.push({
31536                 x : x,
31537                 y : y
31538             });
31539
31540             pos.push({
31541                 x : x,
31542                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31543             });
31544             
31545             pos.push({
31546                 x : x + (this.unitWidth + this.gutter) * 1,
31547                 y : y
31548             });
31549             
31550             return pos;
31551             
31552         }
31553         
31554         pos.push({
31555             x : x,
31556             y : y
31557         });
31558
31559         pos.push({
31560             x : x + (this.unitWidth + this.gutter) * 2,
31561             y : y
31562         });
31563
31564         pos.push({
31565             x : x + (this.unitWidth + this.gutter) * 2,
31566             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31567         });
31568             
31569         return pos;
31570         
31571     },
31572     
31573     getVerticalFourBoxColPositions : function(x, y, box)
31574     {
31575         var pos = [];
31576         
31577         if(box[0].size == 'xs'){
31578             
31579             pos.push({
31580                 x : x,
31581                 y : y
31582             });
31583
31584             pos.push({
31585                 x : x,
31586                 y : y + (this.unitHeight + this.gutter) * 1
31587             });
31588             
31589             pos.push({
31590                 x : x,
31591                 y : y + (this.unitHeight + this.gutter) * 2
31592             });
31593             
31594             pos.push({
31595                 x : x + (this.unitWidth + this.gutter) * 1,
31596                 y : y
31597             });
31598             
31599             return pos;
31600             
31601         }
31602         
31603         pos.push({
31604             x : x,
31605             y : y
31606         });
31607
31608         pos.push({
31609             x : x + (this.unitWidth + this.gutter) * 2,
31610             y : y
31611         });
31612
31613         pos.push({
31614             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31615             y : y + (this.unitHeight + this.gutter) * 1
31616         });
31617
31618         pos.push({
31619             x : x + (this.unitWidth + this.gutter) * 2,
31620             y : y + (this.unitWidth + this.gutter) * 2
31621         });
31622
31623         return pos;
31624         
31625     },
31626     
31627     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31628     {
31629         var pos = [];
31630         
31631         if(box[0].size == 'md-left'){
31632             pos.push({
31633                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31634                 y : minY
31635             });
31636             
31637             return pos;
31638         }
31639         
31640         if(box[0].size == 'md-right'){
31641             pos.push({
31642                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31643                 y : minY + (this.unitWidth + this.gutter) * 1
31644             });
31645             
31646             return pos;
31647         }
31648         
31649         var rand = Math.floor(Math.random() * (4 - box[0].y));
31650         
31651         pos.push({
31652             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31653             y : minY + (this.unitWidth + this.gutter) * rand
31654         });
31655         
31656         return pos;
31657         
31658     },
31659     
31660     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31661     {
31662         var pos = [];
31663         
31664         if(box[0].size == 'xs'){
31665             
31666             pos.push({
31667                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31668                 y : minY
31669             });
31670
31671             pos.push({
31672                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31673                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31674             });
31675             
31676             return pos;
31677             
31678         }
31679         
31680         pos.push({
31681             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31682             y : minY
31683         });
31684
31685         pos.push({
31686             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31687             y : minY + (this.unitWidth + this.gutter) * 2
31688         });
31689         
31690         return pos;
31691         
31692     },
31693     
31694     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31695     {
31696         var pos = [];
31697         
31698         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31699             
31700             pos.push({
31701                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31702                 y : minY
31703             });
31704
31705             pos.push({
31706                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31707                 y : minY + (this.unitWidth + this.gutter) * 1
31708             });
31709             
31710             pos.push({
31711                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31712                 y : minY + (this.unitWidth + this.gutter) * 2
31713             });
31714             
31715             return pos;
31716             
31717         }
31718         
31719         if(box[0].size == 'xs' && box[1].size == 'xs'){
31720             
31721             pos.push({
31722                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31723                 y : minY
31724             });
31725
31726             pos.push({
31727                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31728                 y : minY
31729             });
31730             
31731             pos.push({
31732                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31733                 y : minY + (this.unitWidth + this.gutter) * 1
31734             });
31735             
31736             return pos;
31737             
31738         }
31739         
31740         pos.push({
31741             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31742             y : minY
31743         });
31744
31745         pos.push({
31746             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31747             y : minY + (this.unitWidth + this.gutter) * 2
31748         });
31749
31750         pos.push({
31751             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31752             y : minY + (this.unitWidth + this.gutter) * 2
31753         });
31754             
31755         return pos;
31756         
31757     },
31758     
31759     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31760     {
31761         var pos = [];
31762         
31763         if(box[0].size == 'xs'){
31764             
31765             pos.push({
31766                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31767                 y : minY
31768             });
31769
31770             pos.push({
31771                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31772                 y : minY
31773             });
31774             
31775             pos.push({
31776                 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),
31777                 y : minY
31778             });
31779             
31780             pos.push({
31781                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31782                 y : minY + (this.unitWidth + this.gutter) * 1
31783             });
31784             
31785             return pos;
31786             
31787         }
31788         
31789         pos.push({
31790             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31791             y : minY
31792         });
31793         
31794         pos.push({
31795             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31796             y : minY + (this.unitWidth + this.gutter) * 2
31797         });
31798         
31799         pos.push({
31800             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31801             y : minY + (this.unitWidth + this.gutter) * 2
31802         });
31803         
31804         pos.push({
31805             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),
31806             y : minY + (this.unitWidth + this.gutter) * 2
31807         });
31808
31809         return pos;
31810         
31811     },
31812     
31813     /**
31814     * remove a Masonry Brick
31815     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31816     */
31817     removeBrick : function(brick_id)
31818     {
31819         if (!brick_id) {
31820             return;
31821         }
31822         
31823         for (var i = 0; i<this.bricks.length; i++) {
31824             if (this.bricks[i].id == brick_id) {
31825                 this.bricks.splice(i,1);
31826                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31827                 this.initial();
31828             }
31829         }
31830     },
31831     
31832     /**
31833     * adds a Masonry Brick
31834     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31835     */
31836     addBrick : function(cfg)
31837     {
31838         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31839         //this.register(cn);
31840         cn.parentId = this.id;
31841         cn.onRender(this.el, null);
31842         return cn;
31843     },
31844     
31845     /**
31846     * register a Masonry Brick
31847     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31848     */
31849     
31850     register : function(brick)
31851     {
31852         this.bricks.push(brick);
31853         brick.masonryId = this.id;
31854     },
31855     
31856     /**
31857     * clear all the Masonry Brick
31858     */
31859     clearAll : function()
31860     {
31861         this.bricks = [];
31862         //this.getChildContainer().dom.innerHTML = "";
31863         this.el.dom.innerHTML = '';
31864     },
31865     
31866     getSelected : function()
31867     {
31868         if (!this.selectedBrick) {
31869             return false;
31870         }
31871         
31872         return this.selectedBrick;
31873     }
31874 });
31875
31876 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31877     
31878     groups: {},
31879      /**
31880     * register a Masonry Layout
31881     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31882     */
31883     
31884     register : function(layout)
31885     {
31886         this.groups[layout.id] = layout;
31887     },
31888     /**
31889     * fetch a  Masonry Layout based on the masonry layout ID
31890     * @param {string} the masonry layout to add
31891     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31892     */
31893     
31894     get: function(layout_id) {
31895         if (typeof(this.groups[layout_id]) == 'undefined') {
31896             return false;
31897         }
31898         return this.groups[layout_id] ;
31899     }
31900     
31901     
31902     
31903 });
31904
31905  
31906
31907  /**
31908  *
31909  * This is based on 
31910  * http://masonry.desandro.com
31911  *
31912  * The idea is to render all the bricks based on vertical width...
31913  *
31914  * The original code extends 'outlayer' - we might need to use that....
31915  * 
31916  */
31917
31918
31919 /**
31920  * @class Roo.bootstrap.LayoutMasonryAuto
31921  * @extends Roo.bootstrap.Component
31922  * Bootstrap Layout Masonry class
31923  * 
31924  * @constructor
31925  * Create a new Element
31926  * @param {Object} config The config object
31927  */
31928
31929 Roo.bootstrap.LayoutMasonryAuto = function(config){
31930     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31931 };
31932
31933 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31934     
31935       /**
31936      * @cfg {Boolean} isFitWidth  - resize the width..
31937      */   
31938     isFitWidth : false,  // options..
31939     /**
31940      * @cfg {Boolean} isOriginLeft = left align?
31941      */   
31942     isOriginLeft : true,
31943     /**
31944      * @cfg {Boolean} isOriginTop = top align?
31945      */   
31946     isOriginTop : false,
31947     /**
31948      * @cfg {Boolean} isLayoutInstant = no animation?
31949      */   
31950     isLayoutInstant : false, // needed?
31951     /**
31952      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31953      */   
31954     isResizingContainer : true,
31955     /**
31956      * @cfg {Number} columnWidth  width of the columns 
31957      */   
31958     
31959     columnWidth : 0,
31960     
31961     /**
31962      * @cfg {Number} maxCols maximum number of columns
31963      */   
31964     
31965     maxCols: 0,
31966     /**
31967      * @cfg {Number} padHeight padding below box..
31968      */   
31969     
31970     padHeight : 10, 
31971     
31972     /**
31973      * @cfg {Boolean} isAutoInitial defalut true
31974      */   
31975     
31976     isAutoInitial : true, 
31977     
31978     // private?
31979     gutter : 0,
31980     
31981     containerWidth: 0,
31982     initialColumnWidth : 0,
31983     currentSize : null,
31984     
31985     colYs : null, // array.
31986     maxY : 0,
31987     padWidth: 10,
31988     
31989     
31990     tag: 'div',
31991     cls: '',
31992     bricks: null, //CompositeElement
31993     cols : 0, // array?
31994     // element : null, // wrapped now this.el
31995     _isLayoutInited : null, 
31996     
31997     
31998     getAutoCreate : function(){
31999         
32000         var cfg = {
32001             tag: this.tag,
32002             cls: 'blog-masonary-wrapper ' + this.cls,
32003             cn : {
32004                 cls : 'mas-boxes masonary'
32005             }
32006         };
32007         
32008         return cfg;
32009     },
32010     
32011     getChildContainer: function( )
32012     {
32013         if (this.boxesEl) {
32014             return this.boxesEl;
32015         }
32016         
32017         this.boxesEl = this.el.select('.mas-boxes').first();
32018         
32019         return this.boxesEl;
32020     },
32021     
32022     
32023     initEvents : function()
32024     {
32025         var _this = this;
32026         
32027         if(this.isAutoInitial){
32028             Roo.log('hook children rendered');
32029             this.on('childrenrendered', function() {
32030                 Roo.log('children rendered');
32031                 _this.initial();
32032             } ,this);
32033         }
32034         
32035     },
32036     
32037     initial : function()
32038     {
32039         this.reloadItems();
32040
32041         this.currentSize = this.el.getBox(true);
32042
32043         /// was window resize... - let's see if this works..
32044         Roo.EventManager.onWindowResize(this.resize, this); 
32045
32046         if(!this.isAutoInitial){
32047             this.layout();
32048             return;
32049         }
32050         
32051         this.layout.defer(500,this);
32052     },
32053     
32054     reloadItems: function()
32055     {
32056         this.bricks = this.el.select('.masonry-brick', true);
32057         
32058         this.bricks.each(function(b) {
32059             //Roo.log(b.getSize());
32060             if (!b.attr('originalwidth')) {
32061                 b.attr('originalwidth',  b.getSize().width);
32062             }
32063             
32064         });
32065         
32066         Roo.log(this.bricks.elements.length);
32067     },
32068     
32069     resize : function()
32070     {
32071         Roo.log('resize');
32072         var cs = this.el.getBox(true);
32073         
32074         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32075             Roo.log("no change in with or X");
32076             return;
32077         }
32078         this.currentSize = cs;
32079         this.layout();
32080     },
32081     
32082     layout : function()
32083     {
32084          Roo.log('layout');
32085         this._resetLayout();
32086         //this._manageStamps();
32087       
32088         // don't animate first layout
32089         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32090         this.layoutItems( isInstant );
32091       
32092         // flag for initalized
32093         this._isLayoutInited = true;
32094     },
32095     
32096     layoutItems : function( isInstant )
32097     {
32098         //var items = this._getItemsForLayout( this.items );
32099         // original code supports filtering layout items.. we just ignore it..
32100         
32101         this._layoutItems( this.bricks , isInstant );
32102       
32103         this._postLayout();
32104     },
32105     _layoutItems : function ( items , isInstant)
32106     {
32107        //this.fireEvent( 'layout', this, items );
32108     
32109
32110         if ( !items || !items.elements.length ) {
32111           // no items, emit event with empty array
32112             return;
32113         }
32114
32115         var queue = [];
32116         items.each(function(item) {
32117             Roo.log("layout item");
32118             Roo.log(item);
32119             // get x/y object from method
32120             var position = this._getItemLayoutPosition( item );
32121             // enqueue
32122             position.item = item;
32123             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32124             queue.push( position );
32125         }, this);
32126       
32127         this._processLayoutQueue( queue );
32128     },
32129     /** Sets position of item in DOM
32130     * @param {Element} item
32131     * @param {Number} x - horizontal position
32132     * @param {Number} y - vertical position
32133     * @param {Boolean} isInstant - disables transitions
32134     */
32135     _processLayoutQueue : function( queue )
32136     {
32137         for ( var i=0, len = queue.length; i < len; i++ ) {
32138             var obj = queue[i];
32139             obj.item.position('absolute');
32140             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32141         }
32142     },
32143       
32144     
32145     /**
32146     * Any logic you want to do after each layout,
32147     * i.e. size the container
32148     */
32149     _postLayout : function()
32150     {
32151         this.resizeContainer();
32152     },
32153     
32154     resizeContainer : function()
32155     {
32156         if ( !this.isResizingContainer ) {
32157             return;
32158         }
32159         var size = this._getContainerSize();
32160         if ( size ) {
32161             this.el.setSize(size.width,size.height);
32162             this.boxesEl.setSize(size.width,size.height);
32163         }
32164     },
32165     
32166     
32167     
32168     _resetLayout : function()
32169     {
32170         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32171         this.colWidth = this.el.getWidth();
32172         //this.gutter = this.el.getWidth(); 
32173         
32174         this.measureColumns();
32175
32176         // reset column Y
32177         var i = this.cols;
32178         this.colYs = [];
32179         while (i--) {
32180             this.colYs.push( 0 );
32181         }
32182     
32183         this.maxY = 0;
32184     },
32185
32186     measureColumns : function()
32187     {
32188         this.getContainerWidth();
32189       // if columnWidth is 0, default to outerWidth of first item
32190         if ( !this.columnWidth ) {
32191             var firstItem = this.bricks.first();
32192             Roo.log(firstItem);
32193             this.columnWidth  = this.containerWidth;
32194             if (firstItem && firstItem.attr('originalwidth') ) {
32195                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32196             }
32197             // columnWidth fall back to item of first element
32198             Roo.log("set column width?");
32199                         this.initialColumnWidth = this.columnWidth  ;
32200
32201             // if first elem has no width, default to size of container
32202             
32203         }
32204         
32205         
32206         if (this.initialColumnWidth) {
32207             this.columnWidth = this.initialColumnWidth;
32208         }
32209         
32210         
32211             
32212         // column width is fixed at the top - however if container width get's smaller we should
32213         // reduce it...
32214         
32215         // this bit calcs how man columns..
32216             
32217         var columnWidth = this.columnWidth += this.gutter;
32218       
32219         // calculate columns
32220         var containerWidth = this.containerWidth + this.gutter;
32221         
32222         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32223         // fix rounding errors, typically with gutters
32224         var excess = columnWidth - containerWidth % columnWidth;
32225         
32226         
32227         // if overshoot is less than a pixel, round up, otherwise floor it
32228         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32229         cols = Math[ mathMethod ]( cols );
32230         this.cols = Math.max( cols, 1 );
32231         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32232         
32233          // padding positioning..
32234         var totalColWidth = this.cols * this.columnWidth;
32235         var padavail = this.containerWidth - totalColWidth;
32236         // so for 2 columns - we need 3 'pads'
32237         
32238         var padNeeded = (1+this.cols) * this.padWidth;
32239         
32240         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32241         
32242         this.columnWidth += padExtra
32243         //this.padWidth = Math.floor(padavail /  ( this.cols));
32244         
32245         // adjust colum width so that padding is fixed??
32246         
32247         // we have 3 columns ... total = width * 3
32248         // we have X left over... that should be used by 
32249         
32250         //if (this.expandC) {
32251             
32252         //}
32253         
32254         
32255         
32256     },
32257     
32258     getContainerWidth : function()
32259     {
32260        /* // container is parent if fit width
32261         var container = this.isFitWidth ? this.element.parentNode : this.element;
32262         // check that this.size and size are there
32263         // IE8 triggers resize on body size change, so they might not be
32264         
32265         var size = getSize( container );  //FIXME
32266         this.containerWidth = size && size.innerWidth; //FIXME
32267         */
32268          
32269         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32270         
32271     },
32272     
32273     _getItemLayoutPosition : function( item )  // what is item?
32274     {
32275         // we resize the item to our columnWidth..
32276       
32277         item.setWidth(this.columnWidth);
32278         item.autoBoxAdjust  = false;
32279         
32280         var sz = item.getSize();
32281  
32282         // how many columns does this brick span
32283         var remainder = this.containerWidth % this.columnWidth;
32284         
32285         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32286         // round if off by 1 pixel, otherwise use ceil
32287         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32288         colSpan = Math.min( colSpan, this.cols );
32289         
32290         // normally this should be '1' as we dont' currently allow multi width columns..
32291         
32292         var colGroup = this._getColGroup( colSpan );
32293         // get the minimum Y value from the columns
32294         var minimumY = Math.min.apply( Math, colGroup );
32295         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32296         
32297         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32298          
32299         // position the brick
32300         var position = {
32301             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32302             y: this.currentSize.y + minimumY + this.padHeight
32303         };
32304         
32305         Roo.log(position);
32306         // apply setHeight to necessary columns
32307         var setHeight = minimumY + sz.height + this.padHeight;
32308         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32309         
32310         var setSpan = this.cols + 1 - colGroup.length;
32311         for ( var i = 0; i < setSpan; i++ ) {
32312           this.colYs[ shortColIndex + i ] = setHeight ;
32313         }
32314       
32315         return position;
32316     },
32317     
32318     /**
32319      * @param {Number} colSpan - number of columns the element spans
32320      * @returns {Array} colGroup
32321      */
32322     _getColGroup : function( colSpan )
32323     {
32324         if ( colSpan < 2 ) {
32325           // if brick spans only one column, use all the column Ys
32326           return this.colYs;
32327         }
32328       
32329         var colGroup = [];
32330         // how many different places could this brick fit horizontally
32331         var groupCount = this.cols + 1 - colSpan;
32332         // for each group potential horizontal position
32333         for ( var i = 0; i < groupCount; i++ ) {
32334           // make an array of colY values for that one group
32335           var groupColYs = this.colYs.slice( i, i + colSpan );
32336           // and get the max value of the array
32337           colGroup[i] = Math.max.apply( Math, groupColYs );
32338         }
32339         return colGroup;
32340     },
32341     /*
32342     _manageStamp : function( stamp )
32343     {
32344         var stampSize =  stamp.getSize();
32345         var offset = stamp.getBox();
32346         // get the columns that this stamp affects
32347         var firstX = this.isOriginLeft ? offset.x : offset.right;
32348         var lastX = firstX + stampSize.width;
32349         var firstCol = Math.floor( firstX / this.columnWidth );
32350         firstCol = Math.max( 0, firstCol );
32351         
32352         var lastCol = Math.floor( lastX / this.columnWidth );
32353         // lastCol should not go over if multiple of columnWidth #425
32354         lastCol -= lastX % this.columnWidth ? 0 : 1;
32355         lastCol = Math.min( this.cols - 1, lastCol );
32356         
32357         // set colYs to bottom of the stamp
32358         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32359             stampSize.height;
32360             
32361         for ( var i = firstCol; i <= lastCol; i++ ) {
32362           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32363         }
32364     },
32365     */
32366     
32367     _getContainerSize : function()
32368     {
32369         this.maxY = Math.max.apply( Math, this.colYs );
32370         var size = {
32371             height: this.maxY
32372         };
32373       
32374         if ( this.isFitWidth ) {
32375             size.width = this._getContainerFitWidth();
32376         }
32377       
32378         return size;
32379     },
32380     
32381     _getContainerFitWidth : function()
32382     {
32383         var unusedCols = 0;
32384         // count unused columns
32385         var i = this.cols;
32386         while ( --i ) {
32387           if ( this.colYs[i] !== 0 ) {
32388             break;
32389           }
32390           unusedCols++;
32391         }
32392         // fit container to columns that have been used
32393         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32394     },
32395     
32396     needsResizeLayout : function()
32397     {
32398         var previousWidth = this.containerWidth;
32399         this.getContainerWidth();
32400         return previousWidth !== this.containerWidth;
32401     }
32402  
32403 });
32404
32405  
32406
32407  /*
32408  * - LGPL
32409  *
32410  * element
32411  * 
32412  */
32413
32414 /**
32415  * @class Roo.bootstrap.MasonryBrick
32416  * @extends Roo.bootstrap.Component
32417  * Bootstrap MasonryBrick class
32418  * 
32419  * @constructor
32420  * Create a new MasonryBrick
32421  * @param {Object} config The config object
32422  */
32423
32424 Roo.bootstrap.MasonryBrick = function(config){
32425     
32426     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32427     
32428     Roo.bootstrap.MasonryBrick.register(this);
32429     
32430     this.addEvents({
32431         // raw events
32432         /**
32433          * @event click
32434          * When a MasonryBrick is clcik
32435          * @param {Roo.bootstrap.MasonryBrick} this
32436          * @param {Roo.EventObject} e
32437          */
32438         "click" : true
32439     });
32440 };
32441
32442 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32443     
32444     /**
32445      * @cfg {String} title
32446      */   
32447     title : '',
32448     /**
32449      * @cfg {String} html
32450      */   
32451     html : '',
32452     /**
32453      * @cfg {String} bgimage
32454      */   
32455     bgimage : '',
32456     /**
32457      * @cfg {String} videourl
32458      */   
32459     videourl : '',
32460     /**
32461      * @cfg {String} cls
32462      */   
32463     cls : '',
32464     /**
32465      * @cfg {String} href
32466      */   
32467     href : '',
32468     /**
32469      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32470      */   
32471     size : 'xs',
32472     
32473     /**
32474      * @cfg {String} placetitle (center|bottom)
32475      */   
32476     placetitle : '',
32477     
32478     /**
32479      * @cfg {Boolean} isFitContainer defalut true
32480      */   
32481     isFitContainer : true, 
32482     
32483     /**
32484      * @cfg {Boolean} preventDefault defalut false
32485      */   
32486     preventDefault : false, 
32487     
32488     /**
32489      * @cfg {Boolean} inverse defalut false
32490      */   
32491     maskInverse : false, 
32492     
32493     getAutoCreate : function()
32494     {
32495         if(!this.isFitContainer){
32496             return this.getSplitAutoCreate();
32497         }
32498         
32499         var cls = 'masonry-brick masonry-brick-full';
32500         
32501         if(this.href.length){
32502             cls += ' masonry-brick-link';
32503         }
32504         
32505         if(this.bgimage.length){
32506             cls += ' masonry-brick-image';
32507         }
32508         
32509         if(this.maskInverse){
32510             cls += ' mask-inverse';
32511         }
32512         
32513         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32514             cls += ' enable-mask';
32515         }
32516         
32517         if(this.size){
32518             cls += ' masonry-' + this.size + '-brick';
32519         }
32520         
32521         if(this.placetitle.length){
32522             
32523             switch (this.placetitle) {
32524                 case 'center' :
32525                     cls += ' masonry-center-title';
32526                     break;
32527                 case 'bottom' :
32528                     cls += ' masonry-bottom-title';
32529                     break;
32530                 default:
32531                     break;
32532             }
32533             
32534         } else {
32535             if(!this.html.length && !this.bgimage.length){
32536                 cls += ' masonry-center-title';
32537             }
32538
32539             if(!this.html.length && this.bgimage.length){
32540                 cls += ' masonry-bottom-title';
32541             }
32542         }
32543         
32544         if(this.cls){
32545             cls += ' ' + this.cls;
32546         }
32547         
32548         var cfg = {
32549             tag: (this.href.length) ? 'a' : 'div',
32550             cls: cls,
32551             cn: [
32552                 {
32553                     tag: 'div',
32554                     cls: 'masonry-brick-mask'
32555                 },
32556                 {
32557                     tag: 'div',
32558                     cls: 'masonry-brick-paragraph',
32559                     cn: []
32560                 }
32561             ]
32562         };
32563         
32564         if(this.href.length){
32565             cfg.href = this.href;
32566         }
32567         
32568         var cn = cfg.cn[1].cn;
32569         
32570         if(this.title.length){
32571             cn.push({
32572                 tag: 'h4',
32573                 cls: 'masonry-brick-title',
32574                 html: this.title
32575             });
32576         }
32577         
32578         if(this.html.length){
32579             cn.push({
32580                 tag: 'p',
32581                 cls: 'masonry-brick-text',
32582                 html: this.html
32583             });
32584         }
32585         
32586         if (!this.title.length && !this.html.length) {
32587             cfg.cn[1].cls += ' hide';
32588         }
32589         
32590         if(this.bgimage.length){
32591             cfg.cn.push({
32592                 tag: 'img',
32593                 cls: 'masonry-brick-image-view',
32594                 src: this.bgimage
32595             });
32596         }
32597         
32598         if(this.videourl.length){
32599             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32600             // youtube support only?
32601             cfg.cn.push({
32602                 tag: 'iframe',
32603                 cls: 'masonry-brick-image-view',
32604                 src: vurl,
32605                 frameborder : 0,
32606                 allowfullscreen : true
32607             });
32608         }
32609         
32610         return cfg;
32611         
32612     },
32613     
32614     getSplitAutoCreate : function()
32615     {
32616         var cls = 'masonry-brick masonry-brick-split';
32617         
32618         if(this.href.length){
32619             cls += ' masonry-brick-link';
32620         }
32621         
32622         if(this.bgimage.length){
32623             cls += ' masonry-brick-image';
32624         }
32625         
32626         if(this.size){
32627             cls += ' masonry-' + this.size + '-brick';
32628         }
32629         
32630         switch (this.placetitle) {
32631             case 'center' :
32632                 cls += ' masonry-center-title';
32633                 break;
32634             case 'bottom' :
32635                 cls += ' masonry-bottom-title';
32636                 break;
32637             default:
32638                 if(!this.bgimage.length){
32639                     cls += ' masonry-center-title';
32640                 }
32641
32642                 if(this.bgimage.length){
32643                     cls += ' masonry-bottom-title';
32644                 }
32645                 break;
32646         }
32647         
32648         if(this.cls){
32649             cls += ' ' + this.cls;
32650         }
32651         
32652         var cfg = {
32653             tag: (this.href.length) ? 'a' : 'div',
32654             cls: cls,
32655             cn: [
32656                 {
32657                     tag: 'div',
32658                     cls: 'masonry-brick-split-head',
32659                     cn: [
32660                         {
32661                             tag: 'div',
32662                             cls: 'masonry-brick-paragraph',
32663                             cn: []
32664                         }
32665                     ]
32666                 },
32667                 {
32668                     tag: 'div',
32669                     cls: 'masonry-brick-split-body',
32670                     cn: []
32671                 }
32672             ]
32673         };
32674         
32675         if(this.href.length){
32676             cfg.href = this.href;
32677         }
32678         
32679         if(this.title.length){
32680             cfg.cn[0].cn[0].cn.push({
32681                 tag: 'h4',
32682                 cls: 'masonry-brick-title',
32683                 html: this.title
32684             });
32685         }
32686         
32687         if(this.html.length){
32688             cfg.cn[1].cn.push({
32689                 tag: 'p',
32690                 cls: 'masonry-brick-text',
32691                 html: this.html
32692             });
32693         }
32694
32695         if(this.bgimage.length){
32696             cfg.cn[0].cn.push({
32697                 tag: 'img',
32698                 cls: 'masonry-brick-image-view',
32699                 src: this.bgimage
32700             });
32701         }
32702         
32703         if(this.videourl.length){
32704             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32705             // youtube support only?
32706             cfg.cn[0].cn.cn.push({
32707                 tag: 'iframe',
32708                 cls: 'masonry-brick-image-view',
32709                 src: vurl,
32710                 frameborder : 0,
32711                 allowfullscreen : true
32712             });
32713         }
32714         
32715         return cfg;
32716     },
32717     
32718     initEvents: function() 
32719     {
32720         switch (this.size) {
32721             case 'xs' :
32722                 this.x = 1;
32723                 this.y = 1;
32724                 break;
32725             case 'sm' :
32726                 this.x = 2;
32727                 this.y = 2;
32728                 break;
32729             case 'md' :
32730             case 'md-left' :
32731             case 'md-right' :
32732                 this.x = 3;
32733                 this.y = 3;
32734                 break;
32735             case 'tall' :
32736                 this.x = 2;
32737                 this.y = 3;
32738                 break;
32739             case 'wide' :
32740                 this.x = 3;
32741                 this.y = 2;
32742                 break;
32743             case 'wide-thin' :
32744                 this.x = 3;
32745                 this.y = 1;
32746                 break;
32747                         
32748             default :
32749                 break;
32750         }
32751         
32752         if(Roo.isTouch){
32753             this.el.on('touchstart', this.onTouchStart, this);
32754             this.el.on('touchmove', this.onTouchMove, this);
32755             this.el.on('touchend', this.onTouchEnd, this);
32756             this.el.on('contextmenu', this.onContextMenu, this);
32757         } else {
32758             this.el.on('mouseenter'  ,this.enter, this);
32759             this.el.on('mouseleave', this.leave, this);
32760             this.el.on('click', this.onClick, this);
32761         }
32762         
32763         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32764             this.parent().bricks.push(this);   
32765         }
32766         
32767     },
32768     
32769     onClick: function(e, el)
32770     {
32771         var time = this.endTimer - this.startTimer;
32772         // Roo.log(e.preventDefault());
32773         if(Roo.isTouch){
32774             if(time > 1000){
32775                 e.preventDefault();
32776                 return;
32777             }
32778         }
32779         
32780         if(!this.preventDefault){
32781             return;
32782         }
32783         
32784         e.preventDefault();
32785         
32786         if (this.activeClass != '') {
32787             this.selectBrick();
32788         }
32789         
32790         this.fireEvent('click', this, e);
32791     },
32792     
32793     enter: function(e, el)
32794     {
32795         e.preventDefault();
32796         
32797         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32798             return;
32799         }
32800         
32801         if(this.bgimage.length && this.html.length){
32802             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32803         }
32804     },
32805     
32806     leave: function(e, el)
32807     {
32808         e.preventDefault();
32809         
32810         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32811             return;
32812         }
32813         
32814         if(this.bgimage.length && this.html.length){
32815             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32816         }
32817     },
32818     
32819     onTouchStart: function(e, el)
32820     {
32821 //        e.preventDefault();
32822         
32823         this.touchmoved = false;
32824         
32825         if(!this.isFitContainer){
32826             return;
32827         }
32828         
32829         if(!this.bgimage.length || !this.html.length){
32830             return;
32831         }
32832         
32833         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32834         
32835         this.timer = new Date().getTime();
32836         
32837     },
32838     
32839     onTouchMove: function(e, el)
32840     {
32841         this.touchmoved = true;
32842     },
32843     
32844     onContextMenu : function(e,el)
32845     {
32846         e.preventDefault();
32847         e.stopPropagation();
32848         return false;
32849     },
32850     
32851     onTouchEnd: function(e, el)
32852     {
32853 //        e.preventDefault();
32854         
32855         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32856         
32857             this.leave(e,el);
32858             
32859             return;
32860         }
32861         
32862         if(!this.bgimage.length || !this.html.length){
32863             
32864             if(this.href.length){
32865                 window.location.href = this.href;
32866             }
32867             
32868             return;
32869         }
32870         
32871         if(!this.isFitContainer){
32872             return;
32873         }
32874         
32875         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32876         
32877         window.location.href = this.href;
32878     },
32879     
32880     //selection on single brick only
32881     selectBrick : function() {
32882         
32883         if (!this.parentId) {
32884             return;
32885         }
32886         
32887         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32888         var index = m.selectedBrick.indexOf(this.id);
32889         
32890         if ( index > -1) {
32891             m.selectedBrick.splice(index,1);
32892             this.el.removeClass(this.activeClass);
32893             return;
32894         }
32895         
32896         for(var i = 0; i < m.selectedBrick.length; i++) {
32897             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32898             b.el.removeClass(b.activeClass);
32899         }
32900         
32901         m.selectedBrick = [];
32902         
32903         m.selectedBrick.push(this.id);
32904         this.el.addClass(this.activeClass);
32905         return;
32906     },
32907     
32908     isSelected : function(){
32909         return this.el.hasClass(this.activeClass);
32910         
32911     }
32912 });
32913
32914 Roo.apply(Roo.bootstrap.MasonryBrick, {
32915     
32916     //groups: {},
32917     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32918      /**
32919     * register a Masonry Brick
32920     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32921     */
32922     
32923     register : function(brick)
32924     {
32925         //this.groups[brick.id] = brick;
32926         this.groups.add(brick.id, brick);
32927     },
32928     /**
32929     * fetch a  masonry brick based on the masonry brick ID
32930     * @param {string} the masonry brick to add
32931     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32932     */
32933     
32934     get: function(brick_id) 
32935     {
32936         // if (typeof(this.groups[brick_id]) == 'undefined') {
32937         //     return false;
32938         // }
32939         // return this.groups[brick_id] ;
32940         
32941         if(this.groups.key(brick_id)) {
32942             return this.groups.key(brick_id);
32943         }
32944         
32945         return false;
32946     }
32947     
32948     
32949     
32950 });
32951
32952  /*
32953  * - LGPL
32954  *
32955  * element
32956  * 
32957  */
32958
32959 /**
32960  * @class Roo.bootstrap.Brick
32961  * @extends Roo.bootstrap.Component
32962  * Bootstrap Brick class
32963  * 
32964  * @constructor
32965  * Create a new Brick
32966  * @param {Object} config The config object
32967  */
32968
32969 Roo.bootstrap.Brick = function(config){
32970     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32971     
32972     this.addEvents({
32973         // raw events
32974         /**
32975          * @event click
32976          * When a Brick is click
32977          * @param {Roo.bootstrap.Brick} this
32978          * @param {Roo.EventObject} e
32979          */
32980         "click" : true
32981     });
32982 };
32983
32984 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32985     
32986     /**
32987      * @cfg {String} title
32988      */   
32989     title : '',
32990     /**
32991      * @cfg {String} html
32992      */   
32993     html : '',
32994     /**
32995      * @cfg {String} bgimage
32996      */   
32997     bgimage : '',
32998     /**
32999      * @cfg {String} cls
33000      */   
33001     cls : '',
33002     /**
33003      * @cfg {String} href
33004      */   
33005     href : '',
33006     /**
33007      * @cfg {String} video
33008      */   
33009     video : '',
33010     /**
33011      * @cfg {Boolean} square
33012      */   
33013     square : true,
33014     
33015     getAutoCreate : function()
33016     {
33017         var cls = 'roo-brick';
33018         
33019         if(this.href.length){
33020             cls += ' roo-brick-link';
33021         }
33022         
33023         if(this.bgimage.length){
33024             cls += ' roo-brick-image';
33025         }
33026         
33027         if(!this.html.length && !this.bgimage.length){
33028             cls += ' roo-brick-center-title';
33029         }
33030         
33031         if(!this.html.length && this.bgimage.length){
33032             cls += ' roo-brick-bottom-title';
33033         }
33034         
33035         if(this.cls){
33036             cls += ' ' + this.cls;
33037         }
33038         
33039         var cfg = {
33040             tag: (this.href.length) ? 'a' : 'div',
33041             cls: cls,
33042             cn: [
33043                 {
33044                     tag: 'div',
33045                     cls: 'roo-brick-paragraph',
33046                     cn: []
33047                 }
33048             ]
33049         };
33050         
33051         if(this.href.length){
33052             cfg.href = this.href;
33053         }
33054         
33055         var cn = cfg.cn[0].cn;
33056         
33057         if(this.title.length){
33058             cn.push({
33059                 tag: 'h4',
33060                 cls: 'roo-brick-title',
33061                 html: this.title
33062             });
33063         }
33064         
33065         if(this.html.length){
33066             cn.push({
33067                 tag: 'p',
33068                 cls: 'roo-brick-text',
33069                 html: this.html
33070             });
33071         } else {
33072             cn.cls += ' hide';
33073         }
33074         
33075         if(this.bgimage.length){
33076             cfg.cn.push({
33077                 tag: 'img',
33078                 cls: 'roo-brick-image-view',
33079                 src: this.bgimage
33080             });
33081         }
33082         
33083         return cfg;
33084     },
33085     
33086     initEvents: function() 
33087     {
33088         if(this.title.length || this.html.length){
33089             this.el.on('mouseenter'  ,this.enter, this);
33090             this.el.on('mouseleave', this.leave, this);
33091         }
33092         
33093         Roo.EventManager.onWindowResize(this.resize, this); 
33094         
33095         if(this.bgimage.length){
33096             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33097             this.imageEl.on('load', this.onImageLoad, this);
33098             return;
33099         }
33100         
33101         this.resize();
33102     },
33103     
33104     onImageLoad : function()
33105     {
33106         this.resize();
33107     },
33108     
33109     resize : function()
33110     {
33111         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33112         
33113         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33114         
33115         if(this.bgimage.length){
33116             var image = this.el.select('.roo-brick-image-view', true).first();
33117             
33118             image.setWidth(paragraph.getWidth());
33119             
33120             if(this.square){
33121                 image.setHeight(paragraph.getWidth());
33122             }
33123             
33124             this.el.setHeight(image.getHeight());
33125             paragraph.setHeight(image.getHeight());
33126             
33127         }
33128         
33129     },
33130     
33131     enter: function(e, el)
33132     {
33133         e.preventDefault();
33134         
33135         if(this.bgimage.length){
33136             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33137             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33138         }
33139     },
33140     
33141     leave: function(e, el)
33142     {
33143         e.preventDefault();
33144         
33145         if(this.bgimage.length){
33146             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33147             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33148         }
33149     }
33150     
33151 });
33152
33153  
33154
33155  /*
33156  * - LGPL
33157  *
33158  * Number field 
33159  */
33160
33161 /**
33162  * @class Roo.bootstrap.NumberField
33163  * @extends Roo.bootstrap.Input
33164  * Bootstrap NumberField class
33165  * 
33166  * 
33167  * 
33168  * 
33169  * @constructor
33170  * Create a new NumberField
33171  * @param {Object} config The config object
33172  */
33173
33174 Roo.bootstrap.NumberField = function(config){
33175     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33176 };
33177
33178 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33179     
33180     /**
33181      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33182      */
33183     allowDecimals : true,
33184     /**
33185      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33186      */
33187     decimalSeparator : ".",
33188     /**
33189      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33190      */
33191     decimalPrecision : 2,
33192     /**
33193      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33194      */
33195     allowNegative : true,
33196     
33197     /**
33198      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33199      */
33200     allowZero: true,
33201     /**
33202      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33203      */
33204     minValue : Number.NEGATIVE_INFINITY,
33205     /**
33206      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33207      */
33208     maxValue : Number.MAX_VALUE,
33209     /**
33210      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33211      */
33212     minText : "The minimum value for this field is {0}",
33213     /**
33214      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33215      */
33216     maxText : "The maximum value for this field is {0}",
33217     /**
33218      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33219      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33220      */
33221     nanText : "{0} is not a valid number",
33222     /**
33223      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33224      */
33225     castInt : true,
33226     /**
33227      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33228      */
33229     thousandsDelimiter : false,
33230     /**
33231      * @cfg {String} valueAlign alignment of value
33232      */
33233     valueAlign : "left",
33234
33235     getAutoCreate : function()
33236     {
33237         var hiddenInput = {
33238             tag: 'input',
33239             type: 'hidden',
33240             id: Roo.id(),
33241             cls: 'hidden-number-input'
33242         };
33243         
33244         if (this.name) {
33245             hiddenInput.name = this.name;
33246         }
33247         
33248         this.name = '';
33249         
33250         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33251         
33252         this.name = hiddenInput.name;
33253         
33254         if(cfg.cn.length > 0) {
33255             cfg.cn.push(hiddenInput);
33256         }
33257         
33258         return cfg;
33259     },
33260
33261     // private
33262     initEvents : function()
33263     {   
33264         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33265         
33266         var allowed = "0123456789";
33267         
33268         if(this.allowDecimals){
33269             allowed += this.decimalSeparator;
33270         }
33271         
33272         if(this.allowNegative){
33273             allowed += "-";
33274         }
33275         
33276         if(this.thousandsDelimiter) {
33277             allowed += ",";
33278         }
33279         
33280         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33281         
33282         var keyPress = function(e){
33283             
33284             var k = e.getKey();
33285             
33286             var c = e.getCharCode();
33287             
33288             if(
33289                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33290                     allowed.indexOf(String.fromCharCode(c)) === -1
33291             ){
33292                 e.stopEvent();
33293                 return;
33294             }
33295             
33296             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33297                 return;
33298             }
33299             
33300             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33301                 e.stopEvent();
33302             }
33303         };
33304         
33305         this.el.on("keypress", keyPress, this);
33306     },
33307     
33308     validateValue : function(value)
33309     {
33310         
33311         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33312             return false;
33313         }
33314         
33315         var num = this.parseValue(value);
33316         
33317         if(isNaN(num)){
33318             this.markInvalid(String.format(this.nanText, value));
33319             return false;
33320         }
33321         
33322         if(num < this.minValue){
33323             this.markInvalid(String.format(this.minText, this.minValue));
33324             return false;
33325         }
33326         
33327         if(num > this.maxValue){
33328             this.markInvalid(String.format(this.maxText, this.maxValue));
33329             return false;
33330         }
33331         
33332         return true;
33333     },
33334
33335     getValue : function()
33336     {
33337         var v = this.hiddenEl().getValue();
33338         
33339         return this.fixPrecision(this.parseValue(v));
33340     },
33341
33342     parseValue : function(value)
33343     {
33344         if(this.thousandsDelimiter) {
33345             value += "";
33346             r = new RegExp(",", "g");
33347             value = value.replace(r, "");
33348         }
33349         
33350         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33351         return isNaN(value) ? '' : value;
33352     },
33353
33354     fixPrecision : function(value)
33355     {
33356         if(this.thousandsDelimiter) {
33357             value += "";
33358             r = new RegExp(",", "g");
33359             value = value.replace(r, "");
33360         }
33361         
33362         var nan = isNaN(value);
33363         
33364         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33365             return nan ? '' : value;
33366         }
33367         return parseFloat(value).toFixed(this.decimalPrecision);
33368     },
33369
33370     setValue : function(v)
33371     {
33372         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33373         
33374         this.value = v;
33375         
33376         if(this.rendered){
33377             
33378             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33379             
33380             this.inputEl().dom.value = (v == '') ? '' :
33381                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33382             
33383             if(!this.allowZero && v === '0') {
33384                 this.hiddenEl().dom.value = '';
33385                 this.inputEl().dom.value = '';
33386             }
33387             
33388             this.validate();
33389         }
33390     },
33391
33392     decimalPrecisionFcn : function(v)
33393     {
33394         return Math.floor(v);
33395     },
33396
33397     beforeBlur : function()
33398     {
33399         if(!this.castInt){
33400             return;
33401         }
33402         
33403         var v = this.parseValue(this.getRawValue());
33404         
33405         if(v || v === 0){
33406             this.setValue(v);
33407         }
33408     },
33409     
33410     hiddenEl : function()
33411     {
33412         return this.el.select('input.hidden-number-input',true).first();
33413     }
33414     
33415 });
33416
33417  
33418
33419 /*
33420 * Licence: LGPL
33421 */
33422
33423 /**
33424  * @class Roo.bootstrap.DocumentSlider
33425  * @extends Roo.bootstrap.Component
33426  * Bootstrap DocumentSlider class
33427  * 
33428  * @constructor
33429  * Create a new DocumentViewer
33430  * @param {Object} config The config object
33431  */
33432
33433 Roo.bootstrap.DocumentSlider = function(config){
33434     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33435     
33436     this.files = [];
33437     
33438     this.addEvents({
33439         /**
33440          * @event initial
33441          * Fire after initEvent
33442          * @param {Roo.bootstrap.DocumentSlider} this
33443          */
33444         "initial" : true,
33445         /**
33446          * @event update
33447          * Fire after update
33448          * @param {Roo.bootstrap.DocumentSlider} this
33449          */
33450         "update" : true,
33451         /**
33452          * @event click
33453          * Fire after click
33454          * @param {Roo.bootstrap.DocumentSlider} this
33455          */
33456         "click" : true
33457     });
33458 };
33459
33460 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33461     
33462     files : false,
33463     
33464     indicator : 0,
33465     
33466     getAutoCreate : function()
33467     {
33468         var cfg = {
33469             tag : 'div',
33470             cls : 'roo-document-slider',
33471             cn : [
33472                 {
33473                     tag : 'div',
33474                     cls : 'roo-document-slider-header',
33475                     cn : [
33476                         {
33477                             tag : 'div',
33478                             cls : 'roo-document-slider-header-title'
33479                         }
33480                     ]
33481                 },
33482                 {
33483                     tag : 'div',
33484                     cls : 'roo-document-slider-body',
33485                     cn : [
33486                         {
33487                             tag : 'div',
33488                             cls : 'roo-document-slider-prev',
33489                             cn : [
33490                                 {
33491                                     tag : 'i',
33492                                     cls : 'fa fa-chevron-left'
33493                                 }
33494                             ]
33495                         },
33496                         {
33497                             tag : 'div',
33498                             cls : 'roo-document-slider-thumb',
33499                             cn : [
33500                                 {
33501                                     tag : 'img',
33502                                     cls : 'roo-document-slider-image'
33503                                 }
33504                             ]
33505                         },
33506                         {
33507                             tag : 'div',
33508                             cls : 'roo-document-slider-next',
33509                             cn : [
33510                                 {
33511                                     tag : 'i',
33512                                     cls : 'fa fa-chevron-right'
33513                                 }
33514                             ]
33515                         }
33516                     ]
33517                 }
33518             ]
33519         };
33520         
33521         return cfg;
33522     },
33523     
33524     initEvents : function()
33525     {
33526         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33527         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33528         
33529         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33530         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33531         
33532         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33533         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33534         
33535         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33536         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33537         
33538         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33539         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33540         
33541         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33542         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33543         
33544         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33545         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33546         
33547         this.thumbEl.on('click', this.onClick, this);
33548         
33549         this.prevIndicator.on('click', this.prev, this);
33550         
33551         this.nextIndicator.on('click', this.next, this);
33552         
33553     },
33554     
33555     initial : function()
33556     {
33557         if(this.files.length){
33558             this.indicator = 1;
33559             this.update()
33560         }
33561         
33562         this.fireEvent('initial', this);
33563     },
33564     
33565     update : function()
33566     {
33567         this.imageEl.attr('src', this.files[this.indicator - 1]);
33568         
33569         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33570         
33571         this.prevIndicator.show();
33572         
33573         if(this.indicator == 1){
33574             this.prevIndicator.hide();
33575         }
33576         
33577         this.nextIndicator.show();
33578         
33579         if(this.indicator == this.files.length){
33580             this.nextIndicator.hide();
33581         }
33582         
33583         this.thumbEl.scrollTo('top');
33584         
33585         this.fireEvent('update', this);
33586     },
33587     
33588     onClick : function(e)
33589     {
33590         e.preventDefault();
33591         
33592         this.fireEvent('click', this);
33593     },
33594     
33595     prev : function(e)
33596     {
33597         e.preventDefault();
33598         
33599         this.indicator = Math.max(1, this.indicator - 1);
33600         
33601         this.update();
33602     },
33603     
33604     next : function(e)
33605     {
33606         e.preventDefault();
33607         
33608         this.indicator = Math.min(this.files.length, this.indicator + 1);
33609         
33610         this.update();
33611     }
33612 });
33613 /*
33614  * - LGPL
33615  *
33616  * RadioSet
33617  *
33618  *
33619  */
33620
33621 /**
33622  * @class Roo.bootstrap.RadioSet
33623  * @extends Roo.bootstrap.Input
33624  * Bootstrap RadioSet class
33625  * @cfg {String} indicatorpos (left|right) default left
33626  * @cfg {Boolean} inline (true|false) inline the element (default true)
33627  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33628  * @constructor
33629  * Create a new RadioSet
33630  * @param {Object} config The config object
33631  */
33632
33633 Roo.bootstrap.RadioSet = function(config){
33634     
33635     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33636     
33637     this.radioes = [];
33638     
33639     Roo.bootstrap.RadioSet.register(this);
33640     
33641     this.addEvents({
33642         /**
33643         * @event check
33644         * Fires when the element is checked or unchecked.
33645         * @param {Roo.bootstrap.RadioSet} this This radio
33646         * @param {Roo.bootstrap.Radio} item The checked item
33647         */
33648        check : true,
33649        /**
33650         * @event click
33651         * Fires when the element is click.
33652         * @param {Roo.bootstrap.RadioSet} this This radio set
33653         * @param {Roo.bootstrap.Radio} item The checked item
33654         * @param {Roo.EventObject} e The event object
33655         */
33656        click : true
33657     });
33658     
33659 };
33660
33661 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33662
33663     radioes : false,
33664     
33665     inline : true,
33666     
33667     weight : '',
33668     
33669     indicatorpos : 'left',
33670     
33671     getAutoCreate : function()
33672     {
33673         var label = {
33674             tag : 'label',
33675             cls : 'roo-radio-set-label',
33676             cn : [
33677                 {
33678                     tag : 'span',
33679                     html : this.fieldLabel
33680                 }
33681             ]
33682         };
33683         
33684         if(this.indicatorpos == 'left'){
33685             label.cn.unshift({
33686                 tag : 'i',
33687                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33688                 tooltip : 'This field is required'
33689             });
33690         } else {
33691             label.cn.push({
33692                 tag : 'i',
33693                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33694                 tooltip : 'This field is required'
33695             });
33696         }
33697         
33698         var items = {
33699             tag : 'div',
33700             cls : 'roo-radio-set-items'
33701         };
33702         
33703         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33704         
33705         if (align === 'left' && this.fieldLabel.length) {
33706             
33707             items = {
33708                 cls : "roo-radio-set-right", 
33709                 cn: [
33710                     items
33711                 ]
33712             };
33713             
33714             if(this.labelWidth > 12){
33715                 label.style = "width: " + this.labelWidth + 'px';
33716             }
33717             
33718             if(this.labelWidth < 13 && this.labelmd == 0){
33719                 this.labelmd = this.labelWidth;
33720             }
33721             
33722             if(this.labellg > 0){
33723                 label.cls += ' col-lg-' + this.labellg;
33724                 items.cls += ' col-lg-' + (12 - this.labellg);
33725             }
33726             
33727             if(this.labelmd > 0){
33728                 label.cls += ' col-md-' + this.labelmd;
33729                 items.cls += ' col-md-' + (12 - this.labelmd);
33730             }
33731             
33732             if(this.labelsm > 0){
33733                 label.cls += ' col-sm-' + this.labelsm;
33734                 items.cls += ' col-sm-' + (12 - this.labelsm);
33735             }
33736             
33737             if(this.labelxs > 0){
33738                 label.cls += ' col-xs-' + this.labelxs;
33739                 items.cls += ' col-xs-' + (12 - this.labelxs);
33740             }
33741         }
33742         
33743         var cfg = {
33744             tag : 'div',
33745             cls : 'roo-radio-set',
33746             cn : [
33747                 {
33748                     tag : 'input',
33749                     cls : 'roo-radio-set-input',
33750                     type : 'hidden',
33751                     name : this.name,
33752                     value : this.value ? this.value :  ''
33753                 },
33754                 label,
33755                 items
33756             ]
33757         };
33758         
33759         if(this.weight.length){
33760             cfg.cls += ' roo-radio-' + this.weight;
33761         }
33762         
33763         if(this.inline) {
33764             cfg.cls += ' roo-radio-set-inline';
33765         }
33766         
33767         var settings=this;
33768         ['xs','sm','md','lg'].map(function(size){
33769             if (settings[size]) {
33770                 cfg.cls += ' col-' + size + '-' + settings[size];
33771             }
33772         });
33773         
33774         return cfg;
33775         
33776     },
33777
33778     initEvents : function()
33779     {
33780         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33781         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33782         
33783         if(!this.fieldLabel.length){
33784             this.labelEl.hide();
33785         }
33786         
33787         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33788         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33789         
33790         this.indicator = this.indicatorEl();
33791         
33792         if(this.indicator){
33793             this.indicator.addClass('invisible');
33794         }
33795         
33796         this.originalValue = this.getValue();
33797         
33798     },
33799     
33800     inputEl: function ()
33801     {
33802         return this.el.select('.roo-radio-set-input', true).first();
33803     },
33804     
33805     getChildContainer : function()
33806     {
33807         return this.itemsEl;
33808     },
33809     
33810     register : function(item)
33811     {
33812         this.radioes.push(item);
33813         
33814     },
33815     
33816     validate : function()
33817     {   
33818         if(this.getVisibilityEl().hasClass('hidden')){
33819             return true;
33820         }
33821         
33822         var valid = false;
33823         
33824         Roo.each(this.radioes, function(i){
33825             if(!i.checked){
33826                 return;
33827             }
33828             
33829             valid = true;
33830             return false;
33831         });
33832         
33833         if(this.allowBlank) {
33834             return true;
33835         }
33836         
33837         if(this.disabled || valid){
33838             this.markValid();
33839             return true;
33840         }
33841         
33842         this.markInvalid();
33843         return false;
33844         
33845     },
33846     
33847     markValid : function()
33848     {
33849         if(this.labelEl.isVisible(true)){
33850             this.indicatorEl().removeClass('visible');
33851             this.indicatorEl().addClass('invisible');
33852         }
33853         
33854         this.el.removeClass([this.invalidClass, this.validClass]);
33855         this.el.addClass(this.validClass);
33856         
33857         this.fireEvent('valid', this);
33858     },
33859     
33860     markInvalid : function(msg)
33861     {
33862         if(this.allowBlank || this.disabled){
33863             return;
33864         }
33865         
33866         if(this.labelEl.isVisible(true)){
33867             this.indicatorEl().removeClass('invisible');
33868             this.indicatorEl().addClass('visible');
33869         }
33870         
33871         this.el.removeClass([this.invalidClass, this.validClass]);
33872         this.el.addClass(this.invalidClass);
33873         
33874         this.fireEvent('invalid', this, msg);
33875         
33876     },
33877     
33878     setValue : function(v, suppressEvent)
33879     {   
33880         if(this.value === v){
33881             return;
33882         }
33883         
33884         this.value = v;
33885         
33886         if(this.rendered){
33887             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33888         }
33889         
33890         Roo.each(this.radioes, function(i){
33891             i.checked = false;
33892             i.el.removeClass('checked');
33893         });
33894         
33895         Roo.each(this.radioes, function(i){
33896             
33897             if(i.value === v || i.value.toString() === v.toString()){
33898                 i.checked = true;
33899                 i.el.addClass('checked');
33900                 
33901                 if(suppressEvent !== true){
33902                     this.fireEvent('check', this, i);
33903                 }
33904                 
33905                 return false;
33906             }
33907             
33908         }, this);
33909         
33910         this.validate();
33911     },
33912     
33913     clearInvalid : function(){
33914         
33915         if(!this.el || this.preventMark){
33916             return;
33917         }
33918         
33919         this.el.removeClass([this.invalidClass]);
33920         
33921         this.fireEvent('valid', this);
33922     }
33923     
33924 });
33925
33926 Roo.apply(Roo.bootstrap.RadioSet, {
33927     
33928     groups: {},
33929     
33930     register : function(set)
33931     {
33932         this.groups[set.name] = set;
33933     },
33934     
33935     get: function(name) 
33936     {
33937         if (typeof(this.groups[name]) == 'undefined') {
33938             return false;
33939         }
33940         
33941         return this.groups[name] ;
33942     }
33943     
33944 });
33945 /*
33946  * Based on:
33947  * Ext JS Library 1.1.1
33948  * Copyright(c) 2006-2007, Ext JS, LLC.
33949  *
33950  * Originally Released Under LGPL - original licence link has changed is not relivant.
33951  *
33952  * Fork - LGPL
33953  * <script type="text/javascript">
33954  */
33955
33956
33957 /**
33958  * @class Roo.bootstrap.SplitBar
33959  * @extends Roo.util.Observable
33960  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33961  * <br><br>
33962  * Usage:
33963  * <pre><code>
33964 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33965                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33966 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33967 split.minSize = 100;
33968 split.maxSize = 600;
33969 split.animate = true;
33970 split.on('moved', splitterMoved);
33971 </code></pre>
33972  * @constructor
33973  * Create a new SplitBar
33974  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33975  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33976  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33977  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33978                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33979                         position of the SplitBar).
33980  */
33981 Roo.bootstrap.SplitBar = function(cfg){
33982     
33983     /** @private */
33984     
33985     //{
33986     //  dragElement : elm
33987     //  resizingElement: el,
33988         // optional..
33989     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33990     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33991         // existingProxy ???
33992     //}
33993     
33994     this.el = Roo.get(cfg.dragElement, true);
33995     this.el.dom.unselectable = "on";
33996     /** @private */
33997     this.resizingEl = Roo.get(cfg.resizingElement, true);
33998
33999     /**
34000      * @private
34001      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34002      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34003      * @type Number
34004      */
34005     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34006     
34007     /**
34008      * The minimum size of the resizing element. (Defaults to 0)
34009      * @type Number
34010      */
34011     this.minSize = 0;
34012     
34013     /**
34014      * The maximum size of the resizing element. (Defaults to 2000)
34015      * @type Number
34016      */
34017     this.maxSize = 2000;
34018     
34019     /**
34020      * Whether to animate the transition to the new size
34021      * @type Boolean
34022      */
34023     this.animate = false;
34024     
34025     /**
34026      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34027      * @type Boolean
34028      */
34029     this.useShim = false;
34030     
34031     /** @private */
34032     this.shim = null;
34033     
34034     if(!cfg.existingProxy){
34035         /** @private */
34036         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34037     }else{
34038         this.proxy = Roo.get(cfg.existingProxy).dom;
34039     }
34040     /** @private */
34041     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34042     
34043     /** @private */
34044     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34045     
34046     /** @private */
34047     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34048     
34049     /** @private */
34050     this.dragSpecs = {};
34051     
34052     /**
34053      * @private The adapter to use to positon and resize elements
34054      */
34055     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34056     this.adapter.init(this);
34057     
34058     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34059         /** @private */
34060         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34061         this.el.addClass("roo-splitbar-h");
34062     }else{
34063         /** @private */
34064         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34065         this.el.addClass("roo-splitbar-v");
34066     }
34067     
34068     this.addEvents({
34069         /**
34070          * @event resize
34071          * Fires when the splitter is moved (alias for {@link #event-moved})
34072          * @param {Roo.bootstrap.SplitBar} this
34073          * @param {Number} newSize the new width or height
34074          */
34075         "resize" : true,
34076         /**
34077          * @event moved
34078          * Fires when the splitter is moved
34079          * @param {Roo.bootstrap.SplitBar} this
34080          * @param {Number} newSize the new width or height
34081          */
34082         "moved" : true,
34083         /**
34084          * @event beforeresize
34085          * Fires before the splitter is dragged
34086          * @param {Roo.bootstrap.SplitBar} this
34087          */
34088         "beforeresize" : true,
34089
34090         "beforeapply" : true
34091     });
34092
34093     Roo.util.Observable.call(this);
34094 };
34095
34096 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34097     onStartProxyDrag : function(x, y){
34098         this.fireEvent("beforeresize", this);
34099         if(!this.overlay){
34100             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34101             o.unselectable();
34102             o.enableDisplayMode("block");
34103             // all splitbars share the same overlay
34104             Roo.bootstrap.SplitBar.prototype.overlay = o;
34105         }
34106         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34107         this.overlay.show();
34108         Roo.get(this.proxy).setDisplayed("block");
34109         var size = this.adapter.getElementSize(this);
34110         this.activeMinSize = this.getMinimumSize();;
34111         this.activeMaxSize = this.getMaximumSize();;
34112         var c1 = size - this.activeMinSize;
34113         var c2 = Math.max(this.activeMaxSize - size, 0);
34114         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34115             this.dd.resetConstraints();
34116             this.dd.setXConstraint(
34117                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34118                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34119             );
34120             this.dd.setYConstraint(0, 0);
34121         }else{
34122             this.dd.resetConstraints();
34123             this.dd.setXConstraint(0, 0);
34124             this.dd.setYConstraint(
34125                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34126                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34127             );
34128          }
34129         this.dragSpecs.startSize = size;
34130         this.dragSpecs.startPoint = [x, y];
34131         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34132     },
34133     
34134     /** 
34135      * @private Called after the drag operation by the DDProxy
34136      */
34137     onEndProxyDrag : function(e){
34138         Roo.get(this.proxy).setDisplayed(false);
34139         var endPoint = Roo.lib.Event.getXY(e);
34140         if(this.overlay){
34141             this.overlay.hide();
34142         }
34143         var newSize;
34144         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34145             newSize = this.dragSpecs.startSize + 
34146                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34147                     endPoint[0] - this.dragSpecs.startPoint[0] :
34148                     this.dragSpecs.startPoint[0] - endPoint[0]
34149                 );
34150         }else{
34151             newSize = this.dragSpecs.startSize + 
34152                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34153                     endPoint[1] - this.dragSpecs.startPoint[1] :
34154                     this.dragSpecs.startPoint[1] - endPoint[1]
34155                 );
34156         }
34157         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34158         if(newSize != this.dragSpecs.startSize){
34159             if(this.fireEvent('beforeapply', this, newSize) !== false){
34160                 this.adapter.setElementSize(this, newSize);
34161                 this.fireEvent("moved", this, newSize);
34162                 this.fireEvent("resize", this, newSize);
34163             }
34164         }
34165     },
34166     
34167     /**
34168      * Get the adapter this SplitBar uses
34169      * @return The adapter object
34170      */
34171     getAdapter : function(){
34172         return this.adapter;
34173     },
34174     
34175     /**
34176      * Set the adapter this SplitBar uses
34177      * @param {Object} adapter A SplitBar adapter object
34178      */
34179     setAdapter : function(adapter){
34180         this.adapter = adapter;
34181         this.adapter.init(this);
34182     },
34183     
34184     /**
34185      * Gets the minimum size for the resizing element
34186      * @return {Number} The minimum size
34187      */
34188     getMinimumSize : function(){
34189         return this.minSize;
34190     },
34191     
34192     /**
34193      * Sets the minimum size for the resizing element
34194      * @param {Number} minSize The minimum size
34195      */
34196     setMinimumSize : function(minSize){
34197         this.minSize = minSize;
34198     },
34199     
34200     /**
34201      * Gets the maximum size for the resizing element
34202      * @return {Number} The maximum size
34203      */
34204     getMaximumSize : function(){
34205         return this.maxSize;
34206     },
34207     
34208     /**
34209      * Sets the maximum size for the resizing element
34210      * @param {Number} maxSize The maximum size
34211      */
34212     setMaximumSize : function(maxSize){
34213         this.maxSize = maxSize;
34214     },
34215     
34216     /**
34217      * Sets the initialize size for the resizing element
34218      * @param {Number} size The initial size
34219      */
34220     setCurrentSize : function(size){
34221         var oldAnimate = this.animate;
34222         this.animate = false;
34223         this.adapter.setElementSize(this, size);
34224         this.animate = oldAnimate;
34225     },
34226     
34227     /**
34228      * Destroy this splitbar. 
34229      * @param {Boolean} removeEl True to remove the element
34230      */
34231     destroy : function(removeEl){
34232         if(this.shim){
34233             this.shim.remove();
34234         }
34235         this.dd.unreg();
34236         this.proxy.parentNode.removeChild(this.proxy);
34237         if(removeEl){
34238             this.el.remove();
34239         }
34240     }
34241 });
34242
34243 /**
34244  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
34245  */
34246 Roo.bootstrap.SplitBar.createProxy = function(dir){
34247     var proxy = new Roo.Element(document.createElement("div"));
34248     proxy.unselectable();
34249     var cls = 'roo-splitbar-proxy';
34250     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34251     document.body.appendChild(proxy.dom);
34252     return proxy.dom;
34253 };
34254
34255 /** 
34256  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34257  * Default Adapter. It assumes the splitter and resizing element are not positioned
34258  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34259  */
34260 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34261 };
34262
34263 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34264     // do nothing for now
34265     init : function(s){
34266     
34267     },
34268     /**
34269      * Called before drag operations to get the current size of the resizing element. 
34270      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34271      */
34272      getElementSize : function(s){
34273         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34274             return s.resizingEl.getWidth();
34275         }else{
34276             return s.resizingEl.getHeight();
34277         }
34278     },
34279     
34280     /**
34281      * Called after drag operations to set the size of the resizing element.
34282      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34283      * @param {Number} newSize The new size to set
34284      * @param {Function} onComplete A function to be invoked when resizing is complete
34285      */
34286     setElementSize : function(s, newSize, onComplete){
34287         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34288             if(!s.animate){
34289                 s.resizingEl.setWidth(newSize);
34290                 if(onComplete){
34291                     onComplete(s, newSize);
34292                 }
34293             }else{
34294                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34295             }
34296         }else{
34297             
34298             if(!s.animate){
34299                 s.resizingEl.setHeight(newSize);
34300                 if(onComplete){
34301                     onComplete(s, newSize);
34302                 }
34303             }else{
34304                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34305             }
34306         }
34307     }
34308 };
34309
34310 /** 
34311  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34312  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34313  * Adapter that  moves the splitter element to align with the resized sizing element. 
34314  * Used with an absolute positioned SplitBar.
34315  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34316  * document.body, make sure you assign an id to the body element.
34317  */
34318 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34319     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34320     this.container = Roo.get(container);
34321 };
34322
34323 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34324     init : function(s){
34325         this.basic.init(s);
34326     },
34327     
34328     getElementSize : function(s){
34329         return this.basic.getElementSize(s);
34330     },
34331     
34332     setElementSize : function(s, newSize, onComplete){
34333         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34334     },
34335     
34336     moveSplitter : function(s){
34337         var yes = Roo.bootstrap.SplitBar;
34338         switch(s.placement){
34339             case yes.LEFT:
34340                 s.el.setX(s.resizingEl.getRight());
34341                 break;
34342             case yes.RIGHT:
34343                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34344                 break;
34345             case yes.TOP:
34346                 s.el.setY(s.resizingEl.getBottom());
34347                 break;
34348             case yes.BOTTOM:
34349                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34350                 break;
34351         }
34352     }
34353 };
34354
34355 /**
34356  * Orientation constant - Create a vertical SplitBar
34357  * @static
34358  * @type Number
34359  */
34360 Roo.bootstrap.SplitBar.VERTICAL = 1;
34361
34362 /**
34363  * Orientation constant - Create a horizontal SplitBar
34364  * @static
34365  * @type Number
34366  */
34367 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34368
34369 /**
34370  * Placement constant - The resizing element is to the left of the splitter element
34371  * @static
34372  * @type Number
34373  */
34374 Roo.bootstrap.SplitBar.LEFT = 1;
34375
34376 /**
34377  * Placement constant - The resizing element is to the right of the splitter element
34378  * @static
34379  * @type Number
34380  */
34381 Roo.bootstrap.SplitBar.RIGHT = 2;
34382
34383 /**
34384  * Placement constant - The resizing element is positioned above the splitter element
34385  * @static
34386  * @type Number
34387  */
34388 Roo.bootstrap.SplitBar.TOP = 3;
34389
34390 /**
34391  * Placement constant - The resizing element is positioned under splitter element
34392  * @static
34393  * @type Number
34394  */
34395 Roo.bootstrap.SplitBar.BOTTOM = 4;
34396 Roo.namespace("Roo.bootstrap.layout");/*
34397  * Based on:
34398  * Ext JS Library 1.1.1
34399  * Copyright(c) 2006-2007, Ext JS, LLC.
34400  *
34401  * Originally Released Under LGPL - original licence link has changed is not relivant.
34402  *
34403  * Fork - LGPL
34404  * <script type="text/javascript">
34405  */
34406
34407 /**
34408  * @class Roo.bootstrap.layout.Manager
34409  * @extends Roo.bootstrap.Component
34410  * Base class for layout managers.
34411  */
34412 Roo.bootstrap.layout.Manager = function(config)
34413 {
34414     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34415
34416
34417
34418
34419
34420     /** false to disable window resize monitoring @type Boolean */
34421     this.monitorWindowResize = true;
34422     this.regions = {};
34423     this.addEvents({
34424         /**
34425          * @event layout
34426          * Fires when a layout is performed.
34427          * @param {Roo.LayoutManager} this
34428          */
34429         "layout" : true,
34430         /**
34431          * @event regionresized
34432          * Fires when the user resizes a region.
34433          * @param {Roo.LayoutRegion} region The resized region
34434          * @param {Number} newSize The new size (width for east/west, height for north/south)
34435          */
34436         "regionresized" : true,
34437         /**
34438          * @event regioncollapsed
34439          * Fires when a region is collapsed.
34440          * @param {Roo.LayoutRegion} region The collapsed region
34441          */
34442         "regioncollapsed" : true,
34443         /**
34444          * @event regionexpanded
34445          * Fires when a region is expanded.
34446          * @param {Roo.LayoutRegion} region The expanded region
34447          */
34448         "regionexpanded" : true
34449     });
34450     this.updating = false;
34451
34452     if (config.el) {
34453         this.el = Roo.get(config.el);
34454         this.initEvents();
34455     }
34456
34457 };
34458
34459 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34460
34461
34462     regions : null,
34463
34464     monitorWindowResize : true,
34465
34466
34467     updating : false,
34468
34469
34470     onRender : function(ct, position)
34471     {
34472         if(!this.el){
34473             this.el = Roo.get(ct);
34474             this.initEvents();
34475         }
34476         //this.fireEvent('render',this);
34477     },
34478
34479
34480     initEvents: function()
34481     {
34482
34483
34484         // ie scrollbar fix
34485         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34486             document.body.scroll = "no";
34487         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34488             this.el.position('relative');
34489         }
34490         this.id = this.el.id;
34491         this.el.addClass("roo-layout-container");
34492         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34493         if(this.el.dom != document.body ) {
34494             this.el.on('resize', this.layout,this);
34495             this.el.on('show', this.layout,this);
34496         }
34497
34498     },
34499
34500     /**
34501      * Returns true if this layout is currently being updated
34502      * @return {Boolean}
34503      */
34504     isUpdating : function(){
34505         return this.updating;
34506     },
34507
34508     /**
34509      * Suspend the LayoutManager from doing auto-layouts while
34510      * making multiple add or remove calls
34511      */
34512     beginUpdate : function(){
34513         this.updating = true;
34514     },
34515
34516     /**
34517      * Restore auto-layouts and optionally disable the manager from performing a layout
34518      * @param {Boolean} noLayout true to disable a layout update
34519      */
34520     endUpdate : function(noLayout){
34521         this.updating = false;
34522         if(!noLayout){
34523             this.layout();
34524         }
34525     },
34526
34527     layout: function(){
34528         // abstract...
34529     },
34530
34531     onRegionResized : function(region, newSize){
34532         this.fireEvent("regionresized", region, newSize);
34533         this.layout();
34534     },
34535
34536     onRegionCollapsed : function(region){
34537         this.fireEvent("regioncollapsed", region);
34538     },
34539
34540     onRegionExpanded : function(region){
34541         this.fireEvent("regionexpanded", region);
34542     },
34543
34544     /**
34545      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34546      * performs box-model adjustments.
34547      * @return {Object} The size as an object {width: (the width), height: (the height)}
34548      */
34549     getViewSize : function()
34550     {
34551         var size;
34552         if(this.el.dom != document.body){
34553             size = this.el.getSize();
34554         }else{
34555             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34556         }
34557         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34558         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34559         return size;
34560     },
34561
34562     /**
34563      * Returns the Element this layout is bound to.
34564      * @return {Roo.Element}
34565      */
34566     getEl : function(){
34567         return this.el;
34568     },
34569
34570     /**
34571      * Returns the specified region.
34572      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34573      * @return {Roo.LayoutRegion}
34574      */
34575     getRegion : function(target){
34576         return this.regions[target.toLowerCase()];
34577     },
34578
34579     onWindowResize : function(){
34580         if(this.monitorWindowResize){
34581             this.layout();
34582         }
34583     }
34584 });
34585 /*
34586  * Based on:
34587  * Ext JS Library 1.1.1
34588  * Copyright(c) 2006-2007, Ext JS, LLC.
34589  *
34590  * Originally Released Under LGPL - original licence link has changed is not relivant.
34591  *
34592  * Fork - LGPL
34593  * <script type="text/javascript">
34594  */
34595 /**
34596  * @class Roo.bootstrap.layout.Border
34597  * @extends Roo.bootstrap.layout.Manager
34598  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34599  * please see: examples/bootstrap/nested.html<br><br>
34600  
34601 <b>The container the layout is rendered into can be either the body element or any other element.
34602 If it is not the body element, the container needs to either be an absolute positioned element,
34603 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34604 the container size if it is not the body element.</b>
34605
34606 * @constructor
34607 * Create a new Border
34608 * @param {Object} config Configuration options
34609  */
34610 Roo.bootstrap.layout.Border = function(config){
34611     config = config || {};
34612     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34613     
34614     
34615     
34616     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34617         if(config[region]){
34618             config[region].region = region;
34619             this.addRegion(config[region]);
34620         }
34621     },this);
34622     
34623 };
34624
34625 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34626
34627 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34628     /**
34629      * Creates and adds a new region if it doesn't already exist.
34630      * @param {String} target The target region key (north, south, east, west or center).
34631      * @param {Object} config The regions config object
34632      * @return {BorderLayoutRegion} The new region
34633      */
34634     addRegion : function(config)
34635     {
34636         if(!this.regions[config.region]){
34637             var r = this.factory(config);
34638             this.bindRegion(r);
34639         }
34640         return this.regions[config.region];
34641     },
34642
34643     // private (kinda)
34644     bindRegion : function(r){
34645         this.regions[r.config.region] = r;
34646         
34647         r.on("visibilitychange",    this.layout, this);
34648         r.on("paneladded",          this.layout, this);
34649         r.on("panelremoved",        this.layout, this);
34650         r.on("invalidated",         this.layout, this);
34651         r.on("resized",             this.onRegionResized, this);
34652         r.on("collapsed",           this.onRegionCollapsed, this);
34653         r.on("expanded",            this.onRegionExpanded, this);
34654     },
34655
34656     /**
34657      * Performs a layout update.
34658      */
34659     layout : function()
34660     {
34661         if(this.updating) {
34662             return;
34663         }
34664         
34665         // render all the rebions if they have not been done alreayd?
34666         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34667             if(this.regions[region] && !this.regions[region].bodyEl){
34668                 this.regions[region].onRender(this.el)
34669             }
34670         },this);
34671         
34672         var size = this.getViewSize();
34673         var w = size.width;
34674         var h = size.height;
34675         var centerW = w;
34676         var centerH = h;
34677         var centerY = 0;
34678         var centerX = 0;
34679         //var x = 0, y = 0;
34680
34681         var rs = this.regions;
34682         var north = rs["north"];
34683         var south = rs["south"]; 
34684         var west = rs["west"];
34685         var east = rs["east"];
34686         var center = rs["center"];
34687         //if(this.hideOnLayout){ // not supported anymore
34688             //c.el.setStyle("display", "none");
34689         //}
34690         if(north && north.isVisible()){
34691             var b = north.getBox();
34692             var m = north.getMargins();
34693             b.width = w - (m.left+m.right);
34694             b.x = m.left;
34695             b.y = m.top;
34696             centerY = b.height + b.y + m.bottom;
34697             centerH -= centerY;
34698             north.updateBox(this.safeBox(b));
34699         }
34700         if(south && south.isVisible()){
34701             var b = south.getBox();
34702             var m = south.getMargins();
34703             b.width = w - (m.left+m.right);
34704             b.x = m.left;
34705             var totalHeight = (b.height + m.top + m.bottom);
34706             b.y = h - totalHeight + m.top;
34707             centerH -= totalHeight;
34708             south.updateBox(this.safeBox(b));
34709         }
34710         if(west && west.isVisible()){
34711             var b = west.getBox();
34712             var m = west.getMargins();
34713             b.height = centerH - (m.top+m.bottom);
34714             b.x = m.left;
34715             b.y = centerY + m.top;
34716             var totalWidth = (b.width + m.left + m.right);
34717             centerX += totalWidth;
34718             centerW -= totalWidth;
34719             west.updateBox(this.safeBox(b));
34720         }
34721         if(east && east.isVisible()){
34722             var b = east.getBox();
34723             var m = east.getMargins();
34724             b.height = centerH - (m.top+m.bottom);
34725             var totalWidth = (b.width + m.left + m.right);
34726             b.x = w - totalWidth + m.left;
34727             b.y = centerY + m.top;
34728             centerW -= totalWidth;
34729             east.updateBox(this.safeBox(b));
34730         }
34731         if(center){
34732             var m = center.getMargins();
34733             var centerBox = {
34734                 x: centerX + m.left,
34735                 y: centerY + m.top,
34736                 width: centerW - (m.left+m.right),
34737                 height: centerH - (m.top+m.bottom)
34738             };
34739             //if(this.hideOnLayout){
34740                 //center.el.setStyle("display", "block");
34741             //}
34742             center.updateBox(this.safeBox(centerBox));
34743         }
34744         this.el.repaint();
34745         this.fireEvent("layout", this);
34746     },
34747
34748     // private
34749     safeBox : function(box){
34750         box.width = Math.max(0, box.width);
34751         box.height = Math.max(0, box.height);
34752         return box;
34753     },
34754
34755     /**
34756      * Adds a ContentPanel (or subclass) to this layout.
34757      * @param {String} target The target region key (north, south, east, west or center).
34758      * @param {Roo.ContentPanel} panel The panel to add
34759      * @return {Roo.ContentPanel} The added panel
34760      */
34761     add : function(target, panel){
34762          
34763         target = target.toLowerCase();
34764         return this.regions[target].add(panel);
34765     },
34766
34767     /**
34768      * Remove a ContentPanel (or subclass) to this layout.
34769      * @param {String} target The target region key (north, south, east, west or center).
34770      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34771      * @return {Roo.ContentPanel} The removed panel
34772      */
34773     remove : function(target, panel){
34774         target = target.toLowerCase();
34775         return this.regions[target].remove(panel);
34776     },
34777
34778     /**
34779      * Searches all regions for a panel with the specified id
34780      * @param {String} panelId
34781      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34782      */
34783     findPanel : function(panelId){
34784         var rs = this.regions;
34785         for(var target in rs){
34786             if(typeof rs[target] != "function"){
34787                 var p = rs[target].getPanel(panelId);
34788                 if(p){
34789                     return p;
34790                 }
34791             }
34792         }
34793         return null;
34794     },
34795
34796     /**
34797      * Searches all regions for a panel with the specified id and activates (shows) it.
34798      * @param {String/ContentPanel} panelId The panels id or the panel itself
34799      * @return {Roo.ContentPanel} The shown panel or null
34800      */
34801     showPanel : function(panelId) {
34802       var rs = this.regions;
34803       for(var target in rs){
34804          var r = rs[target];
34805          if(typeof r != "function"){
34806             if(r.hasPanel(panelId)){
34807                return r.showPanel(panelId);
34808             }
34809          }
34810       }
34811       return null;
34812    },
34813
34814    /**
34815      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34816      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34817      */
34818    /*
34819     restoreState : function(provider){
34820         if(!provider){
34821             provider = Roo.state.Manager;
34822         }
34823         var sm = new Roo.LayoutStateManager();
34824         sm.init(this, provider);
34825     },
34826 */
34827  
34828  
34829     /**
34830      * Adds a xtype elements to the layout.
34831      * <pre><code>
34832
34833 layout.addxtype({
34834        xtype : 'ContentPanel',
34835        region: 'west',
34836        items: [ .... ]
34837    }
34838 );
34839
34840 layout.addxtype({
34841         xtype : 'NestedLayoutPanel',
34842         region: 'west',
34843         layout: {
34844            center: { },
34845            west: { }   
34846         },
34847         items : [ ... list of content panels or nested layout panels.. ]
34848    }
34849 );
34850 </code></pre>
34851      * @param {Object} cfg Xtype definition of item to add.
34852      */
34853     addxtype : function(cfg)
34854     {
34855         // basically accepts a pannel...
34856         // can accept a layout region..!?!?
34857         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34858         
34859         
34860         // theory?  children can only be panels??
34861         
34862         //if (!cfg.xtype.match(/Panel$/)) {
34863         //    return false;
34864         //}
34865         var ret = false;
34866         
34867         if (typeof(cfg.region) == 'undefined') {
34868             Roo.log("Failed to add Panel, region was not set");
34869             Roo.log(cfg);
34870             return false;
34871         }
34872         var region = cfg.region;
34873         delete cfg.region;
34874         
34875           
34876         var xitems = [];
34877         if (cfg.items) {
34878             xitems = cfg.items;
34879             delete cfg.items;
34880         }
34881         var nb = false;
34882         
34883         switch(cfg.xtype) 
34884         {
34885             case 'Content':  // ContentPanel (el, cfg)
34886             case 'Scroll':  // ContentPanel (el, cfg)
34887             case 'View': 
34888                 cfg.autoCreate = true;
34889                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34890                 //} else {
34891                 //    var el = this.el.createChild();
34892                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34893                 //}
34894                 
34895                 this.add(region, ret);
34896                 break;
34897             
34898             /*
34899             case 'TreePanel': // our new panel!
34900                 cfg.el = this.el.createChild();
34901                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34902                 this.add(region, ret);
34903                 break;
34904             */
34905             
34906             case 'Nest': 
34907                 // create a new Layout (which is  a Border Layout...
34908                 
34909                 var clayout = cfg.layout;
34910                 clayout.el  = this.el.createChild();
34911                 clayout.items   = clayout.items  || [];
34912                 
34913                 delete cfg.layout;
34914                 
34915                 // replace this exitems with the clayout ones..
34916                 xitems = clayout.items;
34917                  
34918                 // force background off if it's in center...
34919                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34920                     cfg.background = false;
34921                 }
34922                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34923                 
34924                 
34925                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34926                 //console.log('adding nested layout panel '  + cfg.toSource());
34927                 this.add(region, ret);
34928                 nb = {}; /// find first...
34929                 break;
34930             
34931             case 'Grid':
34932                 
34933                 // needs grid and region
34934                 
34935                 //var el = this.getRegion(region).el.createChild();
34936                 /*
34937                  *var el = this.el.createChild();
34938                 // create the grid first...
34939                 cfg.grid.container = el;
34940                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34941                 */
34942                 
34943                 if (region == 'center' && this.active ) {
34944                     cfg.background = false;
34945                 }
34946                 
34947                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34948                 
34949                 this.add(region, ret);
34950                 /*
34951                 if (cfg.background) {
34952                     // render grid on panel activation (if panel background)
34953                     ret.on('activate', function(gp) {
34954                         if (!gp.grid.rendered) {
34955                     //        gp.grid.render(el);
34956                         }
34957                     });
34958                 } else {
34959                   //  cfg.grid.render(el);
34960                 }
34961                 */
34962                 break;
34963            
34964            
34965             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34966                 // it was the old xcomponent building that caused this before.
34967                 // espeically if border is the top element in the tree.
34968                 ret = this;
34969                 break; 
34970                 
34971                     
34972                 
34973                 
34974                 
34975             default:
34976                 /*
34977                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34978                     
34979                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34980                     this.add(region, ret);
34981                 } else {
34982                 */
34983                     Roo.log(cfg);
34984                     throw "Can not add '" + cfg.xtype + "' to Border";
34985                     return null;
34986              
34987                                 
34988              
34989         }
34990         this.beginUpdate();
34991         // add children..
34992         var region = '';
34993         var abn = {};
34994         Roo.each(xitems, function(i)  {
34995             region = nb && i.region ? i.region : false;
34996             
34997             var add = ret.addxtype(i);
34998            
34999             if (region) {
35000                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35001                 if (!i.background) {
35002                     abn[region] = nb[region] ;
35003                 }
35004             }
35005             
35006         });
35007         this.endUpdate();
35008
35009         // make the last non-background panel active..
35010         //if (nb) { Roo.log(abn); }
35011         if (nb) {
35012             
35013             for(var r in abn) {
35014                 region = this.getRegion(r);
35015                 if (region) {
35016                     // tried using nb[r], but it does not work..
35017                      
35018                     region.showPanel(abn[r]);
35019                    
35020                 }
35021             }
35022         }
35023         return ret;
35024         
35025     },
35026     
35027     
35028 // private
35029     factory : function(cfg)
35030     {
35031         
35032         var validRegions = Roo.bootstrap.layout.Border.regions;
35033
35034         var target = cfg.region;
35035         cfg.mgr = this;
35036         
35037         var r = Roo.bootstrap.layout;
35038         Roo.log(target);
35039         switch(target){
35040             case "north":
35041                 return new r.North(cfg);
35042             case "south":
35043                 return new r.South(cfg);
35044             case "east":
35045                 return new r.East(cfg);
35046             case "west":
35047                 return new r.West(cfg);
35048             case "center":
35049                 return new r.Center(cfg);
35050         }
35051         throw 'Layout region "'+target+'" not supported.';
35052     }
35053     
35054     
35055 });
35056  /*
35057  * Based on:
35058  * Ext JS Library 1.1.1
35059  * Copyright(c) 2006-2007, Ext JS, LLC.
35060  *
35061  * Originally Released Under LGPL - original licence link has changed is not relivant.
35062  *
35063  * Fork - LGPL
35064  * <script type="text/javascript">
35065  */
35066  
35067 /**
35068  * @class Roo.bootstrap.layout.Basic
35069  * @extends Roo.util.Observable
35070  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35071  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35072  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35073  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35074  * @cfg {string}   region  the region that it inhabits..
35075  * @cfg {bool}   skipConfig skip config?
35076  * 
35077
35078  */
35079 Roo.bootstrap.layout.Basic = function(config){
35080     
35081     this.mgr = config.mgr;
35082     
35083     this.position = config.region;
35084     
35085     var skipConfig = config.skipConfig;
35086     
35087     this.events = {
35088         /**
35089          * @scope Roo.BasicLayoutRegion
35090          */
35091         
35092         /**
35093          * @event beforeremove
35094          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35095          * @param {Roo.LayoutRegion} this
35096          * @param {Roo.ContentPanel} panel The panel
35097          * @param {Object} e The cancel event object
35098          */
35099         "beforeremove" : true,
35100         /**
35101          * @event invalidated
35102          * Fires when the layout for this region is changed.
35103          * @param {Roo.LayoutRegion} this
35104          */
35105         "invalidated" : true,
35106         /**
35107          * @event visibilitychange
35108          * Fires when this region is shown or hidden 
35109          * @param {Roo.LayoutRegion} this
35110          * @param {Boolean} visibility true or false
35111          */
35112         "visibilitychange" : true,
35113         /**
35114          * @event paneladded
35115          * Fires when a panel is added. 
35116          * @param {Roo.LayoutRegion} this
35117          * @param {Roo.ContentPanel} panel The panel
35118          */
35119         "paneladded" : true,
35120         /**
35121          * @event panelremoved
35122          * Fires when a panel is removed. 
35123          * @param {Roo.LayoutRegion} this
35124          * @param {Roo.ContentPanel} panel The panel
35125          */
35126         "panelremoved" : true,
35127         /**
35128          * @event beforecollapse
35129          * Fires when this region before collapse.
35130          * @param {Roo.LayoutRegion} this
35131          */
35132         "beforecollapse" : true,
35133         /**
35134          * @event collapsed
35135          * Fires when this region is collapsed.
35136          * @param {Roo.LayoutRegion} this
35137          */
35138         "collapsed" : true,
35139         /**
35140          * @event expanded
35141          * Fires when this region is expanded.
35142          * @param {Roo.LayoutRegion} this
35143          */
35144         "expanded" : true,
35145         /**
35146          * @event slideshow
35147          * Fires when this region is slid into view.
35148          * @param {Roo.LayoutRegion} this
35149          */
35150         "slideshow" : true,
35151         /**
35152          * @event slidehide
35153          * Fires when this region slides out of view. 
35154          * @param {Roo.LayoutRegion} this
35155          */
35156         "slidehide" : true,
35157         /**
35158          * @event panelactivated
35159          * Fires when a panel is activated. 
35160          * @param {Roo.LayoutRegion} this
35161          * @param {Roo.ContentPanel} panel The activated panel
35162          */
35163         "panelactivated" : true,
35164         /**
35165          * @event resized
35166          * Fires when the user resizes this region. 
35167          * @param {Roo.LayoutRegion} this
35168          * @param {Number} newSize The new size (width for east/west, height for north/south)
35169          */
35170         "resized" : true
35171     };
35172     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35173     this.panels = new Roo.util.MixedCollection();
35174     this.panels.getKey = this.getPanelId.createDelegate(this);
35175     this.box = null;
35176     this.activePanel = null;
35177     // ensure listeners are added...
35178     
35179     if (config.listeners || config.events) {
35180         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35181             listeners : config.listeners || {},
35182             events : config.events || {}
35183         });
35184     }
35185     
35186     if(skipConfig !== true){
35187         this.applyConfig(config);
35188     }
35189 };
35190
35191 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35192 {
35193     getPanelId : function(p){
35194         return p.getId();
35195     },
35196     
35197     applyConfig : function(config){
35198         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35199         this.config = config;
35200         
35201     },
35202     
35203     /**
35204      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35205      * the width, for horizontal (north, south) the height.
35206      * @param {Number} newSize The new width or height
35207      */
35208     resizeTo : function(newSize){
35209         var el = this.el ? this.el :
35210                  (this.activePanel ? this.activePanel.getEl() : null);
35211         if(el){
35212             switch(this.position){
35213                 case "east":
35214                 case "west":
35215                     el.setWidth(newSize);
35216                     this.fireEvent("resized", this, newSize);
35217                 break;
35218                 case "north":
35219                 case "south":
35220                     el.setHeight(newSize);
35221                     this.fireEvent("resized", this, newSize);
35222                 break;                
35223             }
35224         }
35225     },
35226     
35227     getBox : function(){
35228         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35229     },
35230     
35231     getMargins : function(){
35232         return this.margins;
35233     },
35234     
35235     updateBox : function(box){
35236         this.box = box;
35237         var el = this.activePanel.getEl();
35238         el.dom.style.left = box.x + "px";
35239         el.dom.style.top = box.y + "px";
35240         this.activePanel.setSize(box.width, box.height);
35241     },
35242     
35243     /**
35244      * Returns the container element for this region.
35245      * @return {Roo.Element}
35246      */
35247     getEl : function(){
35248         return this.activePanel;
35249     },
35250     
35251     /**
35252      * Returns true if this region is currently visible.
35253      * @return {Boolean}
35254      */
35255     isVisible : function(){
35256         return this.activePanel ? true : false;
35257     },
35258     
35259     setActivePanel : function(panel){
35260         panel = this.getPanel(panel);
35261         if(this.activePanel && this.activePanel != panel){
35262             this.activePanel.setActiveState(false);
35263             this.activePanel.getEl().setLeftTop(-10000,-10000);
35264         }
35265         this.activePanel = panel;
35266         panel.setActiveState(true);
35267         if(this.box){
35268             panel.setSize(this.box.width, this.box.height);
35269         }
35270         this.fireEvent("panelactivated", this, panel);
35271         this.fireEvent("invalidated");
35272     },
35273     
35274     /**
35275      * Show the specified panel.
35276      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35277      * @return {Roo.ContentPanel} The shown panel or null
35278      */
35279     showPanel : function(panel){
35280         panel = this.getPanel(panel);
35281         if(panel){
35282             this.setActivePanel(panel);
35283         }
35284         return panel;
35285     },
35286     
35287     /**
35288      * Get the active panel for this region.
35289      * @return {Roo.ContentPanel} The active panel or null
35290      */
35291     getActivePanel : function(){
35292         return this.activePanel;
35293     },
35294     
35295     /**
35296      * Add the passed ContentPanel(s)
35297      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35298      * @return {Roo.ContentPanel} The panel added (if only one was added)
35299      */
35300     add : function(panel){
35301         if(arguments.length > 1){
35302             for(var i = 0, len = arguments.length; i < len; i++) {
35303                 this.add(arguments[i]);
35304             }
35305             return null;
35306         }
35307         if(this.hasPanel(panel)){
35308             this.showPanel(panel);
35309             return panel;
35310         }
35311         var el = panel.getEl();
35312         if(el.dom.parentNode != this.mgr.el.dom){
35313             this.mgr.el.dom.appendChild(el.dom);
35314         }
35315         if(panel.setRegion){
35316             panel.setRegion(this);
35317         }
35318         this.panels.add(panel);
35319         el.setStyle("position", "absolute");
35320         if(!panel.background){
35321             this.setActivePanel(panel);
35322             if(this.config.initialSize && this.panels.getCount()==1){
35323                 this.resizeTo(this.config.initialSize);
35324             }
35325         }
35326         this.fireEvent("paneladded", this, panel);
35327         return panel;
35328     },
35329     
35330     /**
35331      * Returns true if the panel is in this region.
35332      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35333      * @return {Boolean}
35334      */
35335     hasPanel : function(panel){
35336         if(typeof panel == "object"){ // must be panel obj
35337             panel = panel.getId();
35338         }
35339         return this.getPanel(panel) ? true : false;
35340     },
35341     
35342     /**
35343      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35344      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35345      * @param {Boolean} preservePanel Overrides the config preservePanel option
35346      * @return {Roo.ContentPanel} The panel that was removed
35347      */
35348     remove : function(panel, preservePanel){
35349         panel = this.getPanel(panel);
35350         if(!panel){
35351             return null;
35352         }
35353         var e = {};
35354         this.fireEvent("beforeremove", this, panel, e);
35355         if(e.cancel === true){
35356             return null;
35357         }
35358         var panelId = panel.getId();
35359         this.panels.removeKey(panelId);
35360         return panel;
35361     },
35362     
35363     /**
35364      * Returns the panel specified or null if it's not in this region.
35365      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35366      * @return {Roo.ContentPanel}
35367      */
35368     getPanel : function(id){
35369         if(typeof id == "object"){ // must be panel obj
35370             return id;
35371         }
35372         return this.panels.get(id);
35373     },
35374     
35375     /**
35376      * Returns this regions position (north/south/east/west/center).
35377      * @return {String} 
35378      */
35379     getPosition: function(){
35380         return this.position;    
35381     }
35382 });/*
35383  * Based on:
35384  * Ext JS Library 1.1.1
35385  * Copyright(c) 2006-2007, Ext JS, LLC.
35386  *
35387  * Originally Released Under LGPL - original licence link has changed is not relivant.
35388  *
35389  * Fork - LGPL
35390  * <script type="text/javascript">
35391  */
35392  
35393 /**
35394  * @class Roo.bootstrap.layout.Region
35395  * @extends Roo.bootstrap.layout.Basic
35396  * This class represents a region in a layout manager.
35397  
35398  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35399  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
35400  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35401  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35402  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35403  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35404  * @cfg {String}    title           The title for the region (overrides panel titles)
35405  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35406  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35407  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35408  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35409  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35410  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35411  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35412  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35413  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35414  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35415
35416  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35417  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35418  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35419  * @cfg {Number}    width           For East/West panels
35420  * @cfg {Number}    height          For North/South panels
35421  * @cfg {Boolean}   split           To show the splitter
35422  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35423  * 
35424  * @cfg {string}   cls             Extra CSS classes to add to region
35425  * 
35426  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35427  * @cfg {string}   region  the region that it inhabits..
35428  *
35429
35430  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35431  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35432
35433  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35434  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35435  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35436  */
35437 Roo.bootstrap.layout.Region = function(config)
35438 {
35439     this.applyConfig(config);
35440
35441     var mgr = config.mgr;
35442     var pos = config.region;
35443     config.skipConfig = true;
35444     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35445     
35446     if (mgr.el) {
35447         this.onRender(mgr.el);   
35448     }
35449      
35450     this.visible = true;
35451     this.collapsed = false;
35452     this.unrendered_panels = [];
35453 };
35454
35455 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35456
35457     position: '', // set by wrapper (eg. north/south etc..)
35458     unrendered_panels : null,  // unrendered panels.
35459     createBody : function(){
35460         /** This region's body element 
35461         * @type Roo.Element */
35462         this.bodyEl = this.el.createChild({
35463                 tag: "div",
35464                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35465         });
35466     },
35467
35468     onRender: function(ctr, pos)
35469     {
35470         var dh = Roo.DomHelper;
35471         /** This region's container element 
35472         * @type Roo.Element */
35473         this.el = dh.append(ctr.dom, {
35474                 tag: "div",
35475                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35476             }, true);
35477         /** This region's title element 
35478         * @type Roo.Element */
35479     
35480         this.titleEl = dh.append(this.el.dom,
35481             {
35482                     tag: "div",
35483                     unselectable: "on",
35484                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35485                     children:[
35486                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35487                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35488                     ]}, true);
35489         
35490         this.titleEl.enableDisplayMode();
35491         /** This region's title text element 
35492         * @type HTMLElement */
35493         this.titleTextEl = this.titleEl.dom.firstChild;
35494         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35495         /*
35496         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35497         this.closeBtn.enableDisplayMode();
35498         this.closeBtn.on("click", this.closeClicked, this);
35499         this.closeBtn.hide();
35500     */
35501         this.createBody(this.config);
35502         if(this.config.hideWhenEmpty){
35503             this.hide();
35504             this.on("paneladded", this.validateVisibility, this);
35505             this.on("panelremoved", this.validateVisibility, this);
35506         }
35507         if(this.autoScroll){
35508             this.bodyEl.setStyle("overflow", "auto");
35509         }else{
35510             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35511         }
35512         //if(c.titlebar !== false){
35513             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35514                 this.titleEl.hide();
35515             }else{
35516                 this.titleEl.show();
35517                 if(this.config.title){
35518                     this.titleTextEl.innerHTML = this.config.title;
35519                 }
35520             }
35521         //}
35522         if(this.config.collapsed){
35523             this.collapse(true);
35524         }
35525         if(this.config.hidden){
35526             this.hide();
35527         }
35528         
35529         if (this.unrendered_panels && this.unrendered_panels.length) {
35530             for (var i =0;i< this.unrendered_panels.length; i++) {
35531                 this.add(this.unrendered_panels[i]);
35532             }
35533             this.unrendered_panels = null;
35534             
35535         }
35536         
35537     },
35538     
35539     applyConfig : function(c)
35540     {
35541         /*
35542          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35543             var dh = Roo.DomHelper;
35544             if(c.titlebar !== false){
35545                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35546                 this.collapseBtn.on("click", this.collapse, this);
35547                 this.collapseBtn.enableDisplayMode();
35548                 /*
35549                 if(c.showPin === true || this.showPin){
35550                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35551                     this.stickBtn.enableDisplayMode();
35552                     this.stickBtn.on("click", this.expand, this);
35553                     this.stickBtn.hide();
35554                 }
35555                 
35556             }
35557             */
35558             /** This region's collapsed element
35559             * @type Roo.Element */
35560             /*
35561              *
35562             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35563                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35564             ]}, true);
35565             
35566             if(c.floatable !== false){
35567                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35568                this.collapsedEl.on("click", this.collapseClick, this);
35569             }
35570
35571             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35572                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35573                    id: "message", unselectable: "on", style:{"float":"left"}});
35574                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35575              }
35576             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35577             this.expandBtn.on("click", this.expand, this);
35578             
35579         }
35580         
35581         if(this.collapseBtn){
35582             this.collapseBtn.setVisible(c.collapsible == true);
35583         }
35584         
35585         this.cmargins = c.cmargins || this.cmargins ||
35586                          (this.position == "west" || this.position == "east" ?
35587                              {top: 0, left: 2, right:2, bottom: 0} :
35588                              {top: 2, left: 0, right:0, bottom: 2});
35589         */
35590         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35591         
35592         
35593         this.bottomTabs = c.tabPosition != "top";
35594         
35595         this.autoScroll = c.autoScroll || false;
35596         
35597         
35598        
35599         
35600         this.duration = c.duration || .30;
35601         this.slideDuration = c.slideDuration || .45;
35602         this.config = c;
35603        
35604     },
35605     /**
35606      * Returns true if this region is currently visible.
35607      * @return {Boolean}
35608      */
35609     isVisible : function(){
35610         return this.visible;
35611     },
35612
35613     /**
35614      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35615      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35616      */
35617     //setCollapsedTitle : function(title){
35618     //    title = title || "&#160;";
35619      //   if(this.collapsedTitleTextEl){
35620       //      this.collapsedTitleTextEl.innerHTML = title;
35621        // }
35622     //},
35623
35624     getBox : function(){
35625         var b;
35626       //  if(!this.collapsed){
35627             b = this.el.getBox(false, true);
35628        // }else{
35629           //  b = this.collapsedEl.getBox(false, true);
35630         //}
35631         return b;
35632     },
35633
35634     getMargins : function(){
35635         return this.margins;
35636         //return this.collapsed ? this.cmargins : this.margins;
35637     },
35638 /*
35639     highlight : function(){
35640         this.el.addClass("x-layout-panel-dragover");
35641     },
35642
35643     unhighlight : function(){
35644         this.el.removeClass("x-layout-panel-dragover");
35645     },
35646 */
35647     updateBox : function(box)
35648     {
35649         if (!this.bodyEl) {
35650             return; // not rendered yet..
35651         }
35652         
35653         this.box = box;
35654         if(!this.collapsed){
35655             this.el.dom.style.left = box.x + "px";
35656             this.el.dom.style.top = box.y + "px";
35657             this.updateBody(box.width, box.height);
35658         }else{
35659             this.collapsedEl.dom.style.left = box.x + "px";
35660             this.collapsedEl.dom.style.top = box.y + "px";
35661             this.collapsedEl.setSize(box.width, box.height);
35662         }
35663         if(this.tabs){
35664             this.tabs.autoSizeTabs();
35665         }
35666     },
35667
35668     updateBody : function(w, h)
35669     {
35670         if(w !== null){
35671             this.el.setWidth(w);
35672             w -= this.el.getBorderWidth("rl");
35673             if(this.config.adjustments){
35674                 w += this.config.adjustments[0];
35675             }
35676         }
35677         if(h !== null && h > 0){
35678             this.el.setHeight(h);
35679             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35680             h -= this.el.getBorderWidth("tb");
35681             if(this.config.adjustments){
35682                 h += this.config.adjustments[1];
35683             }
35684             this.bodyEl.setHeight(h);
35685             if(this.tabs){
35686                 h = this.tabs.syncHeight(h);
35687             }
35688         }
35689         if(this.panelSize){
35690             w = w !== null ? w : this.panelSize.width;
35691             h = h !== null ? h : this.panelSize.height;
35692         }
35693         if(this.activePanel){
35694             var el = this.activePanel.getEl();
35695             w = w !== null ? w : el.getWidth();
35696             h = h !== null ? h : el.getHeight();
35697             this.panelSize = {width: w, height: h};
35698             this.activePanel.setSize(w, h);
35699         }
35700         if(Roo.isIE && this.tabs){
35701             this.tabs.el.repaint();
35702         }
35703     },
35704
35705     /**
35706      * Returns the container element for this region.
35707      * @return {Roo.Element}
35708      */
35709     getEl : function(){
35710         return this.el;
35711     },
35712
35713     /**
35714      * Hides this region.
35715      */
35716     hide : function(){
35717         //if(!this.collapsed){
35718             this.el.dom.style.left = "-2000px";
35719             this.el.hide();
35720         //}else{
35721          //   this.collapsedEl.dom.style.left = "-2000px";
35722          //   this.collapsedEl.hide();
35723        // }
35724         this.visible = false;
35725         this.fireEvent("visibilitychange", this, false);
35726     },
35727
35728     /**
35729      * Shows this region if it was previously hidden.
35730      */
35731     show : function(){
35732         //if(!this.collapsed){
35733             this.el.show();
35734         //}else{
35735         //    this.collapsedEl.show();
35736        // }
35737         this.visible = true;
35738         this.fireEvent("visibilitychange", this, true);
35739     },
35740 /*
35741     closeClicked : function(){
35742         if(this.activePanel){
35743             this.remove(this.activePanel);
35744         }
35745     },
35746
35747     collapseClick : function(e){
35748         if(this.isSlid){
35749            e.stopPropagation();
35750            this.slideIn();
35751         }else{
35752            e.stopPropagation();
35753            this.slideOut();
35754         }
35755     },
35756 */
35757     /**
35758      * Collapses this region.
35759      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35760      */
35761     /*
35762     collapse : function(skipAnim, skipCheck = false){
35763         if(this.collapsed) {
35764             return;
35765         }
35766         
35767         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35768             
35769             this.collapsed = true;
35770             if(this.split){
35771                 this.split.el.hide();
35772             }
35773             if(this.config.animate && skipAnim !== true){
35774                 this.fireEvent("invalidated", this);
35775                 this.animateCollapse();
35776             }else{
35777                 this.el.setLocation(-20000,-20000);
35778                 this.el.hide();
35779                 this.collapsedEl.show();
35780                 this.fireEvent("collapsed", this);
35781                 this.fireEvent("invalidated", this);
35782             }
35783         }
35784         
35785     },
35786 */
35787     animateCollapse : function(){
35788         // overridden
35789     },
35790
35791     /**
35792      * Expands this region if it was previously collapsed.
35793      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35794      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35795      */
35796     /*
35797     expand : function(e, skipAnim){
35798         if(e) {
35799             e.stopPropagation();
35800         }
35801         if(!this.collapsed || this.el.hasActiveFx()) {
35802             return;
35803         }
35804         if(this.isSlid){
35805             this.afterSlideIn();
35806             skipAnim = true;
35807         }
35808         this.collapsed = false;
35809         if(this.config.animate && skipAnim !== true){
35810             this.animateExpand();
35811         }else{
35812             this.el.show();
35813             if(this.split){
35814                 this.split.el.show();
35815             }
35816             this.collapsedEl.setLocation(-2000,-2000);
35817             this.collapsedEl.hide();
35818             this.fireEvent("invalidated", this);
35819             this.fireEvent("expanded", this);
35820         }
35821     },
35822 */
35823     animateExpand : function(){
35824         // overridden
35825     },
35826
35827     initTabs : function()
35828     {
35829         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35830         
35831         var ts = new Roo.bootstrap.panel.Tabs({
35832                 el: this.bodyEl.dom,
35833                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35834                 disableTooltips: this.config.disableTabTips,
35835                 toolbar : this.config.toolbar
35836             });
35837         
35838         if(this.config.hideTabs){
35839             ts.stripWrap.setDisplayed(false);
35840         }
35841         this.tabs = ts;
35842         ts.resizeTabs = this.config.resizeTabs === true;
35843         ts.minTabWidth = this.config.minTabWidth || 40;
35844         ts.maxTabWidth = this.config.maxTabWidth || 250;
35845         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35846         ts.monitorResize = false;
35847         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35848         ts.bodyEl.addClass('roo-layout-tabs-body');
35849         this.panels.each(this.initPanelAsTab, this);
35850     },
35851
35852     initPanelAsTab : function(panel){
35853         var ti = this.tabs.addTab(
35854             panel.getEl().id,
35855             panel.getTitle(),
35856             null,
35857             this.config.closeOnTab && panel.isClosable(),
35858             panel.tpl
35859         );
35860         if(panel.tabTip !== undefined){
35861             ti.setTooltip(panel.tabTip);
35862         }
35863         ti.on("activate", function(){
35864               this.setActivePanel(panel);
35865         }, this);
35866         
35867         if(this.config.closeOnTab){
35868             ti.on("beforeclose", function(t, e){
35869                 e.cancel = true;
35870                 this.remove(panel);
35871             }, this);
35872         }
35873         
35874         panel.tabItem = ti;
35875         
35876         return ti;
35877     },
35878
35879     updatePanelTitle : function(panel, title)
35880     {
35881         if(this.activePanel == panel){
35882             this.updateTitle(title);
35883         }
35884         if(this.tabs){
35885             var ti = this.tabs.getTab(panel.getEl().id);
35886             ti.setText(title);
35887             if(panel.tabTip !== undefined){
35888                 ti.setTooltip(panel.tabTip);
35889             }
35890         }
35891     },
35892
35893     updateTitle : function(title){
35894         if(this.titleTextEl && !this.config.title){
35895             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35896         }
35897     },
35898
35899     setActivePanel : function(panel)
35900     {
35901         panel = this.getPanel(panel);
35902         if(this.activePanel && this.activePanel != panel){
35903             if(this.activePanel.setActiveState(false) === false){
35904                 return;
35905             }
35906         }
35907         this.activePanel = panel;
35908         panel.setActiveState(true);
35909         if(this.panelSize){
35910             panel.setSize(this.panelSize.width, this.panelSize.height);
35911         }
35912         if(this.closeBtn){
35913             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35914         }
35915         this.updateTitle(panel.getTitle());
35916         if(this.tabs){
35917             this.fireEvent("invalidated", this);
35918         }
35919         this.fireEvent("panelactivated", this, panel);
35920     },
35921
35922     /**
35923      * Shows the specified panel.
35924      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35925      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35926      */
35927     showPanel : function(panel)
35928     {
35929         panel = this.getPanel(panel);
35930         if(panel){
35931             if(this.tabs){
35932                 var tab = this.tabs.getTab(panel.getEl().id);
35933                 if(tab.isHidden()){
35934                     this.tabs.unhideTab(tab.id);
35935                 }
35936                 tab.activate();
35937             }else{
35938                 this.setActivePanel(panel);
35939             }
35940         }
35941         return panel;
35942     },
35943
35944     /**
35945      * Get the active panel for this region.
35946      * @return {Roo.ContentPanel} The active panel or null
35947      */
35948     getActivePanel : function(){
35949         return this.activePanel;
35950     },
35951
35952     validateVisibility : function(){
35953         if(this.panels.getCount() < 1){
35954             this.updateTitle("&#160;");
35955             this.closeBtn.hide();
35956             this.hide();
35957         }else{
35958             if(!this.isVisible()){
35959                 this.show();
35960             }
35961         }
35962     },
35963
35964     /**
35965      * Adds the passed ContentPanel(s) to this region.
35966      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35967      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35968      */
35969     add : function(panel)
35970     {
35971         if(arguments.length > 1){
35972             for(var i = 0, len = arguments.length; i < len; i++) {
35973                 this.add(arguments[i]);
35974             }
35975             return null;
35976         }
35977         
35978         // if we have not been rendered yet, then we can not really do much of this..
35979         if (!this.bodyEl) {
35980             this.unrendered_panels.push(panel);
35981             return panel;
35982         }
35983         
35984         
35985         
35986         
35987         if(this.hasPanel(panel)){
35988             this.showPanel(panel);
35989             return panel;
35990         }
35991         panel.setRegion(this);
35992         this.panels.add(panel);
35993        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35994             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35995             // and hide them... ???
35996             this.bodyEl.dom.appendChild(panel.getEl().dom);
35997             if(panel.background !== true){
35998                 this.setActivePanel(panel);
35999             }
36000             this.fireEvent("paneladded", this, panel);
36001             return panel;
36002         }
36003         */
36004         if(!this.tabs){
36005             this.initTabs();
36006         }else{
36007             this.initPanelAsTab(panel);
36008         }
36009         
36010         
36011         if(panel.background !== true){
36012             this.tabs.activate(panel.getEl().id);
36013         }
36014         this.fireEvent("paneladded", this, panel);
36015         return panel;
36016     },
36017
36018     /**
36019      * Hides the tab for the specified panel.
36020      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36021      */
36022     hidePanel : function(panel){
36023         if(this.tabs && (panel = this.getPanel(panel))){
36024             this.tabs.hideTab(panel.getEl().id);
36025         }
36026     },
36027
36028     /**
36029      * Unhides the tab for a previously hidden panel.
36030      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36031      */
36032     unhidePanel : function(panel){
36033         if(this.tabs && (panel = this.getPanel(panel))){
36034             this.tabs.unhideTab(panel.getEl().id);
36035         }
36036     },
36037
36038     clearPanels : function(){
36039         while(this.panels.getCount() > 0){
36040              this.remove(this.panels.first());
36041         }
36042     },
36043
36044     /**
36045      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36046      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36047      * @param {Boolean} preservePanel Overrides the config preservePanel option
36048      * @return {Roo.ContentPanel} The panel that was removed
36049      */
36050     remove : function(panel, preservePanel)
36051     {
36052         panel = this.getPanel(panel);
36053         if(!panel){
36054             return null;
36055         }
36056         var e = {};
36057         this.fireEvent("beforeremove", this, panel, e);
36058         if(e.cancel === true){
36059             return null;
36060         }
36061         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36062         var panelId = panel.getId();
36063         this.panels.removeKey(panelId);
36064         if(preservePanel){
36065             document.body.appendChild(panel.getEl().dom);
36066         }
36067         if(this.tabs){
36068             this.tabs.removeTab(panel.getEl().id);
36069         }else if (!preservePanel){
36070             this.bodyEl.dom.removeChild(panel.getEl().dom);
36071         }
36072         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36073             var p = this.panels.first();
36074             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36075             tempEl.appendChild(p.getEl().dom);
36076             this.bodyEl.update("");
36077             this.bodyEl.dom.appendChild(p.getEl().dom);
36078             tempEl = null;
36079             this.updateTitle(p.getTitle());
36080             this.tabs = null;
36081             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36082             this.setActivePanel(p);
36083         }
36084         panel.setRegion(null);
36085         if(this.activePanel == panel){
36086             this.activePanel = null;
36087         }
36088         if(this.config.autoDestroy !== false && preservePanel !== true){
36089             try{panel.destroy();}catch(e){}
36090         }
36091         this.fireEvent("panelremoved", this, panel);
36092         return panel;
36093     },
36094
36095     /**
36096      * Returns the TabPanel component used by this region
36097      * @return {Roo.TabPanel}
36098      */
36099     getTabs : function(){
36100         return this.tabs;
36101     },
36102
36103     createTool : function(parentEl, className){
36104         var btn = Roo.DomHelper.append(parentEl, {
36105             tag: "div",
36106             cls: "x-layout-tools-button",
36107             children: [ {
36108                 tag: "div",
36109                 cls: "roo-layout-tools-button-inner " + className,
36110                 html: "&#160;"
36111             }]
36112         }, true);
36113         btn.addClassOnOver("roo-layout-tools-button-over");
36114         return btn;
36115     }
36116 });/*
36117  * Based on:
36118  * Ext JS Library 1.1.1
36119  * Copyright(c) 2006-2007, Ext JS, LLC.
36120  *
36121  * Originally Released Under LGPL - original licence link has changed is not relivant.
36122  *
36123  * Fork - LGPL
36124  * <script type="text/javascript">
36125  */
36126  
36127
36128
36129 /**
36130  * @class Roo.SplitLayoutRegion
36131  * @extends Roo.LayoutRegion
36132  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36133  */
36134 Roo.bootstrap.layout.Split = function(config){
36135     this.cursor = config.cursor;
36136     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36137 };
36138
36139 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36140 {
36141     splitTip : "Drag to resize.",
36142     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36143     useSplitTips : false,
36144
36145     applyConfig : function(config){
36146         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36147     },
36148     
36149     onRender : function(ctr,pos) {
36150         
36151         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36152         if(!this.config.split){
36153             return;
36154         }
36155         if(!this.split){
36156             
36157             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36158                             tag: "div",
36159                             id: this.el.id + "-split",
36160                             cls: "roo-layout-split roo-layout-split-"+this.position,
36161                             html: "&#160;"
36162             });
36163             /** The SplitBar for this region 
36164             * @type Roo.SplitBar */
36165             // does not exist yet...
36166             Roo.log([this.position, this.orientation]);
36167             
36168             this.split = new Roo.bootstrap.SplitBar({
36169                 dragElement : splitEl,
36170                 resizingElement: this.el,
36171                 orientation : this.orientation
36172             });
36173             
36174             this.split.on("moved", this.onSplitMove, this);
36175             this.split.useShim = this.config.useShim === true;
36176             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36177             if(this.useSplitTips){
36178                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36179             }
36180             //if(config.collapsible){
36181             //    this.split.el.on("dblclick", this.collapse,  this);
36182             //}
36183         }
36184         if(typeof this.config.minSize != "undefined"){
36185             this.split.minSize = this.config.minSize;
36186         }
36187         if(typeof this.config.maxSize != "undefined"){
36188             this.split.maxSize = this.config.maxSize;
36189         }
36190         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36191             this.hideSplitter();
36192         }
36193         
36194     },
36195
36196     getHMaxSize : function(){
36197          var cmax = this.config.maxSize || 10000;
36198          var center = this.mgr.getRegion("center");
36199          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36200     },
36201
36202     getVMaxSize : function(){
36203          var cmax = this.config.maxSize || 10000;
36204          var center = this.mgr.getRegion("center");
36205          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36206     },
36207
36208     onSplitMove : function(split, newSize){
36209         this.fireEvent("resized", this, newSize);
36210     },
36211     
36212     /** 
36213      * Returns the {@link Roo.SplitBar} for this region.
36214      * @return {Roo.SplitBar}
36215      */
36216     getSplitBar : function(){
36217         return this.split;
36218     },
36219     
36220     hide : function(){
36221         this.hideSplitter();
36222         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36223     },
36224
36225     hideSplitter : function(){
36226         if(this.split){
36227             this.split.el.setLocation(-2000,-2000);
36228             this.split.el.hide();
36229         }
36230     },
36231
36232     show : function(){
36233         if(this.split){
36234             this.split.el.show();
36235         }
36236         Roo.bootstrap.layout.Split.superclass.show.call(this);
36237     },
36238     
36239     beforeSlide: function(){
36240         if(Roo.isGecko){// firefox overflow auto bug workaround
36241             this.bodyEl.clip();
36242             if(this.tabs) {
36243                 this.tabs.bodyEl.clip();
36244             }
36245             if(this.activePanel){
36246                 this.activePanel.getEl().clip();
36247                 
36248                 if(this.activePanel.beforeSlide){
36249                     this.activePanel.beforeSlide();
36250                 }
36251             }
36252         }
36253     },
36254     
36255     afterSlide : function(){
36256         if(Roo.isGecko){// firefox overflow auto bug workaround
36257             this.bodyEl.unclip();
36258             if(this.tabs) {
36259                 this.tabs.bodyEl.unclip();
36260             }
36261             if(this.activePanel){
36262                 this.activePanel.getEl().unclip();
36263                 if(this.activePanel.afterSlide){
36264                     this.activePanel.afterSlide();
36265                 }
36266             }
36267         }
36268     },
36269
36270     initAutoHide : function(){
36271         if(this.autoHide !== false){
36272             if(!this.autoHideHd){
36273                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36274                 this.autoHideHd = {
36275                     "mouseout": function(e){
36276                         if(!e.within(this.el, true)){
36277                             st.delay(500);
36278                         }
36279                     },
36280                     "mouseover" : function(e){
36281                         st.cancel();
36282                     },
36283                     scope : this
36284                 };
36285             }
36286             this.el.on(this.autoHideHd);
36287         }
36288     },
36289
36290     clearAutoHide : function(){
36291         if(this.autoHide !== false){
36292             this.el.un("mouseout", this.autoHideHd.mouseout);
36293             this.el.un("mouseover", this.autoHideHd.mouseover);
36294         }
36295     },
36296
36297     clearMonitor : function(){
36298         Roo.get(document).un("click", this.slideInIf, this);
36299     },
36300
36301     // these names are backwards but not changed for compat
36302     slideOut : function(){
36303         if(this.isSlid || this.el.hasActiveFx()){
36304             return;
36305         }
36306         this.isSlid = true;
36307         if(this.collapseBtn){
36308             this.collapseBtn.hide();
36309         }
36310         this.closeBtnState = this.closeBtn.getStyle('display');
36311         this.closeBtn.hide();
36312         if(this.stickBtn){
36313             this.stickBtn.show();
36314         }
36315         this.el.show();
36316         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36317         this.beforeSlide();
36318         this.el.setStyle("z-index", 10001);
36319         this.el.slideIn(this.getSlideAnchor(), {
36320             callback: function(){
36321                 this.afterSlide();
36322                 this.initAutoHide();
36323                 Roo.get(document).on("click", this.slideInIf, this);
36324                 this.fireEvent("slideshow", this);
36325             },
36326             scope: this,
36327             block: true
36328         });
36329     },
36330
36331     afterSlideIn : function(){
36332         this.clearAutoHide();
36333         this.isSlid = false;
36334         this.clearMonitor();
36335         this.el.setStyle("z-index", "");
36336         if(this.collapseBtn){
36337             this.collapseBtn.show();
36338         }
36339         this.closeBtn.setStyle('display', this.closeBtnState);
36340         if(this.stickBtn){
36341             this.stickBtn.hide();
36342         }
36343         this.fireEvent("slidehide", this);
36344     },
36345
36346     slideIn : function(cb){
36347         if(!this.isSlid || this.el.hasActiveFx()){
36348             Roo.callback(cb);
36349             return;
36350         }
36351         this.isSlid = false;
36352         this.beforeSlide();
36353         this.el.slideOut(this.getSlideAnchor(), {
36354             callback: function(){
36355                 this.el.setLeftTop(-10000, -10000);
36356                 this.afterSlide();
36357                 this.afterSlideIn();
36358                 Roo.callback(cb);
36359             },
36360             scope: this,
36361             block: true
36362         });
36363     },
36364     
36365     slideInIf : function(e){
36366         if(!e.within(this.el)){
36367             this.slideIn();
36368         }
36369     },
36370
36371     animateCollapse : function(){
36372         this.beforeSlide();
36373         this.el.setStyle("z-index", 20000);
36374         var anchor = this.getSlideAnchor();
36375         this.el.slideOut(anchor, {
36376             callback : function(){
36377                 this.el.setStyle("z-index", "");
36378                 this.collapsedEl.slideIn(anchor, {duration:.3});
36379                 this.afterSlide();
36380                 this.el.setLocation(-10000,-10000);
36381                 this.el.hide();
36382                 this.fireEvent("collapsed", this);
36383             },
36384             scope: this,
36385             block: true
36386         });
36387     },
36388
36389     animateExpand : function(){
36390         this.beforeSlide();
36391         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36392         this.el.setStyle("z-index", 20000);
36393         this.collapsedEl.hide({
36394             duration:.1
36395         });
36396         this.el.slideIn(this.getSlideAnchor(), {
36397             callback : function(){
36398                 this.el.setStyle("z-index", "");
36399                 this.afterSlide();
36400                 if(this.split){
36401                     this.split.el.show();
36402                 }
36403                 this.fireEvent("invalidated", this);
36404                 this.fireEvent("expanded", this);
36405             },
36406             scope: this,
36407             block: true
36408         });
36409     },
36410
36411     anchors : {
36412         "west" : "left",
36413         "east" : "right",
36414         "north" : "top",
36415         "south" : "bottom"
36416     },
36417
36418     sanchors : {
36419         "west" : "l",
36420         "east" : "r",
36421         "north" : "t",
36422         "south" : "b"
36423     },
36424
36425     canchors : {
36426         "west" : "tl-tr",
36427         "east" : "tr-tl",
36428         "north" : "tl-bl",
36429         "south" : "bl-tl"
36430     },
36431
36432     getAnchor : function(){
36433         return this.anchors[this.position];
36434     },
36435
36436     getCollapseAnchor : function(){
36437         return this.canchors[this.position];
36438     },
36439
36440     getSlideAnchor : function(){
36441         return this.sanchors[this.position];
36442     },
36443
36444     getAlignAdj : function(){
36445         var cm = this.cmargins;
36446         switch(this.position){
36447             case "west":
36448                 return [0, 0];
36449             break;
36450             case "east":
36451                 return [0, 0];
36452             break;
36453             case "north":
36454                 return [0, 0];
36455             break;
36456             case "south":
36457                 return [0, 0];
36458             break;
36459         }
36460     },
36461
36462     getExpandAdj : function(){
36463         var c = this.collapsedEl, cm = this.cmargins;
36464         switch(this.position){
36465             case "west":
36466                 return [-(cm.right+c.getWidth()+cm.left), 0];
36467             break;
36468             case "east":
36469                 return [cm.right+c.getWidth()+cm.left, 0];
36470             break;
36471             case "north":
36472                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36473             break;
36474             case "south":
36475                 return [0, cm.top+cm.bottom+c.getHeight()];
36476             break;
36477         }
36478     }
36479 });/*
36480  * Based on:
36481  * Ext JS Library 1.1.1
36482  * Copyright(c) 2006-2007, Ext JS, LLC.
36483  *
36484  * Originally Released Under LGPL - original licence link has changed is not relivant.
36485  *
36486  * Fork - LGPL
36487  * <script type="text/javascript">
36488  */
36489 /*
36490  * These classes are private internal classes
36491  */
36492 Roo.bootstrap.layout.Center = function(config){
36493     config.region = "center";
36494     Roo.bootstrap.layout.Region.call(this, config);
36495     this.visible = true;
36496     this.minWidth = config.minWidth || 20;
36497     this.minHeight = config.minHeight || 20;
36498 };
36499
36500 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36501     hide : function(){
36502         // center panel can't be hidden
36503     },
36504     
36505     show : function(){
36506         // center panel can't be hidden
36507     },
36508     
36509     getMinWidth: function(){
36510         return this.minWidth;
36511     },
36512     
36513     getMinHeight: function(){
36514         return this.minHeight;
36515     }
36516 });
36517
36518
36519
36520
36521  
36522
36523
36524
36525
36526
36527 Roo.bootstrap.layout.North = function(config)
36528 {
36529     config.region = 'north';
36530     config.cursor = 'n-resize';
36531     
36532     Roo.bootstrap.layout.Split.call(this, config);
36533     
36534     
36535     if(this.split){
36536         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36537         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36538         this.split.el.addClass("roo-layout-split-v");
36539     }
36540     var size = config.initialSize || config.height;
36541     if(typeof size != "undefined"){
36542         this.el.setHeight(size);
36543     }
36544 };
36545 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36546 {
36547     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36548     
36549     
36550     
36551     getBox : function(){
36552         if(this.collapsed){
36553             return this.collapsedEl.getBox();
36554         }
36555         var box = this.el.getBox();
36556         if(this.split){
36557             box.height += this.split.el.getHeight();
36558         }
36559         return box;
36560     },
36561     
36562     updateBox : function(box){
36563         if(this.split && !this.collapsed){
36564             box.height -= this.split.el.getHeight();
36565             this.split.el.setLeft(box.x);
36566             this.split.el.setTop(box.y+box.height);
36567             this.split.el.setWidth(box.width);
36568         }
36569         if(this.collapsed){
36570             this.updateBody(box.width, null);
36571         }
36572         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36573     }
36574 });
36575
36576
36577
36578
36579
36580 Roo.bootstrap.layout.South = function(config){
36581     config.region = 'south';
36582     config.cursor = 's-resize';
36583     Roo.bootstrap.layout.Split.call(this, config);
36584     if(this.split){
36585         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36586         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36587         this.split.el.addClass("roo-layout-split-v");
36588     }
36589     var size = config.initialSize || config.height;
36590     if(typeof size != "undefined"){
36591         this.el.setHeight(size);
36592     }
36593 };
36594
36595 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36596     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36597     getBox : function(){
36598         if(this.collapsed){
36599             return this.collapsedEl.getBox();
36600         }
36601         var box = this.el.getBox();
36602         if(this.split){
36603             var sh = this.split.el.getHeight();
36604             box.height += sh;
36605             box.y -= sh;
36606         }
36607         return box;
36608     },
36609     
36610     updateBox : function(box){
36611         if(this.split && !this.collapsed){
36612             var sh = this.split.el.getHeight();
36613             box.height -= sh;
36614             box.y += sh;
36615             this.split.el.setLeft(box.x);
36616             this.split.el.setTop(box.y-sh);
36617             this.split.el.setWidth(box.width);
36618         }
36619         if(this.collapsed){
36620             this.updateBody(box.width, null);
36621         }
36622         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36623     }
36624 });
36625
36626 Roo.bootstrap.layout.East = function(config){
36627     config.region = "east";
36628     config.cursor = "e-resize";
36629     Roo.bootstrap.layout.Split.call(this, config);
36630     if(this.split){
36631         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36632         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36633         this.split.el.addClass("roo-layout-split-h");
36634     }
36635     var size = config.initialSize || config.width;
36636     if(typeof size != "undefined"){
36637         this.el.setWidth(size);
36638     }
36639 };
36640 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36641     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36642     getBox : function(){
36643         if(this.collapsed){
36644             return this.collapsedEl.getBox();
36645         }
36646         var box = this.el.getBox();
36647         if(this.split){
36648             var sw = this.split.el.getWidth();
36649             box.width += sw;
36650             box.x -= sw;
36651         }
36652         return box;
36653     },
36654
36655     updateBox : function(box){
36656         if(this.split && !this.collapsed){
36657             var sw = this.split.el.getWidth();
36658             box.width -= sw;
36659             this.split.el.setLeft(box.x);
36660             this.split.el.setTop(box.y);
36661             this.split.el.setHeight(box.height);
36662             box.x += sw;
36663         }
36664         if(this.collapsed){
36665             this.updateBody(null, box.height);
36666         }
36667         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36668     }
36669 });
36670
36671 Roo.bootstrap.layout.West = function(config){
36672     config.region = "west";
36673     config.cursor = "w-resize";
36674     
36675     Roo.bootstrap.layout.Split.call(this, config);
36676     if(this.split){
36677         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36678         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36679         this.split.el.addClass("roo-layout-split-h");
36680     }
36681     
36682 };
36683 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36684     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36685     
36686     onRender: function(ctr, pos)
36687     {
36688         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36689         var size = this.config.initialSize || this.config.width;
36690         if(typeof size != "undefined"){
36691             this.el.setWidth(size);
36692         }
36693     },
36694     
36695     getBox : function(){
36696         if(this.collapsed){
36697             return this.collapsedEl.getBox();
36698         }
36699         var box = this.el.getBox();
36700         if(this.split){
36701             box.width += this.split.el.getWidth();
36702         }
36703         return box;
36704     },
36705     
36706     updateBox : function(box){
36707         if(this.split && !this.collapsed){
36708             var sw = this.split.el.getWidth();
36709             box.width -= sw;
36710             this.split.el.setLeft(box.x+box.width);
36711             this.split.el.setTop(box.y);
36712             this.split.el.setHeight(box.height);
36713         }
36714         if(this.collapsed){
36715             this.updateBody(null, box.height);
36716         }
36717         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36718     }
36719 });
36720 Roo.namespace("Roo.bootstrap.panel");/*
36721  * Based on:
36722  * Ext JS Library 1.1.1
36723  * Copyright(c) 2006-2007, Ext JS, LLC.
36724  *
36725  * Originally Released Under LGPL - original licence link has changed is not relivant.
36726  *
36727  * Fork - LGPL
36728  * <script type="text/javascript">
36729  */
36730 /**
36731  * @class Roo.ContentPanel
36732  * @extends Roo.util.Observable
36733  * A basic ContentPanel element.
36734  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36735  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36736  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
36737  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36738  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36739  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36740  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36741  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36742  * @cfg {String} title          The title for this panel
36743  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36744  * @cfg {String} url            Calls {@link #setUrl} with this value
36745  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36746  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36747  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36748  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36749  * @cfg {Boolean} badges render the badges
36750
36751  * @constructor
36752  * Create a new ContentPanel.
36753  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36754  * @param {String/Object} config A string to set only the title or a config object
36755  * @param {String} content (optional) Set the HTML content for this panel
36756  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36757  */
36758 Roo.bootstrap.panel.Content = function( config){
36759     
36760     this.tpl = config.tpl || false;
36761     
36762     var el = config.el;
36763     var content = config.content;
36764
36765     if(config.autoCreate){ // xtype is available if this is called from factory
36766         el = Roo.id();
36767     }
36768     this.el = Roo.get(el);
36769     if(!this.el && config && config.autoCreate){
36770         if(typeof config.autoCreate == "object"){
36771             if(!config.autoCreate.id){
36772                 config.autoCreate.id = config.id||el;
36773             }
36774             this.el = Roo.DomHelper.append(document.body,
36775                         config.autoCreate, true);
36776         }else{
36777             var elcfg =  {   tag: "div",
36778                             cls: "roo-layout-inactive-content",
36779                             id: config.id||el
36780                             };
36781             if (config.html) {
36782                 elcfg.html = config.html;
36783                 
36784             }
36785                         
36786             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36787         }
36788     } 
36789     this.closable = false;
36790     this.loaded = false;
36791     this.active = false;
36792    
36793       
36794     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36795         
36796         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36797         
36798         this.wrapEl = this.el; //this.el.wrap();
36799         var ti = [];
36800         if (config.toolbar.items) {
36801             ti = config.toolbar.items ;
36802             delete config.toolbar.items ;
36803         }
36804         
36805         var nitems = [];
36806         this.toolbar.render(this.wrapEl, 'before');
36807         for(var i =0;i < ti.length;i++) {
36808           //  Roo.log(['add child', items[i]]);
36809             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36810         }
36811         this.toolbar.items = nitems;
36812         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36813         delete config.toolbar;
36814         
36815     }
36816     /*
36817     // xtype created footer. - not sure if will work as we normally have to render first..
36818     if (this.footer && !this.footer.el && this.footer.xtype) {
36819         if (!this.wrapEl) {
36820             this.wrapEl = this.el.wrap();
36821         }
36822     
36823         this.footer.container = this.wrapEl.createChild();
36824          
36825         this.footer = Roo.factory(this.footer, Roo);
36826         
36827     }
36828     */
36829     
36830      if(typeof config == "string"){
36831         this.title = config;
36832     }else{
36833         Roo.apply(this, config);
36834     }
36835     
36836     if(this.resizeEl){
36837         this.resizeEl = Roo.get(this.resizeEl, true);
36838     }else{
36839         this.resizeEl = this.el;
36840     }
36841     // handle view.xtype
36842     
36843  
36844     
36845     
36846     this.addEvents({
36847         /**
36848          * @event activate
36849          * Fires when this panel is activated. 
36850          * @param {Roo.ContentPanel} this
36851          */
36852         "activate" : true,
36853         /**
36854          * @event deactivate
36855          * Fires when this panel is activated. 
36856          * @param {Roo.ContentPanel} this
36857          */
36858         "deactivate" : true,
36859
36860         /**
36861          * @event resize
36862          * Fires when this panel is resized if fitToFrame is true.
36863          * @param {Roo.ContentPanel} this
36864          * @param {Number} width The width after any component adjustments
36865          * @param {Number} height The height after any component adjustments
36866          */
36867         "resize" : true,
36868         
36869          /**
36870          * @event render
36871          * Fires when this tab is created
36872          * @param {Roo.ContentPanel} this
36873          */
36874         "render" : true
36875         
36876         
36877         
36878     });
36879     
36880
36881     
36882     
36883     if(this.autoScroll){
36884         this.resizeEl.setStyle("overflow", "auto");
36885     } else {
36886         // fix randome scrolling
36887         //this.el.on('scroll', function() {
36888         //    Roo.log('fix random scolling');
36889         //    this.scrollTo('top',0); 
36890         //});
36891     }
36892     content = content || this.content;
36893     if(content){
36894         this.setContent(content);
36895     }
36896     if(config && config.url){
36897         this.setUrl(this.url, this.params, this.loadOnce);
36898     }
36899     
36900     
36901     
36902     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36903     
36904     if (this.view && typeof(this.view.xtype) != 'undefined') {
36905         this.view.el = this.el.appendChild(document.createElement("div"));
36906         this.view = Roo.factory(this.view); 
36907         this.view.render  &&  this.view.render(false, '');  
36908     }
36909     
36910     
36911     this.fireEvent('render', this);
36912 };
36913
36914 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36915     
36916     tabTip : '',
36917     
36918     setRegion : function(region){
36919         this.region = region;
36920         this.setActiveClass(region && !this.background);
36921     },
36922     
36923     
36924     setActiveClass: function(state)
36925     {
36926         if(state){
36927            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36928            this.el.setStyle('position','relative');
36929         }else{
36930            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36931            this.el.setStyle('position', 'absolute');
36932         } 
36933     },
36934     
36935     /**
36936      * Returns the toolbar for this Panel if one was configured. 
36937      * @return {Roo.Toolbar} 
36938      */
36939     getToolbar : function(){
36940         return this.toolbar;
36941     },
36942     
36943     setActiveState : function(active)
36944     {
36945         this.active = active;
36946         this.setActiveClass(active);
36947         if(!active){
36948             if(this.fireEvent("deactivate", this) === false){
36949                 return false;
36950             }
36951             return true;
36952         }
36953         this.fireEvent("activate", this);
36954         return true;
36955     },
36956     /**
36957      * Updates this panel's element
36958      * @param {String} content The new content
36959      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36960     */
36961     setContent : function(content, loadScripts){
36962         this.el.update(content, loadScripts);
36963     },
36964
36965     ignoreResize : function(w, h){
36966         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36967             return true;
36968         }else{
36969             this.lastSize = {width: w, height: h};
36970             return false;
36971         }
36972     },
36973     /**
36974      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36975      * @return {Roo.UpdateManager} The UpdateManager
36976      */
36977     getUpdateManager : function(){
36978         return this.el.getUpdateManager();
36979     },
36980      /**
36981      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36982      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
36983 <pre><code>
36984 panel.load({
36985     url: "your-url.php",
36986     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36987     callback: yourFunction,
36988     scope: yourObject, //(optional scope)
36989     discardUrl: false,
36990     nocache: false,
36991     text: "Loading...",
36992     timeout: 30,
36993     scripts: false
36994 });
36995 </code></pre>
36996      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36997      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
36998      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
36999      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37000      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
37001      * @return {Roo.ContentPanel} this
37002      */
37003     load : function(){
37004         var um = this.el.getUpdateManager();
37005         um.update.apply(um, arguments);
37006         return this;
37007     },
37008
37009
37010     /**
37011      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
37012      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37013      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
37014      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
37015      * @return {Roo.UpdateManager} The UpdateManager
37016      */
37017     setUrl : function(url, params, loadOnce){
37018         if(this.refreshDelegate){
37019             this.removeListener("activate", this.refreshDelegate);
37020         }
37021         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37022         this.on("activate", this.refreshDelegate);
37023         return this.el.getUpdateManager();
37024     },
37025     
37026     _handleRefresh : function(url, params, loadOnce){
37027         if(!loadOnce || !this.loaded){
37028             var updater = this.el.getUpdateManager();
37029             updater.update(url, params, this._setLoaded.createDelegate(this));
37030         }
37031     },
37032     
37033     _setLoaded : function(){
37034         this.loaded = true;
37035     }, 
37036     
37037     /**
37038      * Returns this panel's id
37039      * @return {String} 
37040      */
37041     getId : function(){
37042         return this.el.id;
37043     },
37044     
37045     /** 
37046      * Returns this panel's element - used by regiosn to add.
37047      * @return {Roo.Element} 
37048      */
37049     getEl : function(){
37050         return this.wrapEl || this.el;
37051     },
37052     
37053    
37054     
37055     adjustForComponents : function(width, height)
37056     {
37057         //Roo.log('adjustForComponents ');
37058         if(this.resizeEl != this.el){
37059             width -= this.el.getFrameWidth('lr');
37060             height -= this.el.getFrameWidth('tb');
37061         }
37062         if(this.toolbar){
37063             var te = this.toolbar.getEl();
37064             te.setWidth(width);
37065             height -= te.getHeight();
37066         }
37067         if(this.footer){
37068             var te = this.footer.getEl();
37069             te.setWidth(width);
37070             height -= te.getHeight();
37071         }
37072         
37073         
37074         if(this.adjustments){
37075             width += this.adjustments[0];
37076             height += this.adjustments[1];
37077         }
37078         return {"width": width, "height": height};
37079     },
37080     
37081     setSize : function(width, height){
37082         if(this.fitToFrame && !this.ignoreResize(width, height)){
37083             if(this.fitContainer && this.resizeEl != this.el){
37084                 this.el.setSize(width, height);
37085             }
37086             var size = this.adjustForComponents(width, height);
37087             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37088             this.fireEvent('resize', this, size.width, size.height);
37089         }
37090     },
37091     
37092     /**
37093      * Returns this panel's title
37094      * @return {String} 
37095      */
37096     getTitle : function(){
37097         
37098         if (typeof(this.title) != 'object') {
37099             return this.title;
37100         }
37101         
37102         var t = '';
37103         for (var k in this.title) {
37104             if (!this.title.hasOwnProperty(k)) {
37105                 continue;
37106             }
37107             
37108             if (k.indexOf('-') >= 0) {
37109                 var s = k.split('-');
37110                 for (var i = 0; i<s.length; i++) {
37111                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37112                 }
37113             } else {
37114                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37115             }
37116         }
37117         return t;
37118     },
37119     
37120     /**
37121      * Set this panel's title
37122      * @param {String} title
37123      */
37124     setTitle : function(title){
37125         this.title = title;
37126         if(this.region){
37127             this.region.updatePanelTitle(this, title);
37128         }
37129     },
37130     
37131     /**
37132      * Returns true is this panel was configured to be closable
37133      * @return {Boolean} 
37134      */
37135     isClosable : function(){
37136         return this.closable;
37137     },
37138     
37139     beforeSlide : function(){
37140         this.el.clip();
37141         this.resizeEl.clip();
37142     },
37143     
37144     afterSlide : function(){
37145         this.el.unclip();
37146         this.resizeEl.unclip();
37147     },
37148     
37149     /**
37150      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37151      *   Will fail silently if the {@link #setUrl} method has not been called.
37152      *   This does not activate the panel, just updates its content.
37153      */
37154     refresh : function(){
37155         if(this.refreshDelegate){
37156            this.loaded = false;
37157            this.refreshDelegate();
37158         }
37159     },
37160     
37161     /**
37162      * Destroys this panel
37163      */
37164     destroy : function(){
37165         this.el.removeAllListeners();
37166         var tempEl = document.createElement("span");
37167         tempEl.appendChild(this.el.dom);
37168         tempEl.innerHTML = "";
37169         this.el.remove();
37170         this.el = null;
37171     },
37172     
37173     /**
37174      * form - if the content panel contains a form - this is a reference to it.
37175      * @type {Roo.form.Form}
37176      */
37177     form : false,
37178     /**
37179      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37180      *    This contains a reference to it.
37181      * @type {Roo.View}
37182      */
37183     view : false,
37184     
37185       /**
37186      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37187      * <pre><code>
37188
37189 layout.addxtype({
37190        xtype : 'Form',
37191        items: [ .... ]
37192    }
37193 );
37194
37195 </code></pre>
37196      * @param {Object} cfg Xtype definition of item to add.
37197      */
37198     
37199     
37200     getChildContainer: function () {
37201         return this.getEl();
37202     }
37203     
37204     
37205     /*
37206         var  ret = new Roo.factory(cfg);
37207         return ret;
37208         
37209         
37210         // add form..
37211         if (cfg.xtype.match(/^Form$/)) {
37212             
37213             var el;
37214             //if (this.footer) {
37215             //    el = this.footer.container.insertSibling(false, 'before');
37216             //} else {
37217                 el = this.el.createChild();
37218             //}
37219
37220             this.form = new  Roo.form.Form(cfg);
37221             
37222             
37223             if ( this.form.allItems.length) {
37224                 this.form.render(el.dom);
37225             }
37226             return this.form;
37227         }
37228         // should only have one of theses..
37229         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37230             // views.. should not be just added - used named prop 'view''
37231             
37232             cfg.el = this.el.appendChild(document.createElement("div"));
37233             // factory?
37234             
37235             var ret = new Roo.factory(cfg);
37236              
37237              ret.render && ret.render(false, ''); // render blank..
37238             this.view = ret;
37239             return ret;
37240         }
37241         return false;
37242     }
37243     \*/
37244 });
37245  
37246 /**
37247  * @class Roo.bootstrap.panel.Grid
37248  * @extends Roo.bootstrap.panel.Content
37249  * @constructor
37250  * Create a new GridPanel.
37251  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37252  * @param {Object} config A the config object
37253   
37254  */
37255
37256
37257
37258 Roo.bootstrap.panel.Grid = function(config)
37259 {
37260     
37261       
37262     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37263         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37264
37265     config.el = this.wrapper;
37266     //this.el = this.wrapper;
37267     
37268       if (config.container) {
37269         // ctor'ed from a Border/panel.grid
37270         
37271         
37272         this.wrapper.setStyle("overflow", "hidden");
37273         this.wrapper.addClass('roo-grid-container');
37274
37275     }
37276     
37277     
37278     if(config.toolbar){
37279         var tool_el = this.wrapper.createChild();    
37280         this.toolbar = Roo.factory(config.toolbar);
37281         var ti = [];
37282         if (config.toolbar.items) {
37283             ti = config.toolbar.items ;
37284             delete config.toolbar.items ;
37285         }
37286         
37287         var nitems = [];
37288         this.toolbar.render(tool_el);
37289         for(var i =0;i < ti.length;i++) {
37290           //  Roo.log(['add child', items[i]]);
37291             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37292         }
37293         this.toolbar.items = nitems;
37294         
37295         delete config.toolbar;
37296     }
37297     
37298     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37299     config.grid.scrollBody = true;;
37300     config.grid.monitorWindowResize = false; // turn off autosizing
37301     config.grid.autoHeight = false;
37302     config.grid.autoWidth = false;
37303     
37304     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37305     
37306     if (config.background) {
37307         // render grid on panel activation (if panel background)
37308         this.on('activate', function(gp) {
37309             if (!gp.grid.rendered) {
37310                 gp.grid.render(this.wrapper);
37311                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37312             }
37313         });
37314             
37315     } else {
37316         this.grid.render(this.wrapper);
37317         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37318
37319     }
37320     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37321     // ??? needed ??? config.el = this.wrapper;
37322     
37323     
37324     
37325   
37326     // xtype created footer. - not sure if will work as we normally have to render first..
37327     if (this.footer && !this.footer.el && this.footer.xtype) {
37328         
37329         var ctr = this.grid.getView().getFooterPanel(true);
37330         this.footer.dataSource = this.grid.dataSource;
37331         this.footer = Roo.factory(this.footer, Roo);
37332         this.footer.render(ctr);
37333         
37334     }
37335     
37336     
37337     
37338     
37339      
37340 };
37341
37342 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37343     getId : function(){
37344         return this.grid.id;
37345     },
37346     
37347     /**
37348      * Returns the grid for this panel
37349      * @return {Roo.bootstrap.Table} 
37350      */
37351     getGrid : function(){
37352         return this.grid;    
37353     },
37354     
37355     setSize : function(width, height){
37356         if(!this.ignoreResize(width, height)){
37357             var grid = this.grid;
37358             var size = this.adjustForComponents(width, height);
37359             var gridel = grid.getGridEl();
37360             gridel.setSize(size.width, size.height);
37361             /*
37362             var thd = grid.getGridEl().select('thead',true).first();
37363             var tbd = grid.getGridEl().select('tbody', true).first();
37364             if (tbd) {
37365                 tbd.setSize(width, height - thd.getHeight());
37366             }
37367             */
37368             grid.autoSize();
37369         }
37370     },
37371      
37372     
37373     
37374     beforeSlide : function(){
37375         this.grid.getView().scroller.clip();
37376     },
37377     
37378     afterSlide : function(){
37379         this.grid.getView().scroller.unclip();
37380     },
37381     
37382     destroy : function(){
37383         this.grid.destroy();
37384         delete this.grid;
37385         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37386     }
37387 });
37388
37389 /**
37390  * @class Roo.bootstrap.panel.Nest
37391  * @extends Roo.bootstrap.panel.Content
37392  * @constructor
37393  * Create a new Panel, that can contain a layout.Border.
37394  * 
37395  * 
37396  * @param {Roo.BorderLayout} layout The layout for this panel
37397  * @param {String/Object} config A string to set only the title or a config object
37398  */
37399 Roo.bootstrap.panel.Nest = function(config)
37400 {
37401     // construct with only one argument..
37402     /* FIXME - implement nicer consturctors
37403     if (layout.layout) {
37404         config = layout;
37405         layout = config.layout;
37406         delete config.layout;
37407     }
37408     if (layout.xtype && !layout.getEl) {
37409         // then layout needs constructing..
37410         layout = Roo.factory(layout, Roo);
37411     }
37412     */
37413     
37414     config.el =  config.layout.getEl();
37415     
37416     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37417     
37418     config.layout.monitorWindowResize = false; // turn off autosizing
37419     this.layout = config.layout;
37420     this.layout.getEl().addClass("roo-layout-nested-layout");
37421     
37422     
37423     
37424     
37425 };
37426
37427 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37428
37429     setSize : function(width, height){
37430         if(!this.ignoreResize(width, height)){
37431             var size = this.adjustForComponents(width, height);
37432             var el = this.layout.getEl();
37433             if (size.height < 1) {
37434                 el.setWidth(size.width);   
37435             } else {
37436                 el.setSize(size.width, size.height);
37437             }
37438             var touch = el.dom.offsetWidth;
37439             this.layout.layout();
37440             // ie requires a double layout on the first pass
37441             if(Roo.isIE && !this.initialized){
37442                 this.initialized = true;
37443                 this.layout.layout();
37444             }
37445         }
37446     },
37447     
37448     // activate all subpanels if not currently active..
37449     
37450     setActiveState : function(active){
37451         this.active = active;
37452         this.setActiveClass(active);
37453         
37454         if(!active){
37455             this.fireEvent("deactivate", this);
37456             return;
37457         }
37458         
37459         this.fireEvent("activate", this);
37460         // not sure if this should happen before or after..
37461         if (!this.layout) {
37462             return; // should not happen..
37463         }
37464         var reg = false;
37465         for (var r in this.layout.regions) {
37466             reg = this.layout.getRegion(r);
37467             if (reg.getActivePanel()) {
37468                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37469                 reg.setActivePanel(reg.getActivePanel());
37470                 continue;
37471             }
37472             if (!reg.panels.length) {
37473                 continue;
37474             }
37475             reg.showPanel(reg.getPanel(0));
37476         }
37477         
37478         
37479         
37480         
37481     },
37482     
37483     /**
37484      * Returns the nested BorderLayout for this panel
37485      * @return {Roo.BorderLayout} 
37486      */
37487     getLayout : function(){
37488         return this.layout;
37489     },
37490     
37491      /**
37492      * Adds a xtype elements to the layout of the nested panel
37493      * <pre><code>
37494
37495 panel.addxtype({
37496        xtype : 'ContentPanel',
37497        region: 'west',
37498        items: [ .... ]
37499    }
37500 );
37501
37502 panel.addxtype({
37503         xtype : 'NestedLayoutPanel',
37504         region: 'west',
37505         layout: {
37506            center: { },
37507            west: { }   
37508         },
37509         items : [ ... list of content panels or nested layout panels.. ]
37510    }
37511 );
37512 </code></pre>
37513      * @param {Object} cfg Xtype definition of item to add.
37514      */
37515     addxtype : function(cfg) {
37516         return this.layout.addxtype(cfg);
37517     
37518     }
37519 });        /*
37520  * Based on:
37521  * Ext JS Library 1.1.1
37522  * Copyright(c) 2006-2007, Ext JS, LLC.
37523  *
37524  * Originally Released Under LGPL - original licence link has changed is not relivant.
37525  *
37526  * Fork - LGPL
37527  * <script type="text/javascript">
37528  */
37529 /**
37530  * @class Roo.TabPanel
37531  * @extends Roo.util.Observable
37532  * A lightweight tab container.
37533  * <br><br>
37534  * Usage:
37535  * <pre><code>
37536 // basic tabs 1, built from existing content
37537 var tabs = new Roo.TabPanel("tabs1");
37538 tabs.addTab("script", "View Script");
37539 tabs.addTab("markup", "View Markup");
37540 tabs.activate("script");
37541
37542 // more advanced tabs, built from javascript
37543 var jtabs = new Roo.TabPanel("jtabs");
37544 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37545
37546 // set up the UpdateManager
37547 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37548 var updater = tab2.getUpdateManager();
37549 updater.setDefaultUrl("ajax1.htm");
37550 tab2.on('activate', updater.refresh, updater, true);
37551
37552 // Use setUrl for Ajax loading
37553 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37554 tab3.setUrl("ajax2.htm", null, true);
37555
37556 // Disabled tab
37557 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37558 tab4.disable();
37559
37560 jtabs.activate("jtabs-1");
37561  * </code></pre>
37562  * @constructor
37563  * Create a new TabPanel.
37564  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37565  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37566  */
37567 Roo.bootstrap.panel.Tabs = function(config){
37568     /**
37569     * The container element for this TabPanel.
37570     * @type Roo.Element
37571     */
37572     this.el = Roo.get(config.el);
37573     delete config.el;
37574     if(config){
37575         if(typeof config == "boolean"){
37576             this.tabPosition = config ? "bottom" : "top";
37577         }else{
37578             Roo.apply(this, config);
37579         }
37580     }
37581     
37582     if(this.tabPosition == "bottom"){
37583         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37584         this.el.addClass("roo-tabs-bottom");
37585     }
37586     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37587     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37588     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37589     if(Roo.isIE){
37590         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37591     }
37592     if(this.tabPosition != "bottom"){
37593         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37594          * @type Roo.Element
37595          */
37596         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37597         this.el.addClass("roo-tabs-top");
37598     }
37599     this.items = [];
37600
37601     this.bodyEl.setStyle("position", "relative");
37602
37603     this.active = null;
37604     this.activateDelegate = this.activate.createDelegate(this);
37605
37606     this.addEvents({
37607         /**
37608          * @event tabchange
37609          * Fires when the active tab changes
37610          * @param {Roo.TabPanel} this
37611          * @param {Roo.TabPanelItem} activePanel The new active tab
37612          */
37613         "tabchange": true,
37614         /**
37615          * @event beforetabchange
37616          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37617          * @param {Roo.TabPanel} this
37618          * @param {Object} e Set cancel to true on this object to cancel the tab change
37619          * @param {Roo.TabPanelItem} tab The tab being changed to
37620          */
37621         "beforetabchange" : true
37622     });
37623
37624     Roo.EventManager.onWindowResize(this.onResize, this);
37625     this.cpad = this.el.getPadding("lr");
37626     this.hiddenCount = 0;
37627
37628
37629     // toolbar on the tabbar support...
37630     if (this.toolbar) {
37631         alert("no toolbar support yet");
37632         this.toolbar  = false;
37633         /*
37634         var tcfg = this.toolbar;
37635         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37636         this.toolbar = new Roo.Toolbar(tcfg);
37637         if (Roo.isSafari) {
37638             var tbl = tcfg.container.child('table', true);
37639             tbl.setAttribute('width', '100%');
37640         }
37641         */
37642         
37643     }
37644    
37645
37646
37647     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37648 };
37649
37650 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37651     /*
37652      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37653      */
37654     tabPosition : "top",
37655     /*
37656      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37657      */
37658     currentTabWidth : 0,
37659     /*
37660      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37661      */
37662     minTabWidth : 40,
37663     /*
37664      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37665      */
37666     maxTabWidth : 250,
37667     /*
37668      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37669      */
37670     preferredTabWidth : 175,
37671     /*
37672      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37673      */
37674     resizeTabs : false,
37675     /*
37676      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37677      */
37678     monitorResize : true,
37679     /*
37680      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37681      */
37682     toolbar : false,
37683
37684     /**
37685      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37686      * @param {String} id The id of the div to use <b>or create</b>
37687      * @param {String} text The text for the tab
37688      * @param {String} content (optional) Content to put in the TabPanelItem body
37689      * @param {Boolean} closable (optional) True to create a close icon on the tab
37690      * @return {Roo.TabPanelItem} The created TabPanelItem
37691      */
37692     addTab : function(id, text, content, closable, tpl)
37693     {
37694         var item = new Roo.bootstrap.panel.TabItem({
37695             panel: this,
37696             id : id,
37697             text : text,
37698             closable : closable,
37699             tpl : tpl
37700         });
37701         this.addTabItem(item);
37702         if(content){
37703             item.setContent(content);
37704         }
37705         return item;
37706     },
37707
37708     /**
37709      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37710      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37711      * @return {Roo.TabPanelItem}
37712      */
37713     getTab : function(id){
37714         return this.items[id];
37715     },
37716
37717     /**
37718      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37719      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37720      */
37721     hideTab : function(id){
37722         var t = this.items[id];
37723         if(!t.isHidden()){
37724            t.setHidden(true);
37725            this.hiddenCount++;
37726            this.autoSizeTabs();
37727         }
37728     },
37729
37730     /**
37731      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37732      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37733      */
37734     unhideTab : function(id){
37735         var t = this.items[id];
37736         if(t.isHidden()){
37737            t.setHidden(false);
37738            this.hiddenCount--;
37739            this.autoSizeTabs();
37740         }
37741     },
37742
37743     /**
37744      * Adds an existing {@link Roo.TabPanelItem}.
37745      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37746      */
37747     addTabItem : function(item){
37748         this.items[item.id] = item;
37749         this.items.push(item);
37750       //  if(this.resizeTabs){
37751     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37752   //         this.autoSizeTabs();
37753 //        }else{
37754 //            item.autoSize();
37755        // }
37756     },
37757
37758     /**
37759      * Removes a {@link Roo.TabPanelItem}.
37760      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37761      */
37762     removeTab : function(id){
37763         var items = this.items;
37764         var tab = items[id];
37765         if(!tab) { return; }
37766         var index = items.indexOf(tab);
37767         if(this.active == tab && items.length > 1){
37768             var newTab = this.getNextAvailable(index);
37769             if(newTab) {
37770                 newTab.activate();
37771             }
37772         }
37773         this.stripEl.dom.removeChild(tab.pnode.dom);
37774         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37775             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37776         }
37777         items.splice(index, 1);
37778         delete this.items[tab.id];
37779         tab.fireEvent("close", tab);
37780         tab.purgeListeners();
37781         this.autoSizeTabs();
37782     },
37783
37784     getNextAvailable : function(start){
37785         var items = this.items;
37786         var index = start;
37787         // look for a next tab that will slide over to
37788         // replace the one being removed
37789         while(index < items.length){
37790             var item = items[++index];
37791             if(item && !item.isHidden()){
37792                 return item;
37793             }
37794         }
37795         // if one isn't found select the previous tab (on the left)
37796         index = start;
37797         while(index >= 0){
37798             var item = items[--index];
37799             if(item && !item.isHidden()){
37800                 return item;
37801             }
37802         }
37803         return null;
37804     },
37805
37806     /**
37807      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37808      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37809      */
37810     disableTab : function(id){
37811         var tab = this.items[id];
37812         if(tab && this.active != tab){
37813             tab.disable();
37814         }
37815     },
37816
37817     /**
37818      * Enables a {@link Roo.TabPanelItem} that is disabled.
37819      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37820      */
37821     enableTab : function(id){
37822         var tab = this.items[id];
37823         tab.enable();
37824     },
37825
37826     /**
37827      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37828      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37829      * @return {Roo.TabPanelItem} The TabPanelItem.
37830      */
37831     activate : function(id){
37832         var tab = this.items[id];
37833         if(!tab){
37834             return null;
37835         }
37836         if(tab == this.active || tab.disabled){
37837             return tab;
37838         }
37839         var e = {};
37840         this.fireEvent("beforetabchange", this, e, tab);
37841         if(e.cancel !== true && !tab.disabled){
37842             if(this.active){
37843                 this.active.hide();
37844             }
37845             this.active = this.items[id];
37846             this.active.show();
37847             this.fireEvent("tabchange", this, this.active);
37848         }
37849         return tab;
37850     },
37851
37852     /**
37853      * Gets the active {@link Roo.TabPanelItem}.
37854      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37855      */
37856     getActiveTab : function(){
37857         return this.active;
37858     },
37859
37860     /**
37861      * Updates the tab body element to fit the height of the container element
37862      * for overflow scrolling
37863      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37864      */
37865     syncHeight : function(targetHeight){
37866         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37867         var bm = this.bodyEl.getMargins();
37868         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37869         this.bodyEl.setHeight(newHeight);
37870         return newHeight;
37871     },
37872
37873     onResize : function(){
37874         if(this.monitorResize){
37875             this.autoSizeTabs();
37876         }
37877     },
37878
37879     /**
37880      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37881      */
37882     beginUpdate : function(){
37883         this.updating = true;
37884     },
37885
37886     /**
37887      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37888      */
37889     endUpdate : function(){
37890         this.updating = false;
37891         this.autoSizeTabs();
37892     },
37893
37894     /**
37895      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37896      */
37897     autoSizeTabs : function(){
37898         var count = this.items.length;
37899         var vcount = count - this.hiddenCount;
37900         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37901             return;
37902         }
37903         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37904         var availWidth = Math.floor(w / vcount);
37905         var b = this.stripBody;
37906         if(b.getWidth() > w){
37907             var tabs = this.items;
37908             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37909             if(availWidth < this.minTabWidth){
37910                 /*if(!this.sleft){    // incomplete scrolling code
37911                     this.createScrollButtons();
37912                 }
37913                 this.showScroll();
37914                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37915             }
37916         }else{
37917             if(this.currentTabWidth < this.preferredTabWidth){
37918                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37919             }
37920         }
37921     },
37922
37923     /**
37924      * Returns the number of tabs in this TabPanel.
37925      * @return {Number}
37926      */
37927      getCount : function(){
37928          return this.items.length;
37929      },
37930
37931     /**
37932      * Resizes all the tabs to the passed width
37933      * @param {Number} The new width
37934      */
37935     setTabWidth : function(width){
37936         this.currentTabWidth = width;
37937         for(var i = 0, len = this.items.length; i < len; i++) {
37938                 if(!this.items[i].isHidden()) {
37939                 this.items[i].setWidth(width);
37940             }
37941         }
37942     },
37943
37944     /**
37945      * Destroys this TabPanel
37946      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37947      */
37948     destroy : function(removeEl){
37949         Roo.EventManager.removeResizeListener(this.onResize, this);
37950         for(var i = 0, len = this.items.length; i < len; i++){
37951             this.items[i].purgeListeners();
37952         }
37953         if(removeEl === true){
37954             this.el.update("");
37955             this.el.remove();
37956         }
37957     },
37958     
37959     createStrip : function(container)
37960     {
37961         var strip = document.createElement("nav");
37962         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37963         container.appendChild(strip);
37964         return strip;
37965     },
37966     
37967     createStripList : function(strip)
37968     {
37969         // div wrapper for retard IE
37970         // returns the "tr" element.
37971         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37972         //'<div class="x-tabs-strip-wrap">'+
37973           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37974           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37975         return strip.firstChild; //.firstChild.firstChild.firstChild;
37976     },
37977     createBody : function(container)
37978     {
37979         var body = document.createElement("div");
37980         Roo.id(body, "tab-body");
37981         //Roo.fly(body).addClass("x-tabs-body");
37982         Roo.fly(body).addClass("tab-content");
37983         container.appendChild(body);
37984         return body;
37985     },
37986     createItemBody :function(bodyEl, id){
37987         var body = Roo.getDom(id);
37988         if(!body){
37989             body = document.createElement("div");
37990             body.id = id;
37991         }
37992         //Roo.fly(body).addClass("x-tabs-item-body");
37993         Roo.fly(body).addClass("tab-pane");
37994          bodyEl.insertBefore(body, bodyEl.firstChild);
37995         return body;
37996     },
37997     /** @private */
37998     createStripElements :  function(stripEl, text, closable, tpl)
37999     {
38000         var td = document.createElement("li"); // was td..
38001         
38002         
38003         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38004         
38005         
38006         stripEl.appendChild(td);
38007         /*if(closable){
38008             td.className = "x-tabs-closable";
38009             if(!this.closeTpl){
38010                 this.closeTpl = new Roo.Template(
38011                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38012                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38013                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38014                 );
38015             }
38016             var el = this.closeTpl.overwrite(td, {"text": text});
38017             var close = el.getElementsByTagName("div")[0];
38018             var inner = el.getElementsByTagName("em")[0];
38019             return {"el": el, "close": close, "inner": inner};
38020         } else {
38021         */
38022         // not sure what this is..
38023 //            if(!this.tabTpl){
38024                 //this.tabTpl = new Roo.Template(
38025                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38026                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38027                 //);
38028 //                this.tabTpl = new Roo.Template(
38029 //                   '<a href="#">' +
38030 //                   '<span unselectable="on"' +
38031 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38032 //                            ' >{text}</span></a>'
38033 //                );
38034 //                
38035 //            }
38036
38037
38038             var template = tpl || this.tabTpl || false;
38039             
38040             if(!template){
38041                 
38042                 template = new Roo.Template(
38043                    '<a href="#">' +
38044                    '<span unselectable="on"' +
38045                             (this.disableTooltips ? '' : ' title="{text}"') +
38046                             ' >{text}</span></a>'
38047                 );
38048             }
38049             
38050             switch (typeof(template)) {
38051                 case 'object' :
38052                     break;
38053                 case 'string' :
38054                     template = new Roo.Template(template);
38055                     break;
38056                 default :
38057                     break;
38058             }
38059             
38060             var el = template.overwrite(td, {"text": text});
38061             
38062             var inner = el.getElementsByTagName("span")[0];
38063             
38064             return {"el": el, "inner": inner};
38065             
38066     }
38067         
38068     
38069 });
38070
38071 /**
38072  * @class Roo.TabPanelItem
38073  * @extends Roo.util.Observable
38074  * Represents an individual item (tab plus body) in a TabPanel.
38075  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38076  * @param {String} id The id of this TabPanelItem
38077  * @param {String} text The text for the tab of this TabPanelItem
38078  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38079  */
38080 Roo.bootstrap.panel.TabItem = function(config){
38081     /**
38082      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38083      * @type Roo.TabPanel
38084      */
38085     this.tabPanel = config.panel;
38086     /**
38087      * The id for this TabPanelItem
38088      * @type String
38089      */
38090     this.id = config.id;
38091     /** @private */
38092     this.disabled = false;
38093     /** @private */
38094     this.text = config.text;
38095     /** @private */
38096     this.loaded = false;
38097     this.closable = config.closable;
38098
38099     /**
38100      * The body element for this TabPanelItem.
38101      * @type Roo.Element
38102      */
38103     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38104     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38105     this.bodyEl.setStyle("display", "block");
38106     this.bodyEl.setStyle("zoom", "1");
38107     //this.hideAction();
38108
38109     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38110     /** @private */
38111     this.el = Roo.get(els.el);
38112     this.inner = Roo.get(els.inner, true);
38113     this.textEl = Roo.get(this.el.dom.firstChild, true);
38114     this.pnode = Roo.get(els.el.parentNode, true);
38115 //    this.el.on("mousedown", this.onTabMouseDown, this);
38116     this.el.on("click", this.onTabClick, this);
38117     /** @private */
38118     if(config.closable){
38119         var c = Roo.get(els.close, true);
38120         c.dom.title = this.closeText;
38121         c.addClassOnOver("close-over");
38122         c.on("click", this.closeClick, this);
38123      }
38124
38125     this.addEvents({
38126          /**
38127          * @event activate
38128          * Fires when this tab becomes the active tab.
38129          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38130          * @param {Roo.TabPanelItem} this
38131          */
38132         "activate": true,
38133         /**
38134          * @event beforeclose
38135          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38136          * @param {Roo.TabPanelItem} this
38137          * @param {Object} e Set cancel to true on this object to cancel the close.
38138          */
38139         "beforeclose": true,
38140         /**
38141          * @event close
38142          * Fires when this tab is closed.
38143          * @param {Roo.TabPanelItem} this
38144          */
38145          "close": true,
38146         /**
38147          * @event deactivate
38148          * Fires when this tab is no longer the active tab.
38149          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38150          * @param {Roo.TabPanelItem} this
38151          */
38152          "deactivate" : true
38153     });
38154     this.hidden = false;
38155
38156     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38157 };
38158
38159 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38160            {
38161     purgeListeners : function(){
38162        Roo.util.Observable.prototype.purgeListeners.call(this);
38163        this.el.removeAllListeners();
38164     },
38165     /**
38166      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38167      */
38168     show : function(){
38169         this.pnode.addClass("active");
38170         this.showAction();
38171         if(Roo.isOpera){
38172             this.tabPanel.stripWrap.repaint();
38173         }
38174         this.fireEvent("activate", this.tabPanel, this);
38175     },
38176
38177     /**
38178      * Returns true if this tab is the active tab.
38179      * @return {Boolean}
38180      */
38181     isActive : function(){
38182         return this.tabPanel.getActiveTab() == this;
38183     },
38184
38185     /**
38186      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38187      */
38188     hide : function(){
38189         this.pnode.removeClass("active");
38190         this.hideAction();
38191         this.fireEvent("deactivate", this.tabPanel, this);
38192     },
38193
38194     hideAction : function(){
38195         this.bodyEl.hide();
38196         this.bodyEl.setStyle("position", "absolute");
38197         this.bodyEl.setLeft("-20000px");
38198         this.bodyEl.setTop("-20000px");
38199     },
38200
38201     showAction : function(){
38202         this.bodyEl.setStyle("position", "relative");
38203         this.bodyEl.setTop("");
38204         this.bodyEl.setLeft("");
38205         this.bodyEl.show();
38206     },
38207
38208     /**
38209      * Set the tooltip for the tab.
38210      * @param {String} tooltip The tab's tooltip
38211      */
38212     setTooltip : function(text){
38213         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38214             this.textEl.dom.qtip = text;
38215             this.textEl.dom.removeAttribute('title');
38216         }else{
38217             this.textEl.dom.title = text;
38218         }
38219     },
38220
38221     onTabClick : function(e){
38222         e.preventDefault();
38223         this.tabPanel.activate(this.id);
38224     },
38225
38226     onTabMouseDown : function(e){
38227         e.preventDefault();
38228         this.tabPanel.activate(this.id);
38229     },
38230 /*
38231     getWidth : function(){
38232         return this.inner.getWidth();
38233     },
38234
38235     setWidth : function(width){
38236         var iwidth = width - this.pnode.getPadding("lr");
38237         this.inner.setWidth(iwidth);
38238         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38239         this.pnode.setWidth(width);
38240     },
38241 */
38242     /**
38243      * Show or hide the tab
38244      * @param {Boolean} hidden True to hide or false to show.
38245      */
38246     setHidden : function(hidden){
38247         this.hidden = hidden;
38248         this.pnode.setStyle("display", hidden ? "none" : "");
38249     },
38250
38251     /**
38252      * Returns true if this tab is "hidden"
38253      * @return {Boolean}
38254      */
38255     isHidden : function(){
38256         return this.hidden;
38257     },
38258
38259     /**
38260      * Returns the text for this tab
38261      * @return {String}
38262      */
38263     getText : function(){
38264         return this.text;
38265     },
38266     /*
38267     autoSize : function(){
38268         //this.el.beginMeasure();
38269         this.textEl.setWidth(1);
38270         /*
38271          *  #2804 [new] Tabs in Roojs
38272          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38273          */
38274         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38275         //this.el.endMeasure();
38276     //},
38277
38278     /**
38279      * Sets the text for the tab (Note: this also sets the tooltip text)
38280      * @param {String} text The tab's text and tooltip
38281      */
38282     setText : function(text){
38283         this.text = text;
38284         this.textEl.update(text);
38285         this.setTooltip(text);
38286         //if(!this.tabPanel.resizeTabs){
38287         //    this.autoSize();
38288         //}
38289     },
38290     /**
38291      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38292      */
38293     activate : function(){
38294         this.tabPanel.activate(this.id);
38295     },
38296
38297     /**
38298      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38299      */
38300     disable : function(){
38301         if(this.tabPanel.active != this){
38302             this.disabled = true;
38303             this.pnode.addClass("disabled");
38304         }
38305     },
38306
38307     /**
38308      * Enables this TabPanelItem if it was previously disabled.
38309      */
38310     enable : function(){
38311         this.disabled = false;
38312         this.pnode.removeClass("disabled");
38313     },
38314
38315     /**
38316      * Sets the content for this TabPanelItem.
38317      * @param {String} content The content
38318      * @param {Boolean} loadScripts true to look for and load scripts
38319      */
38320     setContent : function(content, loadScripts){
38321         this.bodyEl.update(content, loadScripts);
38322     },
38323
38324     /**
38325      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38326      * @return {Roo.UpdateManager} The UpdateManager
38327      */
38328     getUpdateManager : function(){
38329         return this.bodyEl.getUpdateManager();
38330     },
38331
38332     /**
38333      * Set a URL to be used to load the content for this TabPanelItem.
38334      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38335      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
38336      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
38337      * @return {Roo.UpdateManager} The UpdateManager
38338      */
38339     setUrl : function(url, params, loadOnce){
38340         if(this.refreshDelegate){
38341             this.un('activate', this.refreshDelegate);
38342         }
38343         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38344         this.on("activate", this.refreshDelegate);
38345         return this.bodyEl.getUpdateManager();
38346     },
38347
38348     /** @private */
38349     _handleRefresh : function(url, params, loadOnce){
38350         if(!loadOnce || !this.loaded){
38351             var updater = this.bodyEl.getUpdateManager();
38352             updater.update(url, params, this._setLoaded.createDelegate(this));
38353         }
38354     },
38355
38356     /**
38357      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38358      *   Will fail silently if the setUrl method has not been called.
38359      *   This does not activate the panel, just updates its content.
38360      */
38361     refresh : function(){
38362         if(this.refreshDelegate){
38363            this.loaded = false;
38364            this.refreshDelegate();
38365         }
38366     },
38367
38368     /** @private */
38369     _setLoaded : function(){
38370         this.loaded = true;
38371     },
38372
38373     /** @private */
38374     closeClick : function(e){
38375         var o = {};
38376         e.stopEvent();
38377         this.fireEvent("beforeclose", this, o);
38378         if(o.cancel !== true){
38379             this.tabPanel.removeTab(this.id);
38380         }
38381     },
38382     /**
38383      * The text displayed in the tooltip for the close icon.
38384      * @type String
38385      */
38386     closeText : "Close this tab"
38387 });
38388 /**
38389 *    This script refer to:
38390 *    Title: International Telephone Input
38391 *    Author: Jack O'Connor
38392 *    Code version:  v12.1.12
38393 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38394 **/
38395
38396 Roo.bootstrap.PhoneInputData = function() {
38397     var d = [
38398       [
38399         "Afghanistan (‫افغانستان‬‎)",
38400         "af",
38401         "93"
38402       ],
38403       [
38404         "Albania (Shqipëri)",
38405         "al",
38406         "355"
38407       ],
38408       [
38409         "Algeria (‫الجزائر‬‎)",
38410         "dz",
38411         "213"
38412       ],
38413       [
38414         "American Samoa",
38415         "as",
38416         "1684"
38417       ],
38418       [
38419         "Andorra",
38420         "ad",
38421         "376"
38422       ],
38423       [
38424         "Angola",
38425         "ao",
38426         "244"
38427       ],
38428       [
38429         "Anguilla",
38430         "ai",
38431         "1264"
38432       ],
38433       [
38434         "Antigua and Barbuda",
38435         "ag",
38436         "1268"
38437       ],
38438       [
38439         "Argentina",
38440         "ar",
38441         "54"
38442       ],
38443       [
38444         "Armenia (Հայաստան)",
38445         "am",
38446         "374"
38447       ],
38448       [
38449         "Aruba",
38450         "aw",
38451         "297"
38452       ],
38453       [
38454         "Australia",
38455         "au",
38456         "61",
38457         0
38458       ],
38459       [
38460         "Austria (Österreich)",
38461         "at",
38462         "43"
38463       ],
38464       [
38465         "Azerbaijan (Azərbaycan)",
38466         "az",
38467         "994"
38468       ],
38469       [
38470         "Bahamas",
38471         "bs",
38472         "1242"
38473       ],
38474       [
38475         "Bahrain (‫البحرين‬‎)",
38476         "bh",
38477         "973"
38478       ],
38479       [
38480         "Bangladesh (বাংলাদেশ)",
38481         "bd",
38482         "880"
38483       ],
38484       [
38485         "Barbados",
38486         "bb",
38487         "1246"
38488       ],
38489       [
38490         "Belarus (Беларусь)",
38491         "by",
38492         "375"
38493       ],
38494       [
38495         "Belgium (België)",
38496         "be",
38497         "32"
38498       ],
38499       [
38500         "Belize",
38501         "bz",
38502         "501"
38503       ],
38504       [
38505         "Benin (Bénin)",
38506         "bj",
38507         "229"
38508       ],
38509       [
38510         "Bermuda",
38511         "bm",
38512         "1441"
38513       ],
38514       [
38515         "Bhutan (འབྲུག)",
38516         "bt",
38517         "975"
38518       ],
38519       [
38520         "Bolivia",
38521         "bo",
38522         "591"
38523       ],
38524       [
38525         "Bosnia and Herzegovina (Босна и Херцеговина)",
38526         "ba",
38527         "387"
38528       ],
38529       [
38530         "Botswana",
38531         "bw",
38532         "267"
38533       ],
38534       [
38535         "Brazil (Brasil)",
38536         "br",
38537         "55"
38538       ],
38539       [
38540         "British Indian Ocean Territory",
38541         "io",
38542         "246"
38543       ],
38544       [
38545         "British Virgin Islands",
38546         "vg",
38547         "1284"
38548       ],
38549       [
38550         "Brunei",
38551         "bn",
38552         "673"
38553       ],
38554       [
38555         "Bulgaria (България)",
38556         "bg",
38557         "359"
38558       ],
38559       [
38560         "Burkina Faso",
38561         "bf",
38562         "226"
38563       ],
38564       [
38565         "Burundi (Uburundi)",
38566         "bi",
38567         "257"
38568       ],
38569       [
38570         "Cambodia (កម្ពុជា)",
38571         "kh",
38572         "855"
38573       ],
38574       [
38575         "Cameroon (Cameroun)",
38576         "cm",
38577         "237"
38578       ],
38579       [
38580         "Canada",
38581         "ca",
38582         "1",
38583         1,
38584         ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
38585       ],
38586       [
38587         "Cape Verde (Kabu Verdi)",
38588         "cv",
38589         "238"
38590       ],
38591       [
38592         "Caribbean Netherlands",
38593         "bq",
38594         "599",
38595         1
38596       ],
38597       [
38598         "Cayman Islands",
38599         "ky",
38600         "1345"
38601       ],
38602       [
38603         "Central African Republic (République centrafricaine)",
38604         "cf",
38605         "236"
38606       ],
38607       [
38608         "Chad (Tchad)",
38609         "td",
38610         "235"
38611       ],
38612       [
38613         "Chile",
38614         "cl",
38615         "56"
38616       ],
38617       [
38618         "China (中国)",
38619         "cn",
38620         "86"
38621       ],
38622       [
38623         "Christmas Island",
38624         "cx",
38625         "61",
38626         2
38627       ],
38628       [
38629         "Cocos (Keeling) Islands",
38630         "cc",
38631         "61",
38632         1
38633       ],
38634       [
38635         "Colombia",
38636         "co",
38637         "57"
38638       ],
38639       [
38640         "Comoros (‫جزر القمر‬‎)",
38641         "km",
38642         "269"
38643       ],
38644       [
38645         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38646         "cd",
38647         "243"
38648       ],
38649       [
38650         "Congo (Republic) (Congo-Brazzaville)",
38651         "cg",
38652         "242"
38653       ],
38654       [
38655         "Cook Islands",
38656         "ck",
38657         "682"
38658       ],
38659       [
38660         "Costa Rica",
38661         "cr",
38662         "506"
38663       ],
38664       [
38665         "Côte d’Ivoire",
38666         "ci",
38667         "225"
38668       ],
38669       [
38670         "Croatia (Hrvatska)",
38671         "hr",
38672         "385"
38673       ],
38674       [
38675         "Cuba",
38676         "cu",
38677         "53"
38678       ],
38679       [
38680         "Curaçao",
38681         "cw",
38682         "599",
38683         0
38684       ],
38685       [
38686         "Cyprus (Κύπρος)",
38687         "cy",
38688         "357"
38689       ],
38690       [
38691         "Czech Republic (Česká republika)",
38692         "cz",
38693         "420"
38694       ],
38695       [
38696         "Denmark (Danmark)",
38697         "dk",
38698         "45"
38699       ],
38700       [
38701         "Djibouti",
38702         "dj",
38703         "253"
38704       ],
38705       [
38706         "Dominica",
38707         "dm",
38708         "1767"
38709       ],
38710       [
38711         "Dominican Republic (República Dominicana)",
38712         "do",
38713         "1",
38714         2,
38715         ["809", "829", "849"]
38716       ],
38717       [
38718         "Ecuador",
38719         "ec",
38720         "593"
38721       ],
38722       [
38723         "Egypt (‫مصر‬‎)",
38724         "eg",
38725         "20"
38726       ],
38727       [
38728         "El Salvador",
38729         "sv",
38730         "503"
38731       ],
38732       [
38733         "Equatorial Guinea (Guinea Ecuatorial)",
38734         "gq",
38735         "240"
38736       ],
38737       [
38738         "Eritrea",
38739         "er",
38740         "291"
38741       ],
38742       [
38743         "Estonia (Eesti)",
38744         "ee",
38745         "372"
38746       ],
38747       [
38748         "Ethiopia",
38749         "et",
38750         "251"
38751       ],
38752       [
38753         "Falkland Islands (Islas Malvinas)",
38754         "fk",
38755         "500"
38756       ],
38757       [
38758         "Faroe Islands (Føroyar)",
38759         "fo",
38760         "298"
38761       ],
38762       [
38763         "Fiji",
38764         "fj",
38765         "679"
38766       ],
38767       [
38768         "Finland (Suomi)",
38769         "fi",
38770         "358",
38771         0
38772       ],
38773       [
38774         "France",
38775         "fr",
38776         "33"
38777       ],
38778       [
38779         "French Guiana (Guyane française)",
38780         "gf",
38781         "594"
38782       ],
38783       [
38784         "French Polynesia (Polynésie française)",
38785         "pf",
38786         "689"
38787       ],
38788       [
38789         "Gabon",
38790         "ga",
38791         "241"
38792       ],
38793       [
38794         "Gambia",
38795         "gm",
38796         "220"
38797       ],
38798       [
38799         "Georgia (საქართველო)",
38800         "ge",
38801         "995"
38802       ],
38803       [
38804         "Germany (Deutschland)",
38805         "de",
38806         "49"
38807       ],
38808       [
38809         "Ghana (Gaana)",
38810         "gh",
38811         "233"
38812       ],
38813       [
38814         "Gibraltar",
38815         "gi",
38816         "350"
38817       ],
38818       [
38819         "Greece (Ελλάδα)",
38820         "gr",
38821         "30"
38822       ],
38823       [
38824         "Greenland (Kalaallit Nunaat)",
38825         "gl",
38826         "299"
38827       ],
38828       [
38829         "Grenada",
38830         "gd",
38831         "1473"
38832       ],
38833       [
38834         "Guadeloupe",
38835         "gp",
38836         "590",
38837         0
38838       ],
38839       [
38840         "Guam",
38841         "gu",
38842         "1671"
38843       ],
38844       [
38845         "Guatemala",
38846         "gt",
38847         "502"
38848       ],
38849       [
38850         "Guernsey",
38851         "gg",
38852         "44",
38853         1
38854       ],
38855       [
38856         "Guinea (Guinée)",
38857         "gn",
38858         "224"
38859       ],
38860       [
38861         "Guinea-Bissau (Guiné Bissau)",
38862         "gw",
38863         "245"
38864       ],
38865       [
38866         "Guyana",
38867         "gy",
38868         "592"
38869       ],
38870       [
38871         "Haiti",
38872         "ht",
38873         "509"
38874       ],
38875       [
38876         "Honduras",
38877         "hn",
38878         "504"
38879       ],
38880       [
38881         "Hong Kong (香港)",
38882         "hk",
38883         "852"
38884       ],
38885       [
38886         "Hungary (Magyarország)",
38887         "hu",
38888         "36"
38889       ],
38890       [
38891         "Iceland (Ísland)",
38892         "is",
38893         "354"
38894       ],
38895       [
38896         "India (भारत)",
38897         "in",
38898         "91"
38899       ],
38900       [
38901         "Indonesia",
38902         "id",
38903         "62"
38904       ],
38905       [
38906         "Iran (‫ایران‬‎)",
38907         "ir",
38908         "98"
38909       ],
38910       [
38911         "Iraq (‫العراق‬‎)",
38912         "iq",
38913         "964"
38914       ],
38915       [
38916         "Ireland",
38917         "ie",
38918         "353"
38919       ],
38920       [
38921         "Isle of Man",
38922         "im",
38923         "44",
38924         2
38925       ],
38926       [
38927         "Israel (‫ישראל‬‎)",
38928         "il",
38929         "972"
38930       ],
38931       [
38932         "Italy (Italia)",
38933         "it",
38934         "39",
38935         0
38936       ],
38937       [
38938         "Jamaica",
38939         "jm",
38940         "1876"
38941       ],
38942       [
38943         "Japan (日本)",
38944         "jp",
38945         "81"
38946       ],
38947       [
38948         "Jersey",
38949         "je",
38950         "44",
38951         3
38952       ],
38953       [
38954         "Jordan (‫الأردن‬‎)",
38955         "jo",
38956         "962"
38957       ],
38958       [
38959         "Kazakhstan (Казахстан)",
38960         "kz",
38961         "7",
38962         1
38963       ],
38964       [
38965         "Kenya",
38966         "ke",
38967         "254"
38968       ],
38969       [
38970         "Kiribati",
38971         "ki",
38972         "686"
38973       ],
38974       [
38975         "Kosovo",
38976         "xk",
38977         "383"
38978       ],
38979       [
38980         "Kuwait (‫الكويت‬‎)",
38981         "kw",
38982         "965"
38983       ],
38984       [
38985         "Kyrgyzstan (Кыргызстан)",
38986         "kg",
38987         "996"
38988       ],
38989       [
38990         "Laos (ລາວ)",
38991         "la",
38992         "856"
38993       ],
38994       [
38995         "Latvia (Latvija)",
38996         "lv",
38997         "371"
38998       ],
38999       [
39000         "Lebanon (‫لبنان‬‎)",
39001         "lb",
39002         "961"
39003       ],
39004       [
39005         "Lesotho",
39006         "ls",
39007         "266"
39008       ],
39009       [
39010         "Liberia",
39011         "lr",
39012         "231"
39013       ],
39014       [
39015         "Libya (‫ليبيا‬‎)",
39016         "ly",
39017         "218"
39018       ],
39019       [
39020         "Liechtenstein",
39021         "li",
39022         "423"
39023       ],
39024       [
39025         "Lithuania (Lietuva)",
39026         "lt",
39027         "370"
39028       ],
39029       [
39030         "Luxembourg",
39031         "lu",
39032         "352"
39033       ],
39034       [
39035         "Macau (澳門)",
39036         "mo",
39037         "853"
39038       ],
39039       [
39040         "Macedonia (FYROM) (Македонија)",
39041         "mk",
39042         "389"
39043       ],
39044       [
39045         "Madagascar (Madagasikara)",
39046         "mg",
39047         "261"
39048       ],
39049       [
39050         "Malawi",
39051         "mw",
39052         "265"
39053       ],
39054       [
39055         "Malaysia",
39056         "my",
39057         "60"
39058       ],
39059       [
39060         "Maldives",
39061         "mv",
39062         "960"
39063       ],
39064       [
39065         "Mali",
39066         "ml",
39067         "223"
39068       ],
39069       [
39070         "Malta",
39071         "mt",
39072         "356"
39073       ],
39074       [
39075         "Marshall Islands",
39076         "mh",
39077         "692"
39078       ],
39079       [
39080         "Martinique",
39081         "mq",
39082         "596"
39083       ],
39084       [
39085         "Mauritania (‫موريتانيا‬‎)",
39086         "mr",
39087         "222"
39088       ],
39089       [
39090         "Mauritius (Moris)",
39091         "mu",
39092         "230"
39093       ],
39094       [
39095         "Mayotte",
39096         "yt",
39097         "262",
39098         1
39099       ],
39100       [
39101         "Mexico (México)",
39102         "mx",
39103         "52"
39104       ],
39105       [
39106         "Micronesia",
39107         "fm",
39108         "691"
39109       ],
39110       [
39111         "Moldova (Republica Moldova)",
39112         "md",
39113         "373"
39114       ],
39115       [
39116         "Monaco",
39117         "mc",
39118         "377"
39119       ],
39120       [
39121         "Mongolia (Монгол)",
39122         "mn",
39123         "976"
39124       ],
39125       [
39126         "Montenegro (Crna Gora)",
39127         "me",
39128         "382"
39129       ],
39130       [
39131         "Montserrat",
39132         "ms",
39133         "1664"
39134       ],
39135       [
39136         "Morocco (‫المغرب‬‎)",
39137         "ma",
39138         "212",
39139         0
39140       ],
39141       [
39142         "Mozambique (Moçambique)",
39143         "mz",
39144         "258"
39145       ],
39146       [
39147         "Myanmar (Burma) (မြန်မာ)",
39148         "mm",
39149         "95"
39150       ],
39151       [
39152         "Namibia (Namibië)",
39153         "na",
39154         "264"
39155       ],
39156       [
39157         "Nauru",
39158         "nr",
39159         "674"
39160       ],
39161       [
39162         "Nepal (नेपाल)",
39163         "np",
39164         "977"
39165       ],
39166       [
39167         "Netherlands (Nederland)",
39168         "nl",
39169         "31"
39170       ],
39171       [
39172         "New Caledonia (Nouvelle-Calédonie)",
39173         "nc",
39174         "687"
39175       ],
39176       [
39177         "New Zealand",
39178         "nz",
39179         "64"
39180       ],
39181       [
39182         "Nicaragua",
39183         "ni",
39184         "505"
39185       ],
39186       [
39187         "Niger (Nijar)",
39188         "ne",
39189         "227"
39190       ],
39191       [
39192         "Nigeria",
39193         "ng",
39194         "234"
39195       ],
39196       [
39197         "Niue",
39198         "nu",
39199         "683"
39200       ],
39201       [
39202         "Norfolk Island",
39203         "nf",
39204         "672"
39205       ],
39206       [
39207         "North Korea (조선 민주주의 인민 공화국)",
39208         "kp",
39209         "850"
39210       ],
39211       [
39212         "Northern Mariana Islands",
39213         "mp",
39214         "1670"
39215       ],
39216       [
39217         "Norway (Norge)",
39218         "no",
39219         "47",
39220         0
39221       ],
39222       [
39223         "Oman (‫عُمان‬‎)",
39224         "om",
39225         "968"
39226       ],
39227       [
39228         "Pakistan (‫پاکستان‬‎)",
39229         "pk",
39230         "92"
39231       ],
39232       [
39233         "Palau",
39234         "pw",
39235         "680"
39236       ],
39237       [
39238         "Palestine (‫فلسطين‬‎)",
39239         "ps",
39240         "970"
39241       ],
39242       [
39243         "Panama (Panamá)",
39244         "pa",
39245         "507"
39246       ],
39247       [
39248         "Papua New Guinea",
39249         "pg",
39250         "675"
39251       ],
39252       [
39253         "Paraguay",
39254         "py",
39255         "595"
39256       ],
39257       [
39258         "Peru (Perú)",
39259         "pe",
39260         "51"
39261       ],
39262       [
39263         "Philippines",
39264         "ph",
39265         "63"
39266       ],
39267       [
39268         "Poland (Polska)",
39269         "pl",
39270         "48"
39271       ],
39272       [
39273         "Portugal",
39274         "pt",
39275         "351"
39276       ],
39277       [
39278         "Puerto Rico",
39279         "pr",
39280         "1",
39281         3,
39282         ["787", "939"]
39283       ],
39284       [
39285         "Qatar (‫قطر‬‎)",
39286         "qa",
39287         "974"
39288       ],
39289       [
39290         "Réunion (La Réunion)",
39291         "re",
39292         "262",
39293         0
39294       ],
39295       [
39296         "Romania (România)",
39297         "ro",
39298         "40"
39299       ],
39300       [
39301         "Russia (Россия)",
39302         "ru",
39303         "7",
39304         0
39305       ],
39306       [
39307         "Rwanda",
39308         "rw",
39309         "250"
39310       ],
39311       [
39312         "Saint Barthélemy",
39313         "bl",
39314         "590",
39315         1
39316       ],
39317       [
39318         "Saint Helena",
39319         "sh",
39320         "290"
39321       ],
39322       [
39323         "Saint Kitts and Nevis",
39324         "kn",
39325         "1869"
39326       ],
39327       [
39328         "Saint Lucia",
39329         "lc",
39330         "1758"
39331       ],
39332       [
39333         "Saint Martin (Saint-Martin (partie française))",
39334         "mf",
39335         "590",
39336         2
39337       ],
39338       [
39339         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39340         "pm",
39341         "508"
39342       ],
39343       [
39344         "Saint Vincent and the Grenadines",
39345         "vc",
39346         "1784"
39347       ],
39348       [
39349         "Samoa",
39350         "ws",
39351         "685"
39352       ],
39353       [
39354         "San Marino",
39355         "sm",
39356         "378"
39357       ],
39358       [
39359         "São Tomé and Príncipe (São Tomé e Príncipe)",
39360         "st",
39361         "239"
39362       ],
39363       [
39364         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39365         "sa",
39366         "966"
39367       ],
39368       [
39369         "Senegal (Sénégal)",
39370         "sn",
39371         "221"
39372       ],
39373       [
39374         "Serbia (Србија)",
39375         "rs",
39376         "381"
39377       ],
39378       [
39379         "Seychelles",
39380         "sc",
39381         "248"
39382       ],
39383       [
39384         "Sierra Leone",
39385         "sl",
39386         "232"
39387       ],
39388       [
39389         "Singapore",
39390         "sg",
39391         "65"
39392       ],
39393       [
39394         "Sint Maarten",
39395         "sx",
39396         "1721"
39397       ],
39398       [
39399         "Slovakia (Slovensko)",
39400         "sk",
39401         "421"
39402       ],
39403       [
39404         "Slovenia (Slovenija)",
39405         "si",
39406         "386"
39407       ],
39408       [
39409         "Solomon Islands",
39410         "sb",
39411         "677"
39412       ],
39413       [
39414         "Somalia (Soomaaliya)",
39415         "so",
39416         "252"
39417       ],
39418       [
39419         "South Africa",
39420         "za",
39421         "27"
39422       ],
39423       [
39424         "South Korea (대한민국)",
39425         "kr",
39426         "82"
39427       ],
39428       [
39429         "South Sudan (‫جنوب السودان‬‎)",
39430         "ss",
39431         "211"
39432       ],
39433       [
39434         "Spain (España)",
39435         "es",
39436         "34"
39437       ],
39438       [
39439         "Sri Lanka (ශ්‍රී ලංකාව)",
39440         "lk",
39441         "94"
39442       ],
39443       [
39444         "Sudan (‫السودان‬‎)",
39445         "sd",
39446         "249"
39447       ],
39448       [
39449         "Suriname",
39450         "sr",
39451         "597"
39452       ],
39453       [
39454         "Svalbard and Jan Mayen",
39455         "sj",
39456         "47",
39457         1
39458       ],
39459       [
39460         "Swaziland",
39461         "sz",
39462         "268"
39463       ],
39464       [
39465         "Sweden (Sverige)",
39466         "se",
39467         "46"
39468       ],
39469       [
39470         "Switzerland (Schweiz)",
39471         "ch",
39472         "41"
39473       ],
39474       [
39475         "Syria (‫سوريا‬‎)",
39476         "sy",
39477         "963"
39478       ],
39479       [
39480         "Taiwan (台灣)",
39481         "tw",
39482         "886"
39483       ],
39484       [
39485         "Tajikistan",
39486         "tj",
39487         "992"
39488       ],
39489       [
39490         "Tanzania",
39491         "tz",
39492         "255"
39493       ],
39494       [
39495         "Thailand (ไทย)",
39496         "th",
39497         "66"
39498       ],
39499       [
39500         "Timor-Leste",
39501         "tl",
39502         "670"
39503       ],
39504       [
39505         "Togo",
39506         "tg",
39507         "228"
39508       ],
39509       [
39510         "Tokelau",
39511         "tk",
39512         "690"
39513       ],
39514       [
39515         "Tonga",
39516         "to",
39517         "676"
39518       ],
39519       [
39520         "Trinidad and Tobago",
39521         "tt",
39522         "1868"
39523       ],
39524       [
39525         "Tunisia (‫تونس‬‎)",
39526         "tn",
39527         "216"
39528       ],
39529       [
39530         "Turkey (Türkiye)",
39531         "tr",
39532         "90"
39533       ],
39534       [
39535         "Turkmenistan",
39536         "tm",
39537         "993"
39538       ],
39539       [
39540         "Turks and Caicos Islands",
39541         "tc",
39542         "1649"
39543       ],
39544       [
39545         "Tuvalu",
39546         "tv",
39547         "688"
39548       ],
39549       [
39550         "U.S. Virgin Islands",
39551         "vi",
39552         "1340"
39553       ],
39554       [
39555         "Uganda",
39556         "ug",
39557         "256"
39558       ],
39559       [
39560         "Ukraine (Україна)",
39561         "ua",
39562         "380"
39563       ],
39564       [
39565         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39566         "ae",
39567         "971"
39568       ],
39569       [
39570         "United Kingdom",
39571         "gb",
39572         "44",
39573         0
39574       ],
39575       [
39576         "United States",
39577         "us",
39578         "1",
39579         0
39580       ],
39581       [
39582         "Uruguay",
39583         "uy",
39584         "598"
39585       ],
39586       [
39587         "Uzbekistan (Oʻzbekiston)",
39588         "uz",
39589         "998"
39590       ],
39591       [
39592         "Vanuatu",
39593         "vu",
39594         "678"
39595       ],
39596       [
39597         "Vatican City (Città del Vaticano)",
39598         "va",
39599         "39",
39600         1
39601       ],
39602       [
39603         "Venezuela",
39604         "ve",
39605         "58"
39606       ],
39607       [
39608         "Vietnam (Việt Nam)",
39609         "vn",
39610         "84"
39611       ],
39612       [
39613         "Wallis and Futuna (Wallis-et-Futuna)",
39614         "wf",
39615         "681"
39616       ],
39617       [
39618         "Western Sahara (‫الصحراء الغربية‬‎)",
39619         "eh",
39620         "212",
39621         1
39622       ],
39623       [
39624         "Yemen (‫اليمن‬‎)",
39625         "ye",
39626         "967"
39627       ],
39628       [
39629         "Zambia",
39630         "zm",
39631         "260"
39632       ],
39633       [
39634         "Zimbabwe",
39635         "zw",
39636         "263"
39637       ],
39638       [
39639         "Åland Islands",
39640         "ax",
39641         "358",
39642         1
39643       ]
39644   ];
39645   
39646   return d;
39647 }/**
39648 *    This script refer to:
39649 *    Title: International Telephone Input
39650 *    Author: Jack O'Connor
39651 *    Code version:  v12.1.12
39652 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39653 **/
39654
39655 /**
39656  * @class Roo.bootstrap.PhoneInput
39657  * @extends Roo.bootstrap.TriggerField
39658  * An input with International dial-code selection
39659  
39660  * @cfg {String} defaultDialCode default '+852'
39661  * @cfg {Array} preferedCountries default []
39662   
39663  * @constructor
39664  * Create a new PhoneInput.
39665  * @param {Object} config Configuration options
39666  */
39667
39668 Roo.bootstrap.PhoneInput = function(config) {
39669     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39670 };
39671
39672 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39673         
39674         listWidth: undefined,
39675         
39676         selectedClass: 'active',
39677         
39678         invalidClass : "has-warning",
39679         
39680         validClass: 'has-success',
39681         
39682         allowed: '0123456789',
39683         
39684         /**
39685          * @cfg {String} defaultDialCode The default dial code when initializing the input
39686          */
39687         defaultDialCode: '+852',
39688         
39689         /**
39690          * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
39691          */
39692         preferedCountries: false,
39693         
39694         getAutoCreate : function()
39695         {
39696             var data = Roo.bootstrap.PhoneInputData();
39697             var align = this.labelAlign || this.parentLabelAlign();
39698             var id = Roo.id();
39699             
39700             this.allCountries = [];
39701             this.dialCodeMapping = [];
39702             
39703             for (var i = 0; i < data.length; i++) {
39704               var c = data[i];
39705               this.allCountries[i] = {
39706                 name: c[0],
39707                 iso2: c[1],
39708                 dialCode: c[2],
39709                 priority: c[3] || 0,
39710                 areaCodes: c[4] || null
39711               };
39712               this.dialCodeMapping[c[2]] = {
39713                   name: c[0],
39714                   iso2: c[1],
39715                   priority: c[3] || 0,
39716                   areaCodes: c[4] || null
39717               };
39718             }
39719             
39720             var cfg = {
39721                 cls: 'form-group',
39722                 cn: []
39723             };
39724             
39725             var input =  {
39726                 tag: 'input',
39727                 id : id,
39728                 cls : 'form-control tel-input',
39729                 autocomplete: 'new-password'
39730             };
39731             
39732             var hiddenInput = {
39733                 tag: 'input',
39734                 type: 'hidden',
39735                 cls: 'hidden-tel-input'
39736             };
39737             
39738             if (this.name) {
39739                 hiddenInput.name = this.name;
39740             }
39741             
39742             if (this.disabled) {
39743                 input.disabled = true;
39744             }
39745             
39746             var flag_container = {
39747                 tag: 'div',
39748                 cls: 'flag-box',
39749                 cn: [
39750                     {
39751                         tag: 'div',
39752                         cls: 'flag'
39753                     },
39754                     {
39755                         tag: 'div',
39756                         cls: 'caret'
39757                     }
39758                 ]
39759             };
39760             
39761             var box = {
39762                 tag: 'div',
39763                 cls: this.hasFeedback ? 'has-feedback' : '',
39764                 cn: [
39765                     hiddenInput,
39766                     input,
39767                     {
39768                         tag: 'input',
39769                         cls: 'dial-code-holder',
39770                         disabled: true
39771                     }
39772                 ]
39773             };
39774             
39775             var container = {
39776                 cls: 'roo-select2-container input-group',
39777                 cn: [
39778                     flag_container,
39779                     box
39780                 ]
39781             };
39782             
39783             if (this.fieldLabel.length) {
39784                 var indicator = {
39785                     tag: 'i',
39786                     tooltip: 'This field is required'
39787                 };
39788                 
39789                 var label = {
39790                     tag: 'label',
39791                     'for':  id,
39792                     cls: 'control-label',
39793                     cn: []
39794                 };
39795                 
39796                 var label_text = {
39797                     tag: 'span',
39798                     html: this.fieldLabel
39799                 };
39800                 
39801                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39802                 label.cn = [
39803                     indicator,
39804                     label_text
39805                 ];
39806                 
39807                 if(this.indicatorpos == 'right') {
39808                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39809                     label.cn = [
39810                         label_text,
39811                         indicator
39812                     ];
39813                 }
39814                 
39815                 if(align == 'left') {
39816                     container = {
39817                         tag: 'div',
39818                         cn: [
39819                             container
39820                         ]
39821                     };
39822                     
39823                     if(this.labelWidth > 12){
39824                         label.style = "width: " + this.labelWidth + 'px';
39825                     }
39826                     if(this.labelWidth < 13 && this.labelmd == 0){
39827                         this.labelmd = this.labelWidth;
39828                     }
39829                     if(this.labellg > 0){
39830                         label.cls += ' col-lg-' + this.labellg;
39831                         input.cls += ' col-lg-' + (12 - this.labellg);
39832                     }
39833                     if(this.labelmd > 0){
39834                         label.cls += ' col-md-' + this.labelmd;
39835                         container.cls += ' col-md-' + (12 - this.labelmd);
39836                     }
39837                     if(this.labelsm > 0){
39838                         label.cls += ' col-sm-' + this.labelsm;
39839                         container.cls += ' col-sm-' + (12 - this.labelsm);
39840                     }
39841                     if(this.labelxs > 0){
39842                         label.cls += ' col-xs-' + this.labelxs;
39843                         container.cls += ' col-xs-' + (12 - this.labelxs);
39844                     }
39845                 }
39846             }
39847             
39848             cfg.cn = [
39849                 label,
39850                 container
39851             ];
39852             
39853             var settings = this;
39854             
39855             ['xs','sm','md','lg'].map(function(size){
39856                 if (settings[size]) {
39857                     cfg.cls += ' col-' + size + '-' + settings[size];
39858                 }
39859             });
39860             
39861             this.store = new Roo.data.Store({
39862                 proxy : new Roo.data.MemoryProxy({}),
39863                 reader : new Roo.data.JsonReader({
39864                     fields : [
39865                         {
39866                             'name' : 'name',
39867                             'type' : 'string'
39868                         },
39869                         {
39870                             'name' : 'iso2',
39871                             'type' : 'string'
39872                         },
39873                         {
39874                             'name' : 'dialCode',
39875                             'type' : 'string'
39876                         },
39877                         {
39878                             'name' : 'priority',
39879                             'type' : 'string'
39880                         },
39881                         {
39882                             'name' : 'areaCodes',
39883                             'type' : 'string'
39884                         }
39885                     ]
39886                 })
39887             });
39888             
39889             if(!this.preferedCountries) {
39890                 this.preferedCountries = [
39891                     'hk',
39892                     'gb',
39893                     'us'
39894                 ];
39895             }
39896             
39897             var p = this.preferedCountries.reverse();
39898             
39899             if(p) {
39900                 for (var i = 0; i < p.length; i++) {
39901                     for (var j = 0; j < this.allCountries.length; j++) {
39902                         if(this.allCountries[j].iso2 == p[i]) {
39903                             var t = this.allCountries[j];
39904                             this.allCountries.splice(j,1);
39905                             this.allCountries.unshift(t);
39906                         }
39907                     } 
39908                 }
39909             }
39910             
39911             this.store.proxy.data = {
39912                 success: true,
39913                 data: this.allCountries
39914             };
39915             
39916             return cfg;
39917         },
39918         
39919         initEvents : function()
39920         {
39921             this.createList();
39922             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39923             
39924             this.indicator = this.indicatorEl();
39925             this.flag = this.flagEl();
39926             this.dialCodeHolder = this.dialCodeHolderEl();
39927             
39928             this.trigger = this.el.select('div.flag-box',true).first();
39929             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39930             
39931             var _this = this;
39932             
39933             (function(){
39934                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39935                 _this.list.setWidth(lw);
39936             }).defer(100);
39937             
39938             this.list.on('mouseover', this.onViewOver, this);
39939             this.list.on('mousemove', this.onViewMove, this);
39940             this.inputEl().on("keyup", this.onKeyUp, this);
39941             
39942             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39943
39944             this.view = new Roo.View(this.list, this.tpl, {
39945                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39946             });
39947             
39948             this.view.on('click', this.onViewClick, this);
39949             this.setValue(this.defaultDialCode);
39950         },
39951         
39952         onTriggerClick : function(e)
39953         {
39954             Roo.log('trigger click');
39955             if(this.disabled){
39956                 return;
39957             }
39958             
39959             if(this.isExpanded()){
39960                 this.collapse();
39961                 this.hasFocus = false;
39962             }else {
39963                 this.store.load({});
39964                 this.hasFocus = true;
39965                 this.expand();
39966             }
39967         },
39968         
39969         isExpanded : function()
39970         {
39971             return this.list.isVisible();
39972         },
39973         
39974         collapse : function()
39975         {
39976             if(!this.isExpanded()){
39977                 return;
39978             }
39979             this.list.hide();
39980             Roo.get(document).un('mousedown', this.collapseIf, this);
39981             Roo.get(document).un('mousewheel', this.collapseIf, this);
39982             this.fireEvent('collapse', this);
39983             this.validate();
39984         },
39985         
39986         expand : function()
39987         {
39988             Roo.log('expand');
39989
39990             if(this.isExpanded() || !this.hasFocus){
39991                 return;
39992             }
39993             
39994             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39995             this.list.setWidth(lw);
39996             
39997             this.list.show();
39998             this.restrictHeight();
39999             
40000             Roo.get(document).on('mousedown', this.collapseIf, this);
40001             Roo.get(document).on('mousewheel', this.collapseIf, this);
40002             
40003             this.fireEvent('expand', this);
40004         },
40005         
40006         restrictHeight : function()
40007         {
40008             this.list.alignTo(this.inputEl(), this.listAlign);
40009             this.list.alignTo(this.inputEl(), this.listAlign);
40010         },
40011         
40012         onViewOver : function(e, t)
40013         {
40014             if(this.inKeyMode){
40015                 return;
40016             }
40017             var item = this.view.findItemFromChild(t);
40018             
40019             if(item){
40020                 var index = this.view.indexOf(item);
40021                 this.select(index, false);
40022             }
40023         },
40024
40025         // private
40026         onViewClick : function(view, doFocus, el, e)
40027         {
40028             var index = this.view.getSelectedIndexes()[0];
40029             
40030             var r = this.store.getAt(index);
40031             
40032             if(r){
40033                 this.onSelect(r, index);
40034             }
40035             if(doFocus !== false && !this.blockFocus){
40036                 this.inputEl().focus();
40037             }
40038         },
40039         
40040         onViewMove : function(e, t)
40041         {
40042             this.inKeyMode = false;
40043         },
40044         
40045         select : function(index, scrollIntoView)
40046         {
40047             this.selectedIndex = index;
40048             this.view.select(index);
40049             if(scrollIntoView !== false){
40050                 var el = this.view.getNode(index);
40051                 if(el){
40052                     this.list.scrollChildIntoView(el, false);
40053                 }
40054             }
40055         },
40056         
40057         createList : function()
40058         {
40059             this.list = Roo.get(document.body).createChild({
40060                 tag: 'ul',
40061                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40062                 style: 'display:none'
40063             });
40064             
40065             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40066         },
40067         
40068         collapseIf : function(e)
40069         {
40070             var in_combo  = e.within(this.el);
40071             var in_list =  e.within(this.list);
40072             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40073             
40074             if (in_combo || in_list || is_list) {
40075                 return;
40076             }
40077             this.collapse();
40078         },
40079         
40080         onSelect : function(record, index)
40081         {
40082             if(this.fireEvent('beforeselect', this, record, index) !== false){
40083                 
40084                 this.setFlagClass(record.data.iso2);
40085                 this.setDialCode(record.data.dialCode);
40086                 this.hasFocus = false;
40087                 this.collapse();
40088                 this.fireEvent('select', this, record, index);
40089             }
40090         },
40091         
40092         flagEl : function()
40093         {
40094             var flag = this.el.select('div.flag',true).first();
40095             if(!flag){
40096                 return false;
40097             }
40098             return flag;
40099         },
40100         
40101         dialCodeHolderEl : function()
40102         {
40103             var d = this.el.select('input.dial-code-holder',true).first();
40104             if(!d){
40105                 return false;
40106             }
40107             return d;
40108         },
40109         
40110         setDialCode : function(v)
40111         {
40112             this.dialCodeHolder.dom.value = '+'+v;
40113         },
40114         
40115         setFlagClass : function(n)
40116         {
40117             this.flag.dom.className = 'flag '+n;
40118         },
40119         
40120         getValue : function()
40121         {
40122             var v = this.inputEl().getValue();
40123             if(this.dialCodeHolder) {
40124                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40125             }
40126             return v;
40127         },
40128         
40129         setValue : function(v)
40130         {
40131             var d = this.getDialCode(v);
40132             
40133             //invalid dial code
40134             if(v.length == 0 || !d || d.length == 0) {
40135                 if(this.rendered){
40136                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40137                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40138                 }
40139                 return;
40140             }
40141             
40142             //valid dial code
40143             this.setFlagClass(this.dialCodeMapping[d].iso2);
40144             this.setDialCode(d);
40145             this.inputEl().dom.value = v.replace('+'+d,'');
40146             this.hiddenEl().dom.value = this.getValue();
40147             
40148             this.validate();
40149         },
40150         
40151         getDialCode : function(v)
40152         {
40153             v = v ||  '';
40154             
40155             if (v.length == 0) {
40156                 return this.dialCodeHolder.dom.value;
40157             }
40158             
40159             var dialCode = "";
40160             if (v.charAt(0) != "+") {
40161                 return false;
40162             }
40163             var numericChars = "";
40164             for (var i = 1; i < v.length; i++) {
40165               var c = v.charAt(i);
40166               if (!isNaN(c)) {
40167                 numericChars += c;
40168                 if (this.dialCodeMapping[numericChars]) {
40169                   dialCode = v.substr(1, i);
40170                 }
40171                 if (numericChars.length == 4) {
40172                   break;
40173                 }
40174               }
40175             }
40176             return dialCode;
40177         },
40178         
40179         reset : function()
40180         {
40181             this.setValue(this.defaultDialCode);
40182             this.validate();
40183         },
40184         
40185         hiddenEl : function()
40186         {
40187             return this.el.select('input.hidden-tel-input',true).first();
40188         },
40189         
40190         onKeyUp : function(e){
40191             
40192             var k = e.getKey();
40193             var c = e.getCharCode();
40194             
40195             if(
40196                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40197                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40198             ){
40199                 e.stopEvent();
40200             }
40201             
40202             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40203             //     return;
40204             // }
40205             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40206                 e.stopEvent();
40207             }
40208             
40209             this.setValue(this.getValue());
40210         }
40211         
40212 });
40213 /**
40214  * @class Roo.bootstrap.MoneyField
40215  * @extends Roo.bootstrap.ComboBox
40216  * Bootstrap MoneyField class
40217  * 
40218  * @constructor
40219  * Create a new MoneyField.
40220  * @param {Object} config Configuration options
40221  */
40222
40223 Roo.bootstrap.MoneyField = function(config) {
40224     
40225     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40226     
40227 };
40228
40229 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40230     
40231     /**
40232      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40233      */
40234     allowDecimals : true,
40235     /**
40236      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40237      */
40238     decimalSeparator : ".",
40239     /**
40240      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40241      */
40242     decimalPrecision : 0,
40243     /**
40244      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40245      */
40246     allowNegative : true,
40247     /**
40248      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40249      */
40250     minValue : Number.NEGATIVE_INFINITY,
40251     /**
40252      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40253      */
40254     maxValue : Number.MAX_VALUE,
40255     /**
40256      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40257      */
40258     minText : "The minimum value for this field is {0}",
40259     /**
40260      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40261      */
40262     maxText : "The maximum value for this field is {0}",
40263     /**
40264      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40265      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40266      */
40267     nanText : "{0} is not a valid number",
40268     /**
40269      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40270      */
40271     castInt : true,
40272     /**
40273      * @cfg {String} defaults currency of the MoneyField
40274      * value should be in lkey
40275      */
40276     defaultCurrency : false,
40277     /**
40278      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40279      */
40280     thousandsDelimiter : false,
40281     
40282     
40283     inputlg : 9,
40284     inputmd : 9,
40285     inputsm : 9,
40286     inputxs : 6,
40287     
40288     store : false,
40289     
40290     getAutoCreate : function()
40291     {
40292         var align = this.labelAlign || this.parentLabelAlign();
40293         
40294         var id = Roo.id();
40295
40296         var cfg = {
40297             cls: 'form-group',
40298             cn: []
40299         };
40300
40301         var input =  {
40302             tag: 'input',
40303             id : id,
40304             cls : 'form-control roo-money-amount-input',
40305             autocomplete: 'new-password'
40306         };
40307         
40308         var hiddenInput = {
40309             tag: 'input',
40310             type: 'hidden',
40311             id: Roo.id(),
40312             cls: 'hidden-number-input'
40313         };
40314         
40315         if (this.name) {
40316             hiddenInput.name = this.name;
40317         }
40318
40319         if (this.disabled) {
40320             input.disabled = true;
40321         }
40322
40323         var clg = 12 - this.inputlg;
40324         var cmd = 12 - this.inputmd;
40325         var csm = 12 - this.inputsm;
40326         var cxs = 12 - this.inputxs;
40327         
40328         var container = {
40329             tag : 'div',
40330             cls : 'row roo-money-field',
40331             cn : [
40332                 {
40333                     tag : 'div',
40334                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40335                     cn : [
40336                         {
40337                             tag : 'div',
40338                             cls: 'roo-select2-container input-group',
40339                             cn: [
40340                                 {
40341                                     tag : 'input',
40342                                     cls : 'form-control roo-money-currency-input',
40343                                     autocomplete: 'new-password',
40344                                     readOnly : 1,
40345                                     name : this.currencyName
40346                                 },
40347                                 {
40348                                     tag :'span',
40349                                     cls : 'input-group-addon',
40350                                     cn : [
40351                                         {
40352                                             tag: 'span',
40353                                             cls: 'caret'
40354                                         }
40355                                     ]
40356                                 }
40357                             ]
40358                         }
40359                     ]
40360                 },
40361                 {
40362                     tag : 'div',
40363                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40364                     cn : [
40365                         {
40366                             tag: 'div',
40367                             cls: this.hasFeedback ? 'has-feedback' : '',
40368                             cn: [
40369                                 input
40370                             ]
40371                         }
40372                     ]
40373                 }
40374             ]
40375             
40376         };
40377         
40378         if (this.fieldLabel.length) {
40379             var indicator = {
40380                 tag: 'i',
40381                 tooltip: 'This field is required'
40382             };
40383
40384             var label = {
40385                 tag: 'label',
40386                 'for':  id,
40387                 cls: 'control-label',
40388                 cn: []
40389             };
40390
40391             var label_text = {
40392                 tag: 'span',
40393                 html: this.fieldLabel
40394             };
40395
40396             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40397             label.cn = [
40398                 indicator,
40399                 label_text
40400             ];
40401
40402             if(this.indicatorpos == 'right') {
40403                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40404                 label.cn = [
40405                     label_text,
40406                     indicator
40407                 ];
40408             }
40409
40410             if(align == 'left') {
40411                 container = {
40412                     tag: 'div',
40413                     cn: [
40414                         container
40415                     ]
40416                 };
40417
40418                 if(this.labelWidth > 12){
40419                     label.style = "width: " + this.labelWidth + 'px';
40420                 }
40421                 if(this.labelWidth < 13 && this.labelmd == 0){
40422                     this.labelmd = this.labelWidth;
40423                 }
40424                 if(this.labellg > 0){
40425                     label.cls += ' col-lg-' + this.labellg;
40426                     input.cls += ' col-lg-' + (12 - this.labellg);
40427                 }
40428                 if(this.labelmd > 0){
40429                     label.cls += ' col-md-' + this.labelmd;
40430                     container.cls += ' col-md-' + (12 - this.labelmd);
40431                 }
40432                 if(this.labelsm > 0){
40433                     label.cls += ' col-sm-' + this.labelsm;
40434                     container.cls += ' col-sm-' + (12 - this.labelsm);
40435                 }
40436                 if(this.labelxs > 0){
40437                     label.cls += ' col-xs-' + this.labelxs;
40438                     container.cls += ' col-xs-' + (12 - this.labelxs);
40439                 }
40440             }
40441         }
40442
40443         cfg.cn = [
40444             label,
40445             container,
40446             hiddenInput
40447         ];
40448         
40449         var settings = this;
40450
40451         ['xs','sm','md','lg'].map(function(size){
40452             if (settings[size]) {
40453                 cfg.cls += ' col-' + size + '-' + settings[size];
40454             }
40455         });
40456         
40457         return cfg;
40458     },
40459     
40460     initEvents : function()
40461     {
40462         this.indicator = this.indicatorEl();
40463         
40464         this.initCurrencyEvent();
40465         
40466         this.initNumberEvent();
40467     },
40468     
40469     initCurrencyEvent : function()
40470     {
40471         if (!this.store) {
40472             throw "can not find store for combo";
40473         }
40474         
40475         this.store = Roo.factory(this.store, Roo.data);
40476         this.store.parent = this;
40477         
40478         this.createList();
40479         
40480         this.triggerEl = this.el.select('.input-group-addon', true).first();
40481         
40482         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40483         
40484         var _this = this;
40485         
40486         (function(){
40487             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40488             _this.list.setWidth(lw);
40489         }).defer(100);
40490         
40491         this.list.on('mouseover', this.onViewOver, this);
40492         this.list.on('mousemove', this.onViewMove, this);
40493         this.list.on('scroll', this.onViewScroll, this);
40494         
40495         if(!this.tpl){
40496             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40497         }
40498         
40499         this.view = new Roo.View(this.list, this.tpl, {
40500             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40501         });
40502         
40503         this.view.on('click', this.onViewClick, this);
40504         
40505         this.store.on('beforeload', this.onBeforeLoad, this);
40506         this.store.on('load', this.onLoad, this);
40507         this.store.on('loadexception', this.onLoadException, this);
40508         
40509         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40510             "up" : function(e){
40511                 this.inKeyMode = true;
40512                 this.selectPrev();
40513             },
40514
40515             "down" : function(e){
40516                 if(!this.isExpanded()){
40517                     this.onTriggerClick();
40518                 }else{
40519                     this.inKeyMode = true;
40520                     this.selectNext();
40521                 }
40522             },
40523
40524             "enter" : function(e){
40525                 this.collapse();
40526                 
40527                 if(this.fireEvent("specialkey", this, e)){
40528                     this.onViewClick(false);
40529                 }
40530                 
40531                 return true;
40532             },
40533
40534             "esc" : function(e){
40535                 this.collapse();
40536             },
40537
40538             "tab" : function(e){
40539                 this.collapse();
40540                 
40541                 if(this.fireEvent("specialkey", this, e)){
40542                     this.onViewClick(false);
40543                 }
40544                 
40545                 return true;
40546             },
40547
40548             scope : this,
40549
40550             doRelay : function(foo, bar, hname){
40551                 if(hname == 'down' || this.scope.isExpanded()){
40552                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40553                 }
40554                 return true;
40555             },
40556
40557             forceKeyDown: true
40558         });
40559         
40560         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40561         
40562     },
40563     
40564     initNumberEvent : function(e)
40565     {
40566         this.inputEl().on("keydown" , this.fireKey,  this);
40567         this.inputEl().on("focus", this.onFocus,  this);
40568         this.inputEl().on("blur", this.onBlur,  this);
40569         
40570         this.inputEl().relayEvent('keyup', this);
40571         
40572         if(this.indicator){
40573             this.indicator.addClass('invisible');
40574         }
40575  
40576         this.originalValue = this.getValue();
40577         
40578         if(this.validationEvent == 'keyup'){
40579             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40580             this.inputEl().on('keyup', this.filterValidation, this);
40581         }
40582         else if(this.validationEvent !== false){
40583             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40584         }
40585         
40586         if(this.selectOnFocus){
40587             this.on("focus", this.preFocus, this);
40588             
40589         }
40590         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40591             this.inputEl().on("keypress", this.filterKeys, this);
40592         } else {
40593             this.inputEl().relayEvent('keypress', this);
40594         }
40595         
40596         var allowed = "0123456789";
40597         
40598         if(this.allowDecimals){
40599             allowed += this.decimalSeparator;
40600         }
40601         
40602         if(this.allowNegative){
40603             allowed += "-";
40604         }
40605         
40606         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40607         
40608         var keyPress = function(e){
40609             
40610             var k = e.getKey();
40611             
40612             var c = e.getCharCode();
40613             
40614             if(
40615                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40616                     allowed.indexOf(String.fromCharCode(c)) === -1
40617             ){
40618                 e.stopEvent();
40619                 return;
40620             }
40621             
40622             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40623                 return;
40624             }
40625             
40626             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40627                 e.stopEvent();
40628             }
40629         };
40630         
40631         this.inputEl().on("keypress", keyPress, this);
40632         
40633     },
40634     
40635     onTriggerClick : function(e)
40636     {   
40637         if(this.disabled){
40638             return;
40639         }
40640         
40641         this.page = 0;
40642         this.loadNext = false;
40643         
40644         if(this.isExpanded()){
40645             this.collapse();
40646             return;
40647         }
40648         
40649         this.hasFocus = true;
40650         
40651         if(this.triggerAction == 'all') {
40652             this.doQuery(this.allQuery, true);
40653             return;
40654         }
40655         
40656         this.doQuery(this.getRawValue());
40657     },
40658     
40659     getCurrency : function()
40660     {   
40661         var v = this.currencyEl().getValue();
40662         
40663         return v;
40664     },
40665     
40666     restrictHeight : function()
40667     {
40668         this.list.alignTo(this.currencyEl(), this.listAlign);
40669         this.list.alignTo(this.currencyEl(), this.listAlign);
40670     },
40671     
40672     onViewClick : function(view, doFocus, el, e)
40673     {
40674         var index = this.view.getSelectedIndexes()[0];
40675         
40676         var r = this.store.getAt(index);
40677         
40678         if(r){
40679             this.onSelect(r, index);
40680         }
40681     },
40682     
40683     onSelect : function(record, index){
40684         
40685         if(this.fireEvent('beforeselect', this, record, index) !== false){
40686         
40687             this.setFromCurrencyData(index > -1 ? record.data : false);
40688             
40689             this.collapse();
40690             
40691             this.fireEvent('select', this, record, index);
40692         }
40693     },
40694     
40695     setFromCurrencyData : function(o)
40696     {
40697         var currency = '';
40698         
40699         this.lastCurrency = o;
40700         
40701         if (this.currencyField) {
40702             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40703         } else {
40704             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40705         }
40706         
40707         this.lastSelectionText = currency;
40708         
40709         //setting default currency
40710         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40711             this.setCurrency(this.defaultCurrency);
40712             return;
40713         }
40714         
40715         this.setCurrency(currency);
40716     },
40717     
40718     setFromData : function(o)
40719     {
40720         var c = {};
40721         
40722         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40723         
40724         this.setFromCurrencyData(c);
40725         
40726         var value = '';
40727         
40728         if (this.name) {
40729             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40730         } else {
40731             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40732         }
40733         
40734         this.setValue(value);
40735         
40736     },
40737     
40738     setCurrency : function(v)
40739     {   
40740         this.currencyValue = v;
40741         
40742         if(this.rendered){
40743             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40744             this.validate();
40745         }
40746     },
40747     
40748     setValue : function(v)
40749     {
40750         v = this.fixPrecision(v);
40751         
40752         v = String(v).replace(".", this.decimalSeparator);
40753         
40754         this.value = (v === null || v === undefined) ? '' : v;
40755         
40756         if(this.rendered){
40757             
40758             this.inputEl().dom.value = this.hiddenEl().dom.value = this.value;
40759             
40760             if(this.allowDecimals && this.decimalPrecision != -1 && !isNaN(this.value)){
40761                 this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision, 
40762                     this.thousandsDelimiter || ','
40763                 );
40764             }
40765             
40766             if(this.allowBlank && !v) {
40767                 this.inputEl().dom.value = '';
40768             }
40769             
40770             this.validate();
40771         }
40772     },
40773     
40774     getRawValue : function()
40775     {
40776         var v = this.inputEl().getValue();
40777         
40778         return v;
40779     },
40780     
40781     getValue : function()
40782     {
40783         return this.fixPrecision(this.parseValue(this.getRawValue()));
40784     },
40785     
40786     parseValue : function(value)
40787     {
40788         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40789         return isNaN(value) ? '' : value;
40790     },
40791     
40792     fixPrecision : function(value)
40793     {
40794         var nan = isNaN(value);
40795         
40796         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40797             return nan ? '' : value;
40798         }
40799         
40800         return parseFloat(value).toFixed(this.decimalPrecision);
40801     },
40802     
40803     decimalPrecisionFcn : function(v)
40804     {
40805         return Math.floor(v);
40806     },
40807     
40808     validateValue : function(value)
40809     {
40810         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40811             return false;
40812         }
40813         
40814         var num = this.parseValue(value);
40815         
40816         if(isNaN(num)){
40817             this.markInvalid(String.format(this.nanText, value));
40818             return false;
40819         }
40820         
40821         if(num < this.minValue){
40822             this.markInvalid(String.format(this.minText, this.minValue));
40823             return false;
40824         }
40825         
40826         if(num > this.maxValue){
40827             this.markInvalid(String.format(this.maxText, this.maxValue));
40828             return false;
40829         }
40830         
40831         return true;
40832     },
40833     
40834     validate : function()
40835     {
40836         if(this.disabled || this.allowBlank){
40837             this.markValid();
40838             return true;
40839         }
40840         
40841         var currency = this.getCurrency();
40842         
40843         if(this.validateValue(this.getRawValue()) && currency.length){
40844             this.markValid();
40845             return true;
40846         }
40847         
40848         this.markInvalid();
40849         return false;
40850     },
40851     
40852     getName: function()
40853     {
40854         return this.name;
40855     },
40856     
40857     beforeBlur : function()
40858     {
40859         if(!this.castInt){
40860             return;
40861         }
40862         
40863         var v = this.parseValue(this.getRawValue());
40864         
40865         if(v || v == 0){
40866             this.setValue(v);
40867         }
40868     },
40869     
40870     onBlur : function()
40871     {
40872         this.beforeBlur();
40873         
40874         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40875             //this.el.removeClass(this.focusClass);
40876         }
40877         
40878         this.hasFocus = false;
40879         
40880         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40881             this.validate();
40882         }
40883         
40884         var v = this.getValue();
40885         
40886         if(String(v) !== String(this.startValue)){
40887             this.fireEvent('change', this, v, this.startValue);
40888         }
40889         
40890         this.fireEvent("blur", this);
40891     },
40892     
40893     inputEl : function()
40894     {
40895         return this.el.select('.roo-money-amount-input', true).first();
40896     },
40897     
40898     currencyEl : function()
40899     {
40900         return this.el.select('.roo-money-currency-input', true).first();
40901     },
40902     
40903     hiddenEl : function()
40904     {
40905         return this.el.select('input.hidden-number-input',true).first();
40906     }
40907     
40908 });