roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         
405     },
406     /**
407      * Hide a component - adds 'hidden' class
408      */
409     hide: function()
410     {
411         if(!this.getVisibilityEl()){
412             return;
413         }
414         
415         this.getVisibilityEl().addClass('hidden');
416         
417     }
418 });
419
420  /*
421  * - LGPL
422  *
423  * Body
424  *
425  */
426
427 /**
428  * @class Roo.bootstrap.Body
429  * @extends Roo.bootstrap.Component
430  * Bootstrap Body class
431  *
432  * @constructor
433  * Create a new body
434  * @param {Object} config The config object
435  */
436
437 Roo.bootstrap.Body = function(config){
438
439     config = config || {};
440
441     Roo.bootstrap.Body.superclass.constructor.call(this, config);
442     this.el = Roo.get(config.el ? config.el : document.body );
443     if (this.cls && this.cls.length) {
444         Roo.get(document.body).addClass(this.cls);
445     }
446 };
447
448 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
449
450     is_body : true,// just to make sure it's constructed?
451
452         autoCreate : {
453         cls: 'container'
454     },
455     onRender : function(ct, position)
456     {
457        /* Roo.log("Roo.bootstrap.Body - onRender");
458         if (this.cls && this.cls.length) {
459             Roo.get(document.body).addClass(this.cls);
460         }
461         // style??? xttr???
462         */
463     }
464
465
466
467
468 });
469 /*
470  * - LGPL
471  *
472  * button group
473  * 
474  */
475
476
477 /**
478  * @class Roo.bootstrap.ButtonGroup
479  * @extends Roo.bootstrap.Component
480  * Bootstrap ButtonGroup class
481  * @cfg {String} size lg | sm | xs (default empty normal)
482  * @cfg {String} align vertical | justified  (default none)
483  * @cfg {String} direction up | down (default down)
484  * @cfg {Boolean} toolbar false | true
485  * @cfg {Boolean} btn true | false
486  * 
487  * 
488  * @constructor
489  * Create a new Input
490  * @param {Object} config The config object
491  */
492
493 Roo.bootstrap.ButtonGroup = function(config){
494     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
495 };
496
497 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
498     
499     size: '',
500     align: '',
501     direction: '',
502     toolbar: false,
503     btn: true,
504
505     getAutoCreate : function(){
506         var cfg = {
507             cls: 'btn-group',
508             html : null
509         };
510         
511         cfg.html = this.html || cfg.html;
512         
513         if (this.toolbar) {
514             cfg = {
515                 cls: 'btn-toolbar',
516                 html: null
517             };
518             
519             return cfg;
520         }
521         
522         if (['vertical','justified'].indexOf(this.align)!==-1) {
523             cfg.cls = 'btn-group-' + this.align;
524             
525             if (this.align == 'justified') {
526                 console.log(this.items);
527             }
528         }
529         
530         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
531             cfg.cls += ' btn-group-' + this.size;
532         }
533         
534         if (this.direction == 'up') {
535             cfg.cls += ' dropup' ;
536         }
537         
538         return cfg;
539     }
540    
541 });
542
543  /*
544  * - LGPL
545  *
546  * button
547  * 
548  */
549
550 /**
551  * @class Roo.bootstrap.Button
552  * @extends Roo.bootstrap.Component
553  * Bootstrap Button class
554  * @cfg {String} html The button content
555  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
556  * @cfg {String} size ( lg | sm | xs)
557  * @cfg {String} tag ( a | input | submit)
558  * @cfg {String} href empty or href
559  * @cfg {Boolean} disabled default false;
560  * @cfg {Boolean} isClose default false;
561  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
562  * @cfg {String} badge text for badge
563  * @cfg {String} theme default 
564  * @cfg {Boolean} inverse 
565  * @cfg {Boolean} toggle is it a slidy toggle button
566  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
567  * @cfg {String} ontext text for on toggle state
568  * @cfg {String} offtext text for off toggle state
569  * @cfg {Boolean} defaulton 
570  * @cfg {Boolean} preventDefault  default true
571  * @cfg {Boolean} removeClass remove the standard class..
572  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
573  * 
574  * @constructor
575  * Create a new button
576  * @param {Object} config The config object
577  */
578
579
580 Roo.bootstrap.Button = function(config){
581     Roo.bootstrap.Button.superclass.constructor.call(this, config);
582     this.weightClass = ["btn-default", 
583                        "btn-primary", 
584                        "btn-success", 
585                        "btn-info", 
586                        "btn-warning",
587                        "btn-danger",
588                        "btn-link"
589                       ],  
590     this.addEvents({
591         // raw events
592         /**
593          * @event click
594          * When a butotn is pressed
595          * @param {Roo.bootstrap.Button} btn
596          * @param {Roo.EventObject} e
597          */
598         "click" : true,
599          /**
600          * @event toggle
601          * After the button has been toggles
602          * @param {Roo.bootstrap.Button} btn
603          * @param {Roo.EventObject} e
604          * @param {boolean} pressed (also available as button.pressed)
605          */
606         "toggle" : true
607     });
608 };
609
610 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
611     html: false,
612     active: false,
613     weight: '',
614     size: '',
615     tag: 'button',
616     href: '',
617     disabled: false,
618     isClose: false,
619     glyphicon: '',
620     badge: '',
621     theme: 'default',
622     inverse: false,
623     
624     toggle: false,
625     ontext: 'ON',
626     offtext: 'OFF',
627     defaulton: true,
628     preventDefault: true,
629     removeClass: false,
630     name: false,
631     target: false,
632     
633     
634     pressed : null,
635      
636     
637     getAutoCreate : function(){
638         
639         var cfg = {
640             tag : 'button',
641             cls : 'roo-button',
642             html: ''
643         };
644         
645         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
646             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
647             this.tag = 'button';
648         } else {
649             cfg.tag = this.tag;
650         }
651         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
652         
653         if (this.toggle == true) {
654             cfg={
655                 tag: 'div',
656                 cls: 'slider-frame roo-button',
657                 cn: [
658                     {
659                         tag: 'span',
660                         'data-on-text':'ON',
661                         'data-off-text':'OFF',
662                         cls: 'slider-button',
663                         html: this.offtext
664                     }
665                 ]
666             };
667             
668             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
669                 cfg.cls += ' '+this.weight;
670             }
671             
672             return cfg;
673         }
674         
675         if (this.isClose) {
676             cfg.cls += ' close';
677             
678             cfg["aria-hidden"] = true;
679             
680             cfg.html = "&times;";
681             
682             return cfg;
683         }
684         
685          
686         if (this.theme==='default') {
687             cfg.cls = 'btn roo-button';
688             
689             //if (this.parentType != 'Navbar') {
690             this.weight = this.weight.length ?  this.weight : 'default';
691             //}
692             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
693                 
694                 cfg.cls += ' btn-' + this.weight;
695             }
696         } else if (this.theme==='glow') {
697             
698             cfg.tag = 'a';
699             cfg.cls = 'btn-glow roo-button';
700             
701             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 
703                 cfg.cls += ' ' + this.weight;
704             }
705         }
706    
707         
708         if (this.inverse) {
709             this.cls += ' inverse';
710         }
711         
712         
713         if (this.active || this.pressed === true) {
714             cfg.cls += ' active';
715         }
716         
717         if (this.disabled) {
718             cfg.disabled = 'disabled';
719         }
720         
721         if (this.items) {
722             Roo.log('changing to ul' );
723             cfg.tag = 'ul';
724             this.glyphicon = 'caret';
725         }
726         
727         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
728          
729         //gsRoo.log(this.parentType);
730         if (this.parentType === 'Navbar' && !this.parent().bar) {
731             Roo.log('changing to li?');
732             
733             cfg.tag = 'li';
734             
735             cfg.cls = '';
736             cfg.cn =  [{
737                 tag : 'a',
738                 cls : 'roo-button',
739                 html : this.html,
740                 href : this.href || '#'
741             }];
742             if (this.menu) {
743                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
744                 cfg.cls += ' dropdown';
745             }   
746             
747             delete cfg.html;
748             
749         }
750         
751        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
752         
753         if (this.glyphicon) {
754             cfg.html = ' ' + cfg.html;
755             
756             cfg.cn = [
757                 {
758                     tag: 'span',
759                     cls: 'glyphicon glyphicon-' + this.glyphicon
760                 }
761             ];
762         }
763         
764         if (this.badge) {
765             cfg.html += ' ';
766             
767             cfg.tag = 'a';
768             
769 //            cfg.cls='btn roo-button';
770             
771             cfg.href=this.href;
772             
773             var value = cfg.html;
774             
775             if(this.glyphicon){
776                 value = {
777                             tag: 'span',
778                             cls: 'glyphicon glyphicon-' + this.glyphicon,
779                             html: this.html
780                         };
781                 
782             }
783             
784             cfg.cn = [
785                 value,
786                 {
787                     tag: 'span',
788                     cls: 'badge',
789                     html: this.badge
790                 }
791             ];
792             
793             cfg.html='';
794         }
795         
796         if (this.menu) {
797             cfg.cls += ' dropdown';
798             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
799         }
800         
801         if (cfg.tag !== 'a' && this.href !== '') {
802             throw "Tag must be a to set href.";
803         } else if (this.href.length > 0) {
804             cfg.href = this.href;
805         }
806         
807         if(this.removeClass){
808             cfg.cls = '';
809         }
810         
811         if(this.target){
812             cfg.target = this.target;
813         }
814         
815         return cfg;
816     },
817     initEvents: function() {
818        // Roo.log('init events?');
819 //        Roo.log(this.el.dom);
820         // add the menu...
821         
822         if (typeof (this.menu) != 'undefined') {
823             this.menu.parentType = this.xtype;
824             this.menu.triggerEl = this.el;
825             this.addxtype(Roo.apply({}, this.menu));
826         }
827
828
829        if (this.el.hasClass('roo-button')) {
830             this.el.on('click', this.onClick, this);
831        } else {
832             this.el.select('.roo-button').on('click', this.onClick, this);
833        }
834        
835        if(this.removeClass){
836            this.el.on('click', this.onClick, this);
837        }
838        
839        this.el.enableDisplayMode();
840         
841     },
842     onClick : function(e)
843     {
844         if (this.disabled) {
845             return;
846         }
847         
848         
849         Roo.log('button on click ');
850         if(this.preventDefault){
851             e.preventDefault();
852         }
853         
854         if (this.pressed === true || this.pressed === false) {
855             this.pressed = !this.pressed;
856             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
857             this.fireEvent('toggle', this, e, this.pressed);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function()
894     {
895        var active = this.el.hasClass('active');
896        this.setActive(!active);
897        
898         
899     },
900      /**
901      * get the current active state
902      * @return {boolean} true if it's active
903      */
904     isActive : function()
905     {
906         return this.el.hasClass('active');
907     },
908     /**
909      * set the text of the first selected button
910      */
911     setText : function(str)
912     {
913         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
914     },
915     /**
916      * get the text of the first selected button
917      */
918     getText : function()
919     {
920         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
921     },
922     hide: function() {
923        
924      
925         this.el.hide();   
926     },
927     show: function() {
928        
929         this.el.show();   
930     },
931     setWeight : function(str)
932     {
933         this.el.removeClass(this.weightClass);
934         this.el.addClass('btn-' + str);        
935     }
936     
937     
938 });
939
940  /*
941  * - LGPL
942  *
943  * column
944  * 
945  */
946
947 /**
948  * @class Roo.bootstrap.Column
949  * @extends Roo.bootstrap.Component
950  * Bootstrap Column class
951  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
952  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
953  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
954  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
955  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
956  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
957  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
958  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
959  *
960  * 
961  * @cfg {Boolean} hidden (true|false) hide the element
962  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
963  * @cfg {String} fa (ban|check|...) font awesome icon
964  * @cfg {Number} fasize (1|2|....) font awsome size
965
966  * @cfg {String} icon (info-sign|check|...) glyphicon name
967
968  * @cfg {String} html content of column.
969  * 
970  * @constructor
971  * Create a new Column
972  * @param {Object} config The config object
973  */
974
975 Roo.bootstrap.Column = function(config){
976     Roo.bootstrap.Column.superclass.constructor.call(this, config);
977 };
978
979 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
980     
981     xs: false,
982     sm: false,
983     md: false,
984     lg: false,
985     xsoff: false,
986     smoff: false,
987     mdoff: false,
988     lgoff: false,
989     html: '',
990     offset: 0,
991     alert: false,
992     fa: false,
993     icon : false,
994     hidden : false,
995     fasize : 1,
996     
997     getAutoCreate : function(){
998         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
999         
1000         cfg = {
1001             tag: 'div',
1002             cls: 'column'
1003         };
1004         
1005         var settings=this;
1006         ['xs','sm','md','lg'].map(function(size){
1007             //Roo.log( size + ':' + settings[size]);
1008             
1009             if (settings[size+'off'] !== false) {
1010                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1011             }
1012             
1013             if (settings[size] === false) {
1014                 return;
1015             }
1016             
1017             if (!settings[size]) { // 0 = hidden
1018                 cfg.cls += ' hidden-' + size;
1019                 return;
1020             }
1021             cfg.cls += ' col-' + size + '-' + settings[size];
1022             
1023         });
1024         
1025         if (this.hidden) {
1026             cfg.cls += ' hidden';
1027         }
1028         
1029         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1030             cfg.cls +=' alert alert-' + this.alert;
1031         }
1032         
1033         
1034         if (this.html.length) {
1035             cfg.html = this.html;
1036         }
1037         if (this.fa) {
1038             var fasize = '';
1039             if (this.fasize > 1) {
1040                 fasize = ' fa-' + this.fasize + 'x';
1041             }
1042             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1043             
1044             
1045         }
1046         if (this.icon) {
1047             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1048         }
1049         
1050         return cfg;
1051     }
1052    
1053 });
1054
1055  
1056
1057  /*
1058  * - LGPL
1059  *
1060  * page container.
1061  * 
1062  */
1063
1064
1065 /**
1066  * @class Roo.bootstrap.Container
1067  * @extends Roo.bootstrap.Component
1068  * Bootstrap Container class
1069  * @cfg {Boolean} jumbotron is it a jumbotron element
1070  * @cfg {String} html content of element
1071  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1072  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1073  * @cfg {String} header content of header (for panel)
1074  * @cfg {String} footer content of footer (for panel)
1075  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1076  * @cfg {String} tag (header|aside|section) type of HTML tag.
1077  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1078  * @cfg {String} fa font awesome icon
1079  * @cfg {String} icon (info-sign|check|...) glyphicon name
1080  * @cfg {Boolean} hidden (true|false) hide the element
1081  * @cfg {Boolean} expandable (true|false) default false
1082  * @cfg {Boolean} expanded (true|false) default true
1083  * @cfg {String} rheader contet on the right of header
1084  * @cfg {Boolean} clickable (true|false) default false
1085
1086  *     
1087  * @constructor
1088  * Create a new Container
1089  * @param {Object} config The config object
1090  */
1091
1092 Roo.bootstrap.Container = function(config){
1093     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1094     
1095     this.addEvents({
1096         // raw events
1097          /**
1098          * @event expand
1099          * After the panel has been expand
1100          * 
1101          * @param {Roo.bootstrap.Container} this
1102          */
1103         "expand" : true,
1104         /**
1105          * @event collapse
1106          * After the panel has been collapsed
1107          * 
1108          * @param {Roo.bootstrap.Container} this
1109          */
1110         "collapse" : true,
1111         /**
1112          * @event click
1113          * When a element is chick
1114          * @param {Roo.bootstrap.Container} this
1115          * @param {Roo.EventObject} e
1116          */
1117         "click" : true
1118     });
1119 };
1120
1121 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1122     
1123     jumbotron : false,
1124     well: '',
1125     panel : '',
1126     header: '',
1127     footer : '',
1128     sticky: '',
1129     tag : false,
1130     alert : false,
1131     fa: false,
1132     icon : false,
1133     expandable : false,
1134     rheader : '',
1135     expanded : true,
1136     clickable: false,
1137   
1138      
1139     getChildContainer : function() {
1140         
1141         if(!this.el){
1142             return false;
1143         }
1144         
1145         if (this.panel.length) {
1146             return this.el.select('.panel-body',true).first();
1147         }
1148         
1149         return this.el;
1150     },
1151     
1152     
1153     getAutoCreate : function(){
1154         
1155         var cfg = {
1156             tag : this.tag || 'div',
1157             html : '',
1158             cls : ''
1159         };
1160         if (this.jumbotron) {
1161             cfg.cls = 'jumbotron';
1162         }
1163         
1164         
1165         
1166         // - this is applied by the parent..
1167         //if (this.cls) {
1168         //    cfg.cls = this.cls + '';
1169         //}
1170         
1171         if (this.sticky.length) {
1172             
1173             var bd = Roo.get(document.body);
1174             if (!bd.hasClass('bootstrap-sticky')) {
1175                 bd.addClass('bootstrap-sticky');
1176                 Roo.select('html',true).setStyle('height', '100%');
1177             }
1178              
1179             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1180         }
1181         
1182         
1183         if (this.well.length) {
1184             switch (this.well) {
1185                 case 'lg':
1186                 case 'sm':
1187                     cfg.cls +=' well well-' +this.well;
1188                     break;
1189                 default:
1190                     cfg.cls +=' well';
1191                     break;
1192             }
1193         }
1194         
1195         if (this.hidden) {
1196             cfg.cls += ' hidden';
1197         }
1198         
1199         
1200         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1201             cfg.cls +=' alert alert-' + this.alert;
1202         }
1203         
1204         var body = cfg;
1205         
1206         if (this.panel.length) {
1207             cfg.cls += ' panel panel-' + this.panel;
1208             cfg.cn = [];
1209             if (this.header.length) {
1210                 
1211                 var h = [];
1212                 
1213                 if(this.expandable){
1214                     
1215                     cfg.cls = cfg.cls + ' expandable';
1216                     
1217                     h.push({
1218                         tag: 'i',
1219                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1220                     });
1221                     
1222                 }
1223                 
1224                 h.push(
1225                     {
1226                         tag: 'span',
1227                         cls : 'panel-title',
1228                         html : (this.expandable ? '&nbsp;' : '') + this.header
1229                     },
1230                     {
1231                         tag: 'span',
1232                         cls: 'panel-header-right',
1233                         html: this.rheader
1234                     }
1235                 );
1236                 
1237                 cfg.cn.push({
1238                     cls : 'panel-heading',
1239                     style : this.expandable ? 'cursor: pointer' : '',
1240                     cn : h
1241                 });
1242                 
1243             }
1244             
1245             body = false;
1246             cfg.cn.push({
1247                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1248                 html : this.html
1249             });
1250             
1251             
1252             if (this.footer.length) {
1253                 cfg.cn.push({
1254                     cls : 'panel-footer',
1255                     html : this.footer
1256                     
1257                 });
1258             }
1259             
1260         }
1261         
1262         if (body) {
1263             body.html = this.html || cfg.html;
1264             // prefix with the icons..
1265             if (this.fa) {
1266                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1267             }
1268             if (this.icon) {
1269                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1270             }
1271             
1272             
1273         }
1274         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1275             cfg.cls =  'container';
1276         }
1277         
1278         return cfg;
1279     },
1280     
1281     initEvents: function() 
1282     {
1283         if(this.expandable){
1284             var headerEl = this.headerEl();
1285         
1286             if(headerEl){
1287                 headerEl.on('click', this.onToggleClick, this);
1288             }
1289         }
1290         
1291         if(this.clickable){
1292             this.el.on('click', this.onClick, this);
1293         }
1294         
1295     },
1296     
1297     onToggleClick : function()
1298     {
1299         var headerEl = this.headerEl();
1300         
1301         if(!headerEl){
1302             return;
1303         }
1304         
1305         if(this.expanded){
1306             this.collapse();
1307             return;
1308         }
1309         
1310         this.expand();
1311     },
1312     
1313     expand : function()
1314     {
1315         if(this.fireEvent('expand', this)) {
1316             
1317             this.expanded = true;
1318             
1319             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1320             
1321             this.el.select('.panel-body',true).first().removeClass('hide');
1322             
1323             var toggleEl = this.toggleEl();
1324
1325             if(!toggleEl){
1326                 return;
1327             }
1328
1329             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1330         }
1331         
1332     },
1333     
1334     collapse : function()
1335     {
1336         if(this.fireEvent('collapse', this)) {
1337             
1338             this.expanded = false;
1339             
1340             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1341             this.el.select('.panel-body',true).first().addClass('hide');
1342         
1343             var toggleEl = this.toggleEl();
1344
1345             if(!toggleEl){
1346                 return;
1347             }
1348
1349             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1350         }
1351     },
1352     
1353     toggleEl : function()
1354     {
1355         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1356             return;
1357         }
1358         
1359         return this.el.select('.panel-heading .fa',true).first();
1360     },
1361     
1362     headerEl : function()
1363     {
1364         if(!this.el || !this.panel.length || !this.header.length){
1365             return;
1366         }
1367         
1368         return this.el.select('.panel-heading',true).first()
1369     },
1370     
1371     bodyEl : function()
1372     {
1373         if(!this.el || !this.panel.length){
1374             return;
1375         }
1376         
1377         return this.el.select('.panel-body',true).first()
1378     },
1379     
1380     titleEl : function()
1381     {
1382         if(!this.el || !this.panel.length || !this.header.length){
1383             return;
1384         }
1385         
1386         return this.el.select('.panel-title',true).first();
1387     },
1388     
1389     setTitle : function(v)
1390     {
1391         var titleEl = this.titleEl();
1392         
1393         if(!titleEl){
1394             return;
1395         }
1396         
1397         titleEl.dom.innerHTML = v;
1398     },
1399     
1400     getTitle : function()
1401     {
1402         
1403         var titleEl = this.titleEl();
1404         
1405         if(!titleEl){
1406             return '';
1407         }
1408         
1409         return titleEl.dom.innerHTML;
1410     },
1411     
1412     setRightTitle : function(v)
1413     {
1414         var t = this.el.select('.panel-header-right',true).first();
1415         
1416         if(!t){
1417             return;
1418         }
1419         
1420         t.dom.innerHTML = v;
1421     },
1422     
1423     onClick : function(e)
1424     {
1425         e.preventDefault();
1426         
1427         this.fireEvent('click', this, e);
1428     }
1429 });
1430
1431  /*
1432  * - LGPL
1433  *
1434  * image
1435  * 
1436  */
1437
1438
1439 /**
1440  * @class Roo.bootstrap.Img
1441  * @extends Roo.bootstrap.Component
1442  * Bootstrap Img class
1443  * @cfg {Boolean} imgResponsive false | true
1444  * @cfg {String} border rounded | circle | thumbnail
1445  * @cfg {String} src image source
1446  * @cfg {String} alt image alternative text
1447  * @cfg {String} href a tag href
1448  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1449  * @cfg {String} xsUrl xs image source
1450  * @cfg {String} smUrl sm image source
1451  * @cfg {String} mdUrl md image source
1452  * @cfg {String} lgUrl lg image source
1453  * 
1454  * @constructor
1455  * Create a new Input
1456  * @param {Object} config The config object
1457  */
1458
1459 Roo.bootstrap.Img = function(config){
1460     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1461     
1462     this.addEvents({
1463         // img events
1464         /**
1465          * @event click
1466          * The img click event for the img.
1467          * @param {Roo.EventObject} e
1468          */
1469         "click" : true
1470     });
1471 };
1472
1473 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1474     
1475     imgResponsive: true,
1476     border: '',
1477     src: 'about:blank',
1478     href: false,
1479     target: false,
1480     xsUrl: '',
1481     smUrl: '',
1482     mdUrl: '',
1483     lgUrl: '',
1484
1485     getAutoCreate : function()
1486     {   
1487         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1488             return this.createSingleImg();
1489         }
1490         
1491         var cfg = {
1492             tag: 'div',
1493             cls: 'roo-image-responsive-group',
1494             cn: []
1495         };
1496         var _this = this;
1497         
1498         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1499             
1500             if(!_this[size + 'Url']){
1501                 return;
1502             }
1503             
1504             var img = {
1505                 tag: 'img',
1506                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1507                 html: _this.html || cfg.html,
1508                 src: _this[size + 'Url']
1509             };
1510             
1511             img.cls += ' roo-image-responsive-' + size;
1512             
1513             var s = ['xs', 'sm', 'md', 'lg'];
1514             
1515             s.splice(s.indexOf(size), 1);
1516             
1517             Roo.each(s, function(ss){
1518                 img.cls += ' hidden-' + ss;
1519             });
1520             
1521             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1522                 cfg.cls += ' img-' + _this.border;
1523             }
1524             
1525             if(_this.alt){
1526                 cfg.alt = _this.alt;
1527             }
1528             
1529             if(_this.href){
1530                 var a = {
1531                     tag: 'a',
1532                     href: _this.href,
1533                     cn: [
1534                         img
1535                     ]
1536                 };
1537
1538                 if(this.target){
1539                     a.target = _this.target;
1540                 }
1541             }
1542             
1543             cfg.cn.push((_this.href) ? a : img);
1544             
1545         });
1546         
1547         return cfg;
1548     },
1549     
1550     createSingleImg : function()
1551     {
1552         var cfg = {
1553             tag: 'img',
1554             cls: (this.imgResponsive) ? 'img-responsive' : '',
1555             html : null,
1556             src : 'about:blank'  // just incase src get's set to undefined?!?
1557         };
1558         
1559         cfg.html = this.html || cfg.html;
1560         
1561         cfg.src = this.src || cfg.src;
1562         
1563         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1564             cfg.cls += ' img-' + this.border;
1565         }
1566         
1567         if(this.alt){
1568             cfg.alt = this.alt;
1569         }
1570         
1571         if(this.href){
1572             var a = {
1573                 tag: 'a',
1574                 href: this.href,
1575                 cn: [
1576                     cfg
1577                 ]
1578             };
1579             
1580             if(this.target){
1581                 a.target = this.target;
1582             }
1583             
1584         }
1585         
1586         return (this.href) ? a : cfg;
1587     },
1588     
1589     initEvents: function() 
1590     {
1591         if(!this.href){
1592             this.el.on('click', this.onClick, this);
1593         }
1594         
1595     },
1596     
1597     onClick : function(e)
1598     {
1599         Roo.log('img onclick');
1600         this.fireEvent('click', this, e);
1601     },
1602     /**
1603      * Sets the url of the image - used to update it
1604      * @param {String} url the url of the image
1605      */
1606     
1607     setSrc : function(url)
1608     {
1609         this.src =  url;
1610         
1611         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1612             this.el.dom.src =  url;
1613             return;
1614         }
1615         
1616         this.el.select('img', true).first().dom.src =  url;
1617     }
1618     
1619     
1620    
1621 });
1622
1623  /*
1624  * - LGPL
1625  *
1626  * image
1627  * 
1628  */
1629
1630
1631 /**
1632  * @class Roo.bootstrap.Link
1633  * @extends Roo.bootstrap.Component
1634  * Bootstrap Link Class
1635  * @cfg {String} alt image alternative text
1636  * @cfg {String} href a tag href
1637  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1638  * @cfg {String} html the content of the link.
1639  * @cfg {String} anchor name for the anchor link
1640  * @cfg {String} fa - favicon
1641
1642  * @cfg {Boolean} preventDefault (true | false) default false
1643
1644  * 
1645  * @constructor
1646  * Create a new Input
1647  * @param {Object} config The config object
1648  */
1649
1650 Roo.bootstrap.Link = function(config){
1651     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1652     
1653     this.addEvents({
1654         // img events
1655         /**
1656          * @event click
1657          * The img click event for the img.
1658          * @param {Roo.EventObject} e
1659          */
1660         "click" : true
1661     });
1662 };
1663
1664 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1665     
1666     href: false,
1667     target: false,
1668     preventDefault: false,
1669     anchor : false,
1670     alt : false,
1671     fa: false,
1672
1673
1674     getAutoCreate : function()
1675     {
1676         var html = this.html || '';
1677         
1678         if (this.fa !== false) {
1679             html = '<i class="fa fa-' + this.fa + '"></i>';
1680         }
1681         var cfg = {
1682             tag: 'a'
1683         };
1684         // anchor's do not require html/href...
1685         if (this.anchor === false) {
1686             cfg.html = html;
1687             cfg.href = this.href || '#';
1688         } else {
1689             cfg.name = this.anchor;
1690             if (this.html !== false || this.fa !== false) {
1691                 cfg.html = html;
1692             }
1693             if (this.href !== false) {
1694                 cfg.href = this.href;
1695             }
1696         }
1697         
1698         if(this.alt !== false){
1699             cfg.alt = this.alt;
1700         }
1701         
1702         
1703         if(this.target !== false) {
1704             cfg.target = this.target;
1705         }
1706         
1707         return cfg;
1708     },
1709     
1710     initEvents: function() {
1711         
1712         if(!this.href || this.preventDefault){
1713             this.el.on('click', this.onClick, this);
1714         }
1715     },
1716     
1717     onClick : function(e)
1718     {
1719         if(this.preventDefault){
1720             e.preventDefault();
1721         }
1722         //Roo.log('img onclick');
1723         this.fireEvent('click', this, e);
1724     }
1725    
1726 });
1727
1728  /*
1729  * - LGPL
1730  *
1731  * header
1732  * 
1733  */
1734
1735 /**
1736  * @class Roo.bootstrap.Header
1737  * @extends Roo.bootstrap.Component
1738  * Bootstrap Header class
1739  * @cfg {String} html content of header
1740  * @cfg {Number} level (1|2|3|4|5|6) default 1
1741  * 
1742  * @constructor
1743  * Create a new Header
1744  * @param {Object} config The config object
1745  */
1746
1747
1748 Roo.bootstrap.Header  = function(config){
1749     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1750 };
1751
1752 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1753     
1754     //href : false,
1755     html : false,
1756     level : 1,
1757     
1758     
1759     
1760     getAutoCreate : function(){
1761         
1762         
1763         
1764         var cfg = {
1765             tag: 'h' + (1 *this.level),
1766             html: this.html || ''
1767         } ;
1768         
1769         return cfg;
1770     }
1771    
1772 });
1773
1774  
1775
1776  /*
1777  * Based on:
1778  * Ext JS Library 1.1.1
1779  * Copyright(c) 2006-2007, Ext JS, LLC.
1780  *
1781  * Originally Released Under LGPL - original licence link has changed is not relivant.
1782  *
1783  * Fork - LGPL
1784  * <script type="text/javascript">
1785  */
1786  
1787 /**
1788  * @class Roo.bootstrap.MenuMgr
1789  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1790  * @singleton
1791  */
1792 Roo.bootstrap.MenuMgr = function(){
1793    var menus, active, groups = {}, attached = false, lastShow = new Date();
1794
1795    // private - called when first menu is created
1796    function init(){
1797        menus = {};
1798        active = new Roo.util.MixedCollection();
1799        Roo.get(document).addKeyListener(27, function(){
1800            if(active.length > 0){
1801                hideAll();
1802            }
1803        });
1804    }
1805
1806    // private
1807    function hideAll(){
1808        if(active && active.length > 0){
1809            var c = active.clone();
1810            c.each(function(m){
1811                m.hide();
1812            });
1813        }
1814    }
1815
1816    // private
1817    function onHide(m){
1818        active.remove(m);
1819        if(active.length < 1){
1820            Roo.get(document).un("mouseup", onMouseDown);
1821             
1822            attached = false;
1823        }
1824    }
1825
1826    // private
1827    function onShow(m){
1828        var last = active.last();
1829        lastShow = new Date();
1830        active.add(m);
1831        if(!attached){
1832           Roo.get(document).on("mouseup", onMouseDown);
1833            
1834            attached = true;
1835        }
1836        if(m.parentMenu){
1837           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1838           m.parentMenu.activeChild = m;
1839        }else if(last && last.isVisible()){
1840           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1841        }
1842    }
1843
1844    // private
1845    function onBeforeHide(m){
1846        if(m.activeChild){
1847            m.activeChild.hide();
1848        }
1849        if(m.autoHideTimer){
1850            clearTimeout(m.autoHideTimer);
1851            delete m.autoHideTimer;
1852        }
1853    }
1854
1855    // private
1856    function onBeforeShow(m){
1857        var pm = m.parentMenu;
1858        if(!pm && !m.allowOtherMenus){
1859            hideAll();
1860        }else if(pm && pm.activeChild && active != m){
1861            pm.activeChild.hide();
1862        }
1863    }
1864
1865    // private this should really trigger on mouseup..
1866    function onMouseDown(e){
1867         Roo.log("on Mouse Up");
1868         
1869         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1870             Roo.log("MenuManager hideAll");
1871             hideAll();
1872             e.stopEvent();
1873         }
1874         
1875         
1876    }
1877
1878    // private
1879    function onBeforeCheck(mi, state){
1880        if(state){
1881            var g = groups[mi.group];
1882            for(var i = 0, l = g.length; i < l; i++){
1883                if(g[i] != mi){
1884                    g[i].setChecked(false);
1885                }
1886            }
1887        }
1888    }
1889
1890    return {
1891
1892        /**
1893         * Hides all menus that are currently visible
1894         */
1895        hideAll : function(){
1896             hideAll();  
1897        },
1898
1899        // private
1900        register : function(menu){
1901            if(!menus){
1902                init();
1903            }
1904            menus[menu.id] = menu;
1905            menu.on("beforehide", onBeforeHide);
1906            menu.on("hide", onHide);
1907            menu.on("beforeshow", onBeforeShow);
1908            menu.on("show", onShow);
1909            var g = menu.group;
1910            if(g && menu.events["checkchange"]){
1911                if(!groups[g]){
1912                    groups[g] = [];
1913                }
1914                groups[g].push(menu);
1915                menu.on("checkchange", onCheck);
1916            }
1917        },
1918
1919         /**
1920          * Returns a {@link Roo.menu.Menu} object
1921          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1922          * be used to generate and return a new Menu instance.
1923          */
1924        get : function(menu){
1925            if(typeof menu == "string"){ // menu id
1926                return menus[menu];
1927            }else if(menu.events){  // menu instance
1928                return menu;
1929            }
1930            /*else if(typeof menu.length == 'number'){ // array of menu items?
1931                return new Roo.bootstrap.Menu({items:menu});
1932            }else{ // otherwise, must be a config
1933                return new Roo.bootstrap.Menu(menu);
1934            }
1935            */
1936            return false;
1937        },
1938
1939        // private
1940        unregister : function(menu){
1941            delete menus[menu.id];
1942            menu.un("beforehide", onBeforeHide);
1943            menu.un("hide", onHide);
1944            menu.un("beforeshow", onBeforeShow);
1945            menu.un("show", onShow);
1946            var g = menu.group;
1947            if(g && menu.events["checkchange"]){
1948                groups[g].remove(menu);
1949                menu.un("checkchange", onCheck);
1950            }
1951        },
1952
1953        // private
1954        registerCheckable : function(menuItem){
1955            var g = menuItem.group;
1956            if(g){
1957                if(!groups[g]){
1958                    groups[g] = [];
1959                }
1960                groups[g].push(menuItem);
1961                menuItem.on("beforecheckchange", onBeforeCheck);
1962            }
1963        },
1964
1965        // private
1966        unregisterCheckable : function(menuItem){
1967            var g = menuItem.group;
1968            if(g){
1969                groups[g].remove(menuItem);
1970                menuItem.un("beforecheckchange", onBeforeCheck);
1971            }
1972        }
1973    };
1974 }();/*
1975  * - LGPL
1976  *
1977  * menu
1978  * 
1979  */
1980
1981 /**
1982  * @class Roo.bootstrap.Menu
1983  * @extends Roo.bootstrap.Component
1984  * Bootstrap Menu class - container for MenuItems
1985  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1986  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1987  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1988  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1989  * 
1990  * @constructor
1991  * Create a new Menu
1992  * @param {Object} config The config object
1993  */
1994
1995
1996 Roo.bootstrap.Menu = function(config){
1997     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1998     if (this.registerMenu && this.type != 'treeview')  {
1999         Roo.bootstrap.MenuMgr.register(this);
2000     }
2001     this.addEvents({
2002         /**
2003          * @event beforeshow
2004          * Fires before this menu is displayed
2005          * @param {Roo.menu.Menu} this
2006          */
2007         beforeshow : true,
2008         /**
2009          * @event beforehide
2010          * Fires before this menu is hidden
2011          * @param {Roo.menu.Menu} this
2012          */
2013         beforehide : true,
2014         /**
2015          * @event show
2016          * Fires after this menu is displayed
2017          * @param {Roo.menu.Menu} this
2018          */
2019         show : true,
2020         /**
2021          * @event hide
2022          * Fires after this menu is hidden
2023          * @param {Roo.menu.Menu} this
2024          */
2025         hide : true,
2026         /**
2027          * @event click
2028          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2029          * @param {Roo.menu.Menu} this
2030          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2031          * @param {Roo.EventObject} e
2032          */
2033         click : true,
2034         /**
2035          * @event mouseover
2036          * Fires when the mouse is hovering over this menu
2037          * @param {Roo.menu.Menu} this
2038          * @param {Roo.EventObject} e
2039          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2040          */
2041         mouseover : true,
2042         /**
2043          * @event mouseout
2044          * Fires when the mouse exits this menu
2045          * @param {Roo.menu.Menu} this
2046          * @param {Roo.EventObject} e
2047          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2048          */
2049         mouseout : true,
2050         /**
2051          * @event itemclick
2052          * Fires when a menu item contained in this menu is clicked
2053          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2054          * @param {Roo.EventObject} e
2055          */
2056         itemclick: true
2057     });
2058     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2059 };
2060
2061 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2062     
2063    /// html : false,
2064     //align : '',
2065     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2066     type: false,
2067     /**
2068      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2069      */
2070     registerMenu : true,
2071     
2072     menuItems :false, // stores the menu items..
2073     
2074     hidden:true,
2075         
2076     parentMenu : false,
2077     
2078     stopEvent : true,
2079     
2080     isLink : false,
2081     
2082     getChildContainer : function() {
2083         return this.el;  
2084     },
2085     
2086     getAutoCreate : function(){
2087          
2088         //if (['right'].indexOf(this.align)!==-1) {
2089         //    cfg.cn[1].cls += ' pull-right'
2090         //}
2091         
2092         
2093         var cfg = {
2094             tag : 'ul',
2095             cls : 'dropdown-menu' ,
2096             style : 'z-index:1000'
2097             
2098         };
2099         
2100         if (this.type === 'submenu') {
2101             cfg.cls = 'submenu active';
2102         }
2103         if (this.type === 'treeview') {
2104             cfg.cls = 'treeview-menu';
2105         }
2106         
2107         return cfg;
2108     },
2109     initEvents : function() {
2110         
2111        // Roo.log("ADD event");
2112        // Roo.log(this.triggerEl.dom);
2113         
2114         this.triggerEl.on('click', this.onTriggerClick, this);
2115         
2116         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2117         
2118         this.triggerEl.addClass('dropdown-toggle');
2119         
2120         if (Roo.isTouch) {
2121             this.el.on('touchstart'  , this.onTouch, this);
2122         }
2123         this.el.on('click' , this.onClick, this);
2124
2125         this.el.on("mouseover", this.onMouseOver, this);
2126         this.el.on("mouseout", this.onMouseOut, this);
2127         
2128     },
2129     
2130     findTargetItem : function(e)
2131     {
2132         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2133         if(!t){
2134             return false;
2135         }
2136         //Roo.log(t);         Roo.log(t.id);
2137         if(t && t.id){
2138             //Roo.log(this.menuitems);
2139             return this.menuitems.get(t.id);
2140             
2141             //return this.items.get(t.menuItemId);
2142         }
2143         
2144         return false;
2145     },
2146     
2147     onTouch : function(e) 
2148     {
2149         Roo.log("menu.onTouch");
2150         //e.stopEvent(); this make the user popdown broken
2151         this.onClick(e);
2152     },
2153     
2154     onClick : function(e)
2155     {
2156         Roo.log("menu.onClick");
2157         
2158         var t = this.findTargetItem(e);
2159         if(!t || t.isContainer){
2160             return;
2161         }
2162         Roo.log(e);
2163         /*
2164         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2165             if(t == this.activeItem && t.shouldDeactivate(e)){
2166                 this.activeItem.deactivate();
2167                 delete this.activeItem;
2168                 return;
2169             }
2170             if(t.canActivate){
2171                 this.setActiveItem(t, true);
2172             }
2173             return;
2174             
2175             
2176         }
2177         */
2178        
2179         Roo.log('pass click event');
2180         
2181         t.onClick(e);
2182         
2183         this.fireEvent("click", this, t, e);
2184         
2185         var _this = this;
2186         
2187         if(!t.href.length || t.href == '#'){
2188             (function() { _this.hide(); }).defer(100);
2189         }
2190         
2191     },
2192     
2193     onMouseOver : function(e){
2194         var t  = this.findTargetItem(e);
2195         //Roo.log(t);
2196         //if(t){
2197         //    if(t.canActivate && !t.disabled){
2198         //        this.setActiveItem(t, true);
2199         //    }
2200         //}
2201         
2202         this.fireEvent("mouseover", this, e, t);
2203     },
2204     isVisible : function(){
2205         return !this.hidden;
2206     },
2207      onMouseOut : function(e){
2208         var t  = this.findTargetItem(e);
2209         
2210         //if(t ){
2211         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2212         //        this.activeItem.deactivate();
2213         //        delete this.activeItem;
2214         //    }
2215         //}
2216         this.fireEvent("mouseout", this, e, t);
2217     },
2218     
2219     
2220     /**
2221      * Displays this menu relative to another element
2222      * @param {String/HTMLElement/Roo.Element} element The element to align to
2223      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2224      * the element (defaults to this.defaultAlign)
2225      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2226      */
2227     show : function(el, pos, parentMenu){
2228         this.parentMenu = parentMenu;
2229         if(!this.el){
2230             this.render();
2231         }
2232         this.fireEvent("beforeshow", this);
2233         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2234     },
2235      /**
2236      * Displays this menu at a specific xy position
2237      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2238      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2239      */
2240     showAt : function(xy, parentMenu, /* private: */_e){
2241         this.parentMenu = parentMenu;
2242         if(!this.el){
2243             this.render();
2244         }
2245         if(_e !== false){
2246             this.fireEvent("beforeshow", this);
2247             //xy = this.el.adjustForConstraints(xy);
2248         }
2249         
2250         //this.el.show();
2251         this.hideMenuItems();
2252         this.hidden = false;
2253         this.triggerEl.addClass('open');
2254         
2255         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2256             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2257         }
2258         
2259         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2260             this.el.setXY(xy);
2261         }
2262         
2263         this.focus();
2264         this.fireEvent("show", this);
2265     },
2266     
2267     focus : function(){
2268         return;
2269         if(!this.hidden){
2270             this.doFocus.defer(50, this);
2271         }
2272     },
2273
2274     doFocus : function(){
2275         if(!this.hidden){
2276             this.focusEl.focus();
2277         }
2278     },
2279
2280     /**
2281      * Hides this menu and optionally all parent menus
2282      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2283      */
2284     hide : function(deep)
2285     {
2286         
2287         this.hideMenuItems();
2288         if(this.el && this.isVisible()){
2289             this.fireEvent("beforehide", this);
2290             if(this.activeItem){
2291                 this.activeItem.deactivate();
2292                 this.activeItem = null;
2293             }
2294             this.triggerEl.removeClass('open');;
2295             this.hidden = true;
2296             this.fireEvent("hide", this);
2297         }
2298         if(deep === true && this.parentMenu){
2299             this.parentMenu.hide(true);
2300         }
2301     },
2302     
2303     onTriggerClick : function(e)
2304     {
2305         Roo.log('trigger click');
2306         
2307         var target = e.getTarget();
2308         
2309         Roo.log(target.nodeName.toLowerCase());
2310         
2311         if(target.nodeName.toLowerCase() === 'i'){
2312             e.preventDefault();
2313         }
2314         
2315     },
2316     
2317     onTriggerPress  : function(e)
2318     {
2319         Roo.log('trigger press');
2320         //Roo.log(e.getTarget());
2321        // Roo.log(this.triggerEl.dom);
2322        
2323         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2324         var pel = Roo.get(e.getTarget());
2325         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2326             Roo.log('is treeview or dropdown?');
2327             return;
2328         }
2329         
2330         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2331             return;
2332         }
2333         
2334         if (this.isVisible()) {
2335             Roo.log('hide');
2336             this.hide();
2337         } else {
2338             Roo.log('show');
2339             this.show(this.triggerEl, false, false);
2340         }
2341         
2342         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2343             e.stopEvent();
2344         }
2345         
2346     },
2347        
2348     
2349     hideMenuItems : function()
2350     {
2351         Roo.log("hide Menu Items");
2352         if (!this.el) { 
2353             return;
2354         }
2355         //$(backdrop).remove()
2356         this.el.select('.open',true).each(function(aa) {
2357             
2358             aa.removeClass('open');
2359           //var parent = getParent($(this))
2360           //var relatedTarget = { relatedTarget: this }
2361           
2362            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2363           //if (e.isDefaultPrevented()) return
2364            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2365         });
2366     },
2367     addxtypeChild : function (tree, cntr) {
2368         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2369           
2370         this.menuitems.add(comp);
2371         return comp;
2372
2373     },
2374     getEl : function()
2375     {
2376         Roo.log(this.el);
2377         return this.el;
2378     },
2379     
2380     clear : function()
2381     {
2382         this.getEl().dom.innerHTML = '';
2383         this.menuitems.clear();
2384     }
2385 });
2386
2387  
2388  /*
2389  * - LGPL
2390  *
2391  * menu item
2392  * 
2393  */
2394
2395
2396 /**
2397  * @class Roo.bootstrap.MenuItem
2398  * @extends Roo.bootstrap.Component
2399  * Bootstrap MenuItem class
2400  * @cfg {String} html the menu label
2401  * @cfg {String} href the link
2402  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2403  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2404  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2405  * @cfg {String} fa favicon to show on left of menu item.
2406  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2407  * 
2408  * 
2409  * @constructor
2410  * Create a new MenuItem
2411  * @param {Object} config The config object
2412  */
2413
2414
2415 Roo.bootstrap.MenuItem = function(config){
2416     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2417     this.addEvents({
2418         // raw events
2419         /**
2420          * @event click
2421          * The raw click event for the entire grid.
2422          * @param {Roo.bootstrap.MenuItem} this
2423          * @param {Roo.EventObject} e
2424          */
2425         "click" : true
2426     });
2427 };
2428
2429 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2430     
2431     href : false,
2432     html : false,
2433     preventDefault: false,
2434     isContainer : false,
2435     active : false,
2436     fa: false,
2437     
2438     getAutoCreate : function(){
2439         
2440         if(this.isContainer){
2441             return {
2442                 tag: 'li',
2443                 cls: 'dropdown-menu-item'
2444             };
2445         }
2446         var ctag = {
2447             tag: 'span',
2448             html: 'Link'
2449         };
2450         
2451         var anc = {
2452             tag : 'a',
2453             href : '#',
2454             cn : [  ]
2455         };
2456         
2457         if (this.fa !== false) {
2458             anc.cn.push({
2459                 tag : 'i',
2460                 cls : 'fa fa-' + this.fa
2461             });
2462         }
2463         
2464         anc.cn.push(ctag);
2465         
2466         
2467         var cfg= {
2468             tag: 'li',
2469             cls: 'dropdown-menu-item',
2470             cn: [ anc ]
2471         };
2472         if (this.parent().type == 'treeview') {
2473             cfg.cls = 'treeview-menu';
2474         }
2475         if (this.active) {
2476             cfg.cls += ' active';
2477         }
2478         
2479         
2480         
2481         anc.href = this.href || cfg.cn[0].href ;
2482         ctag.html = this.html || cfg.cn[0].html ;
2483         return cfg;
2484     },
2485     
2486     initEvents: function()
2487     {
2488         if (this.parent().type == 'treeview') {
2489             this.el.select('a').on('click', this.onClick, this);
2490         }
2491         
2492         if (this.menu) {
2493             this.menu.parentType = this.xtype;
2494             this.menu.triggerEl = this.el;
2495             this.menu = this.addxtype(Roo.apply({}, this.menu));
2496         }
2497         
2498     },
2499     onClick : function(e)
2500     {
2501         Roo.log('item on click ');
2502         
2503         if(this.preventDefault){
2504             e.preventDefault();
2505         }
2506         //this.parent().hideMenuItems();
2507         
2508         this.fireEvent('click', this, e);
2509     },
2510     getEl : function()
2511     {
2512         return this.el;
2513     } 
2514 });
2515
2516  
2517
2518  /*
2519  * - LGPL
2520  *
2521  * menu separator
2522  * 
2523  */
2524
2525
2526 /**
2527  * @class Roo.bootstrap.MenuSeparator
2528  * @extends Roo.bootstrap.Component
2529  * Bootstrap MenuSeparator class
2530  * 
2531  * @constructor
2532  * Create a new MenuItem
2533  * @param {Object} config The config object
2534  */
2535
2536
2537 Roo.bootstrap.MenuSeparator = function(config){
2538     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2539 };
2540
2541 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2542     
2543     getAutoCreate : function(){
2544         var cfg = {
2545             cls: 'divider',
2546             tag : 'li'
2547         };
2548         
2549         return cfg;
2550     }
2551    
2552 });
2553
2554  
2555
2556  
2557 /*
2558 * Licence: LGPL
2559 */
2560
2561 /**
2562  * @class Roo.bootstrap.Modal
2563  * @extends Roo.bootstrap.Component
2564  * Bootstrap Modal class
2565  * @cfg {String} title Title of dialog
2566  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2567  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2568  * @cfg {Boolean} specificTitle default false
2569  * @cfg {Array} buttons Array of buttons or standard button set..
2570  * @cfg {String} buttonPosition (left|right|center) default right
2571  * @cfg {Boolean} animate default true
2572  * @cfg {Boolean} allow_close default true
2573  * @cfg {Boolean} fitwindow default false
2574  * @cfg {String} size (sm|lg) default empty
2575  *
2576  *
2577  * @constructor
2578  * Create a new Modal Dialog
2579  * @param {Object} config The config object
2580  */
2581
2582 Roo.bootstrap.Modal = function(config){
2583     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2584     this.addEvents({
2585         // raw events
2586         /**
2587          * @event btnclick
2588          * The raw btnclick event for the button
2589          * @param {Roo.EventObject} e
2590          */
2591         "btnclick" : true,
2592         /**
2593          * @event resize
2594          * Fire when dialog resize
2595          * @param {Roo.bootstrap.Modal} this
2596          * @param {Roo.EventObject} e
2597          */
2598         "resize" : true
2599     });
2600     this.buttons = this.buttons || [];
2601
2602     if (this.tmpl) {
2603         this.tmpl = Roo.factory(this.tmpl);
2604     }
2605
2606 };
2607
2608 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2609
2610     title : 'test dialog',
2611
2612     buttons : false,
2613
2614     // set on load...
2615
2616     html: false,
2617
2618     tmp: false,
2619
2620     specificTitle: false,
2621
2622     buttonPosition: 'right',
2623
2624     allow_close : true,
2625
2626     animate : true,
2627
2628     fitwindow: false,
2629
2630
2631      // private
2632     dialogEl: false,
2633     bodyEl:  false,
2634     footerEl:  false,
2635     titleEl:  false,
2636     closeEl:  false,
2637
2638     size: '',
2639
2640
2641     onRender : function(ct, position)
2642     {
2643         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2644
2645         if(!this.el){
2646             var cfg = Roo.apply({},  this.getAutoCreate());
2647             cfg.id = Roo.id();
2648             //if(!cfg.name){
2649             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2650             //}
2651             //if (!cfg.name.length) {
2652             //    delete cfg.name;
2653            // }
2654             if (this.cls) {
2655                 cfg.cls += ' ' + this.cls;
2656             }
2657             if (this.style) {
2658                 cfg.style = this.style;
2659             }
2660             this.el = Roo.get(document.body).createChild(cfg, position);
2661         }
2662         //var type = this.el.dom.type;
2663
2664
2665         if(this.tabIndex !== undefined){
2666             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2667         }
2668
2669         this.dialogEl = this.el.select('.modal-dialog',true).first();
2670         this.bodyEl = this.el.select('.modal-body',true).first();
2671         this.closeEl = this.el.select('.modal-header .close', true).first();
2672         this.headerEl = this.el.select('.modal-header',true).first();
2673         this.titleEl = this.el.select('.modal-title',true).first();
2674         this.footerEl = this.el.select('.modal-footer',true).first();
2675
2676         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2677         this.maskEl.enableDisplayMode("block");
2678         this.maskEl.hide();
2679         //this.el.addClass("x-dlg-modal");
2680
2681         if (this.buttons.length) {
2682             Roo.each(this.buttons, function(bb) {
2683                 var b = Roo.apply({}, bb);
2684                 b.xns = b.xns || Roo.bootstrap;
2685                 b.xtype = b.xtype || 'Button';
2686                 if (typeof(b.listeners) == 'undefined') {
2687                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2688                 }
2689
2690                 var btn = Roo.factory(b);
2691
2692                 btn.render(this.el.select('.modal-footer div').first());
2693
2694             },this);
2695         }
2696         // render the children.
2697         var nitems = [];
2698
2699         if(typeof(this.items) != 'undefined'){
2700             var items = this.items;
2701             delete this.items;
2702
2703             for(var i =0;i < items.length;i++) {
2704                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2705             }
2706         }
2707
2708         this.items = nitems;
2709
2710         // where are these used - they used to be body/close/footer
2711
2712
2713         this.initEvents();
2714         //this.el.addClass([this.fieldClass, this.cls]);
2715
2716     },
2717
2718     getAutoCreate : function(){
2719
2720
2721         var bdy = {
2722                 cls : 'modal-body',
2723                 html : this.html || ''
2724         };
2725
2726         var title = {
2727             tag: 'h4',
2728             cls : 'modal-title',
2729             html : this.title
2730         };
2731
2732         if(this.specificTitle){
2733             title = this.title;
2734
2735         };
2736
2737         var header = [];
2738         if (this.allow_close) {
2739             header.push({
2740                 tag: 'button',
2741                 cls : 'close',
2742                 html : '&times'
2743             });
2744         }
2745
2746         header.push(title);
2747
2748         var size = '';
2749
2750         if(this.size.length){
2751             size = 'modal-' + this.size;
2752         }
2753
2754         var modal = {
2755             cls: "modal",
2756              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                     this.hideing = false;
2890                      this.el.removeClass('show hideing');
2891                 }).defer(150,this);
2892                 
2893             }else{
2894                  this.el.removeClass('show');
2895             }
2896             this.fireEvent('hide', this);
2897         }
2898     },
2899     isVisible : function()
2900     {
2901         
2902         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2903         
2904     },
2905
2906     addButton : function(str, cb)
2907     {
2908
2909
2910         var b = Roo.apply({}, { html : str } );
2911         b.xns = b.xns || Roo.bootstrap;
2912         b.xtype = b.xtype || 'Button';
2913         if (typeof(b.listeners) == 'undefined') {
2914             b.listeners = { click : cb.createDelegate(this)  };
2915         }
2916
2917         var btn = Roo.factory(b);
2918
2919         btn.render(this.el.select('.modal-footer div').first());
2920
2921         return btn;
2922
2923     },
2924
2925     setDefaultButton : function(btn)
2926     {
2927         //this.el.select('.modal-footer').()
2928     },
2929     diff : false,
2930
2931     resizeTo: function(w,h)
2932     {
2933         // skip.. ?? why??
2934
2935         this.dialogEl.setWidth(w);
2936         if (this.diff === false) {
2937             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2938         }
2939
2940         this.bodyEl.setHeight(h-this.diff);
2941
2942         this.fireEvent('resize', this);
2943
2944     },
2945     setContentSize  : function(w, h)
2946     {
2947
2948     },
2949     onButtonClick: function(btn,e)
2950     {
2951         //Roo.log([a,b,c]);
2952         this.fireEvent('btnclick', btn.name, e);
2953     },
2954      /**
2955      * Set the title of the Dialog
2956      * @param {String} str new Title
2957      */
2958     setTitle: function(str) {
2959         this.titleEl.dom.innerHTML = str;
2960     },
2961     /**
2962      * Set the body of the Dialog
2963      * @param {String} str new Title
2964      */
2965     setBody: function(str) {
2966         this.bodyEl.dom.innerHTML = str;
2967     },
2968     /**
2969      * Set the body of the Dialog using the template
2970      * @param {Obj} data - apply this data to the template and replace the body contents.
2971      */
2972     applyBody: function(obj)
2973     {
2974         if (!this.tmpl) {
2975             Roo.log("Error - using apply Body without a template");
2976             //code
2977         }
2978         this.tmpl.overwrite(this.bodyEl, obj);
2979     }
2980
2981 });
2982
2983
2984 Roo.apply(Roo.bootstrap.Modal,  {
2985     /**
2986          * Button config that displays a single OK button
2987          * @type Object
2988          */
2989         OK :  [{
2990             name : 'ok',
2991             weight : 'primary',
2992             html : 'OK'
2993         }],
2994         /**
2995          * Button config that displays Yes and No buttons
2996          * @type Object
2997          */
2998         YESNO : [
2999             {
3000                 name  : 'no',
3001                 html : 'No'
3002             },
3003             {
3004                 name  :'yes',
3005                 weight : 'primary',
3006                 html : 'Yes'
3007             }
3008         ],
3009
3010         /**
3011          * Button config that displays OK and Cancel buttons
3012          * @type Object
3013          */
3014         OKCANCEL : [
3015             {
3016                name : 'cancel',
3017                 html : 'Cancel'
3018             },
3019             {
3020                 name : 'ok',
3021                 weight : 'primary',
3022                 html : 'OK'
3023             }
3024         ],
3025         /**
3026          * Button config that displays Yes, No and Cancel buttons
3027          * @type Object
3028          */
3029         YESNOCANCEL : [
3030             {
3031                 name : 'yes',
3032                 weight : 'primary',
3033                 html : 'Yes'
3034             },
3035             {
3036                 name : 'no',
3037                 html : 'No'
3038             },
3039             {
3040                 name : 'cancel',
3041                 html : 'Cancel'
3042             }
3043         ],
3044         
3045         zIndex : 10001
3046 });
3047 /*
3048  * - LGPL
3049  *
3050  * messagebox - can be used as a replace
3051  * 
3052  */
3053 /**
3054  * @class Roo.MessageBox
3055  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3056  * Example usage:
3057  *<pre><code>
3058 // Basic alert:
3059 Roo.Msg.alert('Status', 'Changes saved successfully.');
3060
3061 // Prompt for user data:
3062 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3063     if (btn == 'ok'){
3064         // process text value...
3065     }
3066 });
3067
3068 // Show a dialog using config options:
3069 Roo.Msg.show({
3070    title:'Save Changes?',
3071    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3072    buttons: Roo.Msg.YESNOCANCEL,
3073    fn: processResult,
3074    animEl: 'elId'
3075 });
3076 </code></pre>
3077  * @singleton
3078  */
3079 Roo.bootstrap.MessageBox = function(){
3080     var dlg, opt, mask, waitTimer;
3081     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3082     var buttons, activeTextEl, bwidth;
3083
3084     
3085     // private
3086     var handleButton = function(button){
3087         dlg.hide();
3088         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3089     };
3090
3091     // private
3092     var handleHide = function(){
3093         if(opt && opt.cls){
3094             dlg.el.removeClass(opt.cls);
3095         }
3096         //if(waitTimer){
3097         //    Roo.TaskMgr.stop(waitTimer);
3098         //    waitTimer = null;
3099         //}
3100     };
3101
3102     // private
3103     var updateButtons = function(b){
3104         var width = 0;
3105         if(!b){
3106             buttons["ok"].hide();
3107             buttons["cancel"].hide();
3108             buttons["yes"].hide();
3109             buttons["no"].hide();
3110             //dlg.footer.dom.style.display = 'none';
3111             return width;
3112         }
3113         dlg.footerEl.dom.style.display = '';
3114         for(var k in buttons){
3115             if(typeof buttons[k] != "function"){
3116                 if(b[k]){
3117                     buttons[k].show();
3118                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3119                     width += buttons[k].el.getWidth()+15;
3120                 }else{
3121                     buttons[k].hide();
3122                 }
3123             }
3124         }
3125         return width;
3126     };
3127
3128     // private
3129     var handleEsc = function(d, k, e){
3130         if(opt && opt.closable !== false){
3131             dlg.hide();
3132         }
3133         if(e){
3134             e.stopEvent();
3135         }
3136     };
3137
3138     return {
3139         /**
3140          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3141          * @return {Roo.BasicDialog} The BasicDialog element
3142          */
3143         getDialog : function(){
3144            if(!dlg){
3145                 dlg = new Roo.bootstrap.Modal( {
3146                     //draggable: true,
3147                     //resizable:false,
3148                     //constraintoviewport:false,
3149                     //fixedcenter:true,
3150                     //collapsible : false,
3151                     //shim:true,
3152                     //modal: true,
3153                 //    width: 'auto',
3154                   //  height:100,
3155                     //buttonAlign:"center",
3156                     closeClick : function(){
3157                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3158                             handleButton("no");
3159                         }else{
3160                             handleButton("cancel");
3161                         }
3162                     }
3163                 });
3164                 dlg.render();
3165                 dlg.on("hide", handleHide);
3166                 mask = dlg.mask;
3167                 //dlg.addKeyListener(27, handleEsc);
3168                 buttons = {};
3169                 this.buttons = buttons;
3170                 var bt = this.buttonText;
3171                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3172                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3173                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3174                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3175                 //Roo.log(buttons);
3176                 bodyEl = dlg.bodyEl.createChild({
3177
3178                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3179                         '<textarea class="roo-mb-textarea"></textarea>' +
3180                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3181                 });
3182                 msgEl = bodyEl.dom.firstChild;
3183                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3184                 textboxEl.enableDisplayMode();
3185                 textboxEl.addKeyListener([10,13], function(){
3186                     if(dlg.isVisible() && opt && opt.buttons){
3187                         if(opt.buttons.ok){
3188                             handleButton("ok");
3189                         }else if(opt.buttons.yes){
3190                             handleButton("yes");
3191                         }
3192                     }
3193                 });
3194                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3195                 textareaEl.enableDisplayMode();
3196                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3197                 progressEl.enableDisplayMode();
3198                 
3199                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3200                 var pf = progressEl.dom.firstChild;
3201                 if (pf) {
3202                     pp = Roo.get(pf.firstChild);
3203                     pp.setHeight(pf.offsetHeight);
3204                 }
3205                 
3206             }
3207             return dlg;
3208         },
3209
3210         /**
3211          * Updates the message box body text
3212          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3213          * the XHTML-compliant non-breaking space character '&amp;#160;')
3214          * @return {Roo.MessageBox} This message box
3215          */
3216         updateText : function(text)
3217         {
3218             if(!dlg.isVisible() && !opt.width){
3219                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3220                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3221             }
3222             msgEl.innerHTML = text || '&#160;';
3223       
3224             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3225             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3226             var w = Math.max(
3227                     Math.min(opt.width || cw , this.maxWidth), 
3228                     Math.max(opt.minWidth || this.minWidth, bwidth)
3229             );
3230             if(opt.prompt){
3231                 activeTextEl.setWidth(w);
3232             }
3233             if(dlg.isVisible()){
3234                 dlg.fixedcenter = false;
3235             }
3236             // to big, make it scroll. = But as usual stupid IE does not support
3237             // !important..
3238             
3239             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3240                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3241                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3242             } else {
3243                 bodyEl.dom.style.height = '';
3244                 bodyEl.dom.style.overflowY = '';
3245             }
3246             if (cw > w) {
3247                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3248             } else {
3249                 bodyEl.dom.style.overflowX = '';
3250             }
3251             
3252             dlg.setContentSize(w, bodyEl.getHeight());
3253             if(dlg.isVisible()){
3254                 dlg.fixedcenter = true;
3255             }
3256             return this;
3257         },
3258
3259         /**
3260          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3261          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3262          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3263          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3264          * @return {Roo.MessageBox} This message box
3265          */
3266         updateProgress : function(value, text){
3267             if(text){
3268                 this.updateText(text);
3269             }
3270             
3271             if (pp) { // weird bug on my firefox - for some reason this is not defined
3272                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3273                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3274             }
3275             return this;
3276         },        
3277
3278         /**
3279          * Returns true if the message box is currently displayed
3280          * @return {Boolean} True if the message box is visible, else false
3281          */
3282         isVisible : function(){
3283             return dlg && dlg.isVisible();  
3284         },
3285
3286         /**
3287          * Hides the message box if it is displayed
3288          */
3289         hide : function(){
3290             if(this.isVisible()){
3291                 dlg.hide();
3292             }  
3293         },
3294
3295         /**
3296          * Displays a new message box, or reinitializes an existing message box, based on the config options
3297          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3298          * The following config object properties are supported:
3299          * <pre>
3300 Property    Type             Description
3301 ----------  ---------------  ------------------------------------------------------------------------------------
3302 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3303                                    closes (defaults to undefined)
3304 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3305                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3306 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3307                                    progress and wait dialogs will ignore this property and always hide the
3308                                    close button as they can only be closed programmatically.
3309 cls               String           A custom CSS class to apply to the message box element
3310 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3311                                    displayed (defaults to 75)
3312 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3313                                    function will be btn (the name of the button that was clicked, if applicable,
3314                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3315                                    Progress and wait dialogs will ignore this option since they do not respond to
3316                                    user actions and can only be closed programmatically, so any required function
3317                                    should be called by the same code after it closes the dialog.
3318 icon              String           A CSS class that provides a background image to be used as an icon for
3319                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3320 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3321 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3322 modal             Boolean          False to allow user interaction with the page while the message box is
3323                                    displayed (defaults to true)
3324 msg               String           A string that will replace the existing message box body text (defaults
3325                                    to the XHTML-compliant non-breaking space character '&#160;')
3326 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3327 progress          Boolean          True to display a progress bar (defaults to false)
3328 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3329 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3330 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3331 title             String           The title text
3332 value             String           The string value to set into the active textbox element if displayed
3333 wait              Boolean          True to display a progress bar (defaults to false)
3334 width             Number           The width of the dialog in pixels
3335 </pre>
3336          *
3337          * Example usage:
3338          * <pre><code>
3339 Roo.Msg.show({
3340    title: 'Address',
3341    msg: 'Please enter your address:',
3342    width: 300,
3343    buttons: Roo.MessageBox.OKCANCEL,
3344    multiline: true,
3345    fn: saveAddress,
3346    animEl: 'addAddressBtn'
3347 });
3348 </code></pre>
3349          * @param {Object} config Configuration options
3350          * @return {Roo.MessageBox} This message box
3351          */
3352         show : function(options)
3353         {
3354             
3355             // this causes nightmares if you show one dialog after another
3356             // especially on callbacks..
3357              
3358             if(this.isVisible()){
3359                 
3360                 this.hide();
3361                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3362                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3363                 Roo.log("New Dialog Message:" +  options.msg )
3364                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3365                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3366                 
3367             }
3368             var d = this.getDialog();
3369             opt = options;
3370             d.setTitle(opt.title || "&#160;");
3371             d.closeEl.setDisplayed(opt.closable !== false);
3372             activeTextEl = textboxEl;
3373             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3374             if(opt.prompt){
3375                 if(opt.multiline){
3376                     textboxEl.hide();
3377                     textareaEl.show();
3378                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3379                         opt.multiline : this.defaultTextHeight);
3380                     activeTextEl = textareaEl;
3381                 }else{
3382                     textboxEl.show();
3383                     textareaEl.hide();
3384                 }
3385             }else{
3386                 textboxEl.hide();
3387                 textareaEl.hide();
3388             }
3389             progressEl.setDisplayed(opt.progress === true);
3390             this.updateProgress(0);
3391             activeTextEl.dom.value = opt.value || "";
3392             if(opt.prompt){
3393                 dlg.setDefaultButton(activeTextEl);
3394             }else{
3395                 var bs = opt.buttons;
3396                 var db = null;
3397                 if(bs && bs.ok){
3398                     db = buttons["ok"];
3399                 }else if(bs && bs.yes){
3400                     db = buttons["yes"];
3401                 }
3402                 dlg.setDefaultButton(db);
3403             }
3404             bwidth = updateButtons(opt.buttons);
3405             this.updateText(opt.msg);
3406             if(opt.cls){
3407                 d.el.addClass(opt.cls);
3408             }
3409             d.proxyDrag = opt.proxyDrag === true;
3410             d.modal = opt.modal !== false;
3411             d.mask = opt.modal !== false ? mask : false;
3412             if(!d.isVisible()){
3413                 // force it to the end of the z-index stack so it gets a cursor in FF
3414                 document.body.appendChild(dlg.el.dom);
3415                 d.animateTarget = null;
3416                 d.show(options.animEl);
3417             }
3418             return this;
3419         },
3420
3421         /**
3422          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3423          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3424          * and closing the message box when the process is complete.
3425          * @param {String} title The title bar text
3426          * @param {String} msg The message box body text
3427          * @return {Roo.MessageBox} This message box
3428          */
3429         progress : function(title, msg){
3430             this.show({
3431                 title : title,
3432                 msg : msg,
3433                 buttons: false,
3434                 progress:true,
3435                 closable:false,
3436                 minWidth: this.minProgressWidth,
3437                 modal : true
3438             });
3439             return this;
3440         },
3441
3442         /**
3443          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3444          * If a callback function is passed it will be called after the user clicks the button, and the
3445          * id of the button that was clicked will be passed as the only parameter to the callback
3446          * (could also be the top-right close button).
3447          * @param {String} title The title bar text
3448          * @param {String} msg The message box body text
3449          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3450          * @param {Object} scope (optional) The scope of the callback function
3451          * @return {Roo.MessageBox} This message box
3452          */
3453         alert : function(title, msg, fn, scope)
3454         {
3455             this.show({
3456                 title : title,
3457                 msg : msg,
3458                 buttons: this.OK,
3459                 fn: fn,
3460                 closable : false,
3461                 scope : scope,
3462                 modal : true
3463             });
3464             return this;
3465         },
3466
3467         /**
3468          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3469          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3470          * You are responsible for closing the message box when the process is complete.
3471          * @param {String} msg The message box body text
3472          * @param {String} title (optional) The title bar text
3473          * @return {Roo.MessageBox} This message box
3474          */
3475         wait : function(msg, title){
3476             this.show({
3477                 title : title,
3478                 msg : msg,
3479                 buttons: false,
3480                 closable:false,
3481                 progress:true,
3482                 modal:true,
3483                 width:300,
3484                 wait:true
3485             });
3486             waitTimer = Roo.TaskMgr.start({
3487                 run: function(i){
3488                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3489                 },
3490                 interval: 1000
3491             });
3492             return this;
3493         },
3494
3495         /**
3496          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3497          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3498          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3499          * @param {String} title The title bar text
3500          * @param {String} msg The message box body text
3501          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3502          * @param {Object} scope (optional) The scope of the callback function
3503          * @return {Roo.MessageBox} This message box
3504          */
3505         confirm : function(title, msg, fn, scope){
3506             this.show({
3507                 title : title,
3508                 msg : msg,
3509                 buttons: this.YESNO,
3510                 fn: fn,
3511                 scope : scope,
3512                 modal : true
3513             });
3514             return this;
3515         },
3516
3517         /**
3518          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3519          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3520          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3521          * (could also be the top-right close button) and the text that was entered will be passed as the two
3522          * parameters to the callback.
3523          * @param {String} title The title bar text
3524          * @param {String} msg The message box body text
3525          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3526          * @param {Object} scope (optional) The scope of the callback function
3527          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3528          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3529          * @return {Roo.MessageBox} This message box
3530          */
3531         prompt : function(title, msg, fn, scope, multiline){
3532             this.show({
3533                 title : title,
3534                 msg : msg,
3535                 buttons: this.OKCANCEL,
3536                 fn: fn,
3537                 minWidth:250,
3538                 scope : scope,
3539                 prompt:true,
3540                 multiline: multiline,
3541                 modal : true
3542             });
3543             return this;
3544         },
3545
3546         /**
3547          * Button config that displays a single OK button
3548          * @type Object
3549          */
3550         OK : {ok:true},
3551         /**
3552          * Button config that displays Yes and No buttons
3553          * @type Object
3554          */
3555         YESNO : {yes:true, no:true},
3556         /**
3557          * Button config that displays OK and Cancel buttons
3558          * @type Object
3559          */
3560         OKCANCEL : {ok:true, cancel:true},
3561         /**
3562          * Button config that displays Yes, No and Cancel buttons
3563          * @type Object
3564          */
3565         YESNOCANCEL : {yes:true, no:true, cancel:true},
3566
3567         /**
3568          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3569          * @type Number
3570          */
3571         defaultTextHeight : 75,
3572         /**
3573          * The maximum width in pixels of the message box (defaults to 600)
3574          * @type Number
3575          */
3576         maxWidth : 600,
3577         /**
3578          * The minimum width in pixels of the message box (defaults to 100)
3579          * @type Number
3580          */
3581         minWidth : 100,
3582         /**
3583          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3584          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3585          * @type Number
3586          */
3587         minProgressWidth : 250,
3588         /**
3589          * An object containing the default button text strings that can be overriden for localized language support.
3590          * Supported properties are: ok, cancel, yes and no.
3591          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3592          * @type Object
3593          */
3594         buttonText : {
3595             ok : "OK",
3596             cancel : "Cancel",
3597             yes : "Yes",
3598             no : "No"
3599         }
3600     };
3601 }();
3602
3603 /**
3604  * Shorthand for {@link Roo.MessageBox}
3605  */
3606 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3607 Roo.Msg = Roo.Msg || Roo.MessageBox;
3608 /*
3609  * - LGPL
3610  *
3611  * navbar
3612  * 
3613  */
3614
3615 /**
3616  * @class Roo.bootstrap.Navbar
3617  * @extends Roo.bootstrap.Component
3618  * Bootstrap Navbar class
3619
3620  * @constructor
3621  * Create a new Navbar
3622  * @param {Object} config The config object
3623  */
3624
3625
3626 Roo.bootstrap.Navbar = function(config){
3627     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3628     this.addEvents({
3629         // raw events
3630         /**
3631          * @event beforetoggle
3632          * Fire before toggle the menu
3633          * @param {Roo.EventObject} e
3634          */
3635         "beforetoggle" : true
3636     });
3637 };
3638
3639 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3640     
3641     
3642    
3643     // private
3644     navItems : false,
3645     loadMask : false,
3646     
3647     
3648     getAutoCreate : function(){
3649         
3650         
3651         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3652         
3653     },
3654     
3655     initEvents :function ()
3656     {
3657         //Roo.log(this.el.select('.navbar-toggle',true));
3658         this.el.select('.navbar-toggle',true).on('click', function() {
3659             if(this.fireEvent('beforetoggle', this) !== false){
3660                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3661             }
3662             
3663         }, this);
3664         
3665         var mark = {
3666             tag: "div",
3667             cls:"x-dlg-mask"
3668         };
3669         
3670         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3671         
3672         var size = this.el.getSize();
3673         this.maskEl.setSize(size.width, size.height);
3674         this.maskEl.enableDisplayMode("block");
3675         this.maskEl.hide();
3676         
3677         if(this.loadMask){
3678             this.maskEl.show();
3679         }
3680     },
3681     
3682     
3683     getChildContainer : function()
3684     {
3685         if (this.el.select('.collapse').getCount()) {
3686             return this.el.select('.collapse',true).first();
3687         }
3688         
3689         return this.el;
3690     },
3691     
3692     mask : function()
3693     {
3694         this.maskEl.show();
3695     },
3696     
3697     unmask : function()
3698     {
3699         this.maskEl.hide();
3700     } 
3701     
3702     
3703     
3704     
3705 });
3706
3707
3708
3709  
3710
3711  /*
3712  * - LGPL
3713  *
3714  * navbar
3715  * 
3716  */
3717
3718 /**
3719  * @class Roo.bootstrap.NavSimplebar
3720  * @extends Roo.bootstrap.Navbar
3721  * Bootstrap Sidebar class
3722  *
3723  * @cfg {Boolean} inverse is inverted color
3724  * 
3725  * @cfg {String} type (nav | pills | tabs)
3726  * @cfg {Boolean} arrangement stacked | justified
3727  * @cfg {String} align (left | right) alignment
3728  * 
3729  * @cfg {Boolean} main (true|false) main nav bar? default false
3730  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3731  * 
3732  * @cfg {String} tag (header|footer|nav|div) default is nav 
3733
3734  * 
3735  * 
3736  * 
3737  * @constructor
3738  * Create a new Sidebar
3739  * @param {Object} config The config object
3740  */
3741
3742
3743 Roo.bootstrap.NavSimplebar = function(config){
3744     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3745 };
3746
3747 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3748     
3749     inverse: false,
3750     
3751     type: false,
3752     arrangement: '',
3753     align : false,
3754     
3755     
3756     
3757     main : false,
3758     
3759     
3760     tag : false,
3761     
3762     
3763     getAutoCreate : function(){
3764         
3765         
3766         var cfg = {
3767             tag : this.tag || 'div',
3768             cls : 'navbar'
3769         };
3770           
3771         
3772         cfg.cn = [
3773             {
3774                 cls: 'nav',
3775                 tag : 'ul'
3776             }
3777         ];
3778         
3779          
3780         this.type = this.type || 'nav';
3781         if (['tabs','pills'].indexOf(this.type)!==-1) {
3782             cfg.cn[0].cls += ' nav-' + this.type
3783         
3784         
3785         } else {
3786             if (this.type!=='nav') {
3787                 Roo.log('nav type must be nav/tabs/pills')
3788             }
3789             cfg.cn[0].cls += ' navbar-nav'
3790         }
3791         
3792         
3793         
3794         
3795         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3796             cfg.cn[0].cls += ' nav-' + this.arrangement;
3797         }
3798         
3799         
3800         if (this.align === 'right') {
3801             cfg.cn[0].cls += ' navbar-right';
3802         }
3803         
3804         if (this.inverse) {
3805             cfg.cls += ' navbar-inverse';
3806             
3807         }
3808         
3809         
3810         return cfg;
3811     
3812         
3813     }
3814     
3815     
3816     
3817 });
3818
3819
3820
3821  
3822
3823  
3824        /*
3825  * - LGPL
3826  *
3827  * navbar
3828  * 
3829  */
3830
3831 /**
3832  * @class Roo.bootstrap.NavHeaderbar
3833  * @extends Roo.bootstrap.NavSimplebar
3834  * Bootstrap Sidebar class
3835  *
3836  * @cfg {String} brand what is brand
3837  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3838  * @cfg {String} brand_href href of the brand
3839  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3840  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3841  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3842  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3843  * 
3844  * @constructor
3845  * Create a new Sidebar
3846  * @param {Object} config The config object
3847  */
3848
3849
3850 Roo.bootstrap.NavHeaderbar = function(config){
3851     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3852       
3853 };
3854
3855 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3856     
3857     position: '',
3858     brand: '',
3859     brand_href: false,
3860     srButton : true,
3861     autohide : false,
3862     desktopCenter : false,
3863    
3864     
3865     getAutoCreate : function(){
3866         
3867         var   cfg = {
3868             tag: this.nav || 'nav',
3869             cls: 'navbar',
3870             role: 'navigation',
3871             cn: []
3872         };
3873         
3874         var cn = cfg.cn;
3875         if (this.desktopCenter) {
3876             cn.push({cls : 'container', cn : []});
3877             cn = cn[0].cn;
3878         }
3879         
3880         if(this.srButton){
3881             cn.push({
3882                 tag: 'div',
3883                 cls: 'navbar-header',
3884                 cn: [
3885                     {
3886                         tag: 'button',
3887                         type: 'button',
3888                         cls: 'navbar-toggle',
3889                         'data-toggle': 'collapse',
3890                         cn: [
3891                             {
3892                                 tag: 'span',
3893                                 cls: 'sr-only',
3894                                 html: 'Toggle navigation'
3895                             },
3896                             {
3897                                 tag: 'span',
3898                                 cls: 'icon-bar'
3899                             },
3900                             {
3901                                 tag: 'span',
3902                                 cls: 'icon-bar'
3903                             },
3904                             {
3905                                 tag: 'span',
3906                                 cls: 'icon-bar'
3907                             }
3908                         ]
3909                     }
3910                 ]
3911             });
3912         }
3913         
3914         cn.push({
3915             tag: 'div',
3916             cls: 'collapse navbar-collapse',
3917             cn : []
3918         });
3919         
3920         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3921         
3922         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3923             cfg.cls += ' navbar-' + this.position;
3924             
3925             // tag can override this..
3926             
3927             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3928         }
3929         
3930         if (this.brand !== '') {
3931             cn[0].cn.push({
3932                 tag: 'a',
3933                 href: this.brand_href ? this.brand_href : '#',
3934                 cls: 'navbar-brand',
3935                 cn: [
3936                 this.brand
3937                 ]
3938             });
3939         }
3940         
3941         if(this.main){
3942             cfg.cls += ' main-nav';
3943         }
3944         
3945         
3946         return cfg;
3947
3948         
3949     },
3950     getHeaderChildContainer : function()
3951     {
3952         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3953             return this.el.select('.navbar-header',true).first();
3954         }
3955         
3956         return this.getChildContainer();
3957     },
3958     
3959     
3960     initEvents : function()
3961     {
3962         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3963         
3964         if (this.autohide) {
3965             
3966             var prevScroll = 0;
3967             var ft = this.el;
3968             
3969             Roo.get(document).on('scroll',function(e) {
3970                 var ns = Roo.get(document).getScroll().top;
3971                 var os = prevScroll;
3972                 prevScroll = ns;
3973                 
3974                 if(ns > os){
3975                     ft.removeClass('slideDown');
3976                     ft.addClass('slideUp');
3977                     return;
3978                 }
3979                 ft.removeClass('slideUp');
3980                 ft.addClass('slideDown');
3981                  
3982               
3983           },this);
3984         }
3985     }    
3986     
3987 });
3988
3989
3990
3991  
3992
3993  /*
3994  * - LGPL
3995  *
3996  * navbar
3997  * 
3998  */
3999
4000 /**
4001  * @class Roo.bootstrap.NavSidebar
4002  * @extends Roo.bootstrap.Navbar
4003  * Bootstrap Sidebar class
4004  * 
4005  * @constructor
4006  * Create a new Sidebar
4007  * @param {Object} config The config object
4008  */
4009
4010
4011 Roo.bootstrap.NavSidebar = function(config){
4012     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4013 };
4014
4015 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4016     
4017     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4018     
4019     getAutoCreate : function(){
4020         
4021         
4022         return  {
4023             tag: 'div',
4024             cls: 'sidebar sidebar-nav'
4025         };
4026     
4027         
4028     }
4029     
4030     
4031     
4032 });
4033
4034
4035
4036  
4037
4038  /*
4039  * - LGPL
4040  *
4041  * nav group
4042  * 
4043  */
4044
4045 /**
4046  * @class Roo.bootstrap.NavGroup
4047  * @extends Roo.bootstrap.Component
4048  * Bootstrap NavGroup class
4049  * @cfg {String} align (left|right)
4050  * @cfg {Boolean} inverse
4051  * @cfg {String} type (nav|pills|tab) default nav
4052  * @cfg {String} navId - reference Id for navbar.
4053
4054  * 
4055  * @constructor
4056  * Create a new nav group
4057  * @param {Object} config The config object
4058  */
4059
4060 Roo.bootstrap.NavGroup = function(config){
4061     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4062     this.navItems = [];
4063    
4064     Roo.bootstrap.NavGroup.register(this);
4065      this.addEvents({
4066         /**
4067              * @event changed
4068              * Fires when the active item changes
4069              * @param {Roo.bootstrap.NavGroup} this
4070              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4071              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4072          */
4073         'changed': true
4074      });
4075     
4076 };
4077
4078 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4079     
4080     align: '',
4081     inverse: false,
4082     form: false,
4083     type: 'nav',
4084     navId : '',
4085     // private
4086     
4087     navItems : false, 
4088     
4089     getAutoCreate : function()
4090     {
4091         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4092         
4093         cfg = {
4094             tag : 'ul',
4095             cls: 'nav' 
4096         };
4097         
4098         if (['tabs','pills'].indexOf(this.type)!==-1) {
4099             cfg.cls += ' nav-' + this.type
4100         } else {
4101             if (this.type!=='nav') {
4102                 Roo.log('nav type must be nav/tabs/pills')
4103             }
4104             cfg.cls += ' navbar-nav'
4105         }
4106         
4107         if (this.parent() && this.parent().sidebar) {
4108             cfg = {
4109                 tag: 'ul',
4110                 cls: 'dashboard-menu sidebar-menu'
4111             };
4112             
4113             return cfg;
4114         }
4115         
4116         if (this.form === true) {
4117             cfg = {
4118                 tag: 'form',
4119                 cls: 'navbar-form'
4120             };
4121             
4122             if (this.align === 'right') {
4123                 cfg.cls += ' navbar-right';
4124             } else {
4125                 cfg.cls += ' navbar-left';
4126             }
4127         }
4128         
4129         if (this.align === 'right') {
4130             cfg.cls += ' navbar-right';
4131         }
4132         
4133         if (this.inverse) {
4134             cfg.cls += ' navbar-inverse';
4135             
4136         }
4137         
4138         
4139         return cfg;
4140     },
4141     /**
4142     * sets the active Navigation item
4143     * @param {Roo.bootstrap.NavItem} the new current navitem
4144     */
4145     setActiveItem : function(item)
4146     {
4147         var prev = false;
4148         Roo.each(this.navItems, function(v){
4149             if (v == item) {
4150                 return ;
4151             }
4152             if (v.isActive()) {
4153                 v.setActive(false, true);
4154                 prev = v;
4155                 
4156             }
4157             
4158         });
4159
4160         item.setActive(true, true);
4161         this.fireEvent('changed', this, item, prev);
4162         
4163         
4164     },
4165     /**
4166     * gets the active Navigation item
4167     * @return {Roo.bootstrap.NavItem} the current navitem
4168     */
4169     getActive : function()
4170     {
4171         
4172         var prev = false;
4173         Roo.each(this.navItems, function(v){
4174             
4175             if (v.isActive()) {
4176                 prev = v;
4177                 
4178             }
4179             
4180         });
4181         return prev;
4182     },
4183     
4184     indexOfNav : function()
4185     {
4186         
4187         var prev = false;
4188         Roo.each(this.navItems, function(v,i){
4189             
4190             if (v.isActive()) {
4191                 prev = i;
4192                 
4193             }
4194             
4195         });
4196         return prev;
4197     },
4198     /**
4199     * adds a Navigation item
4200     * @param {Roo.bootstrap.NavItem} the navitem to add
4201     */
4202     addItem : function(cfg)
4203     {
4204         var cn = new Roo.bootstrap.NavItem(cfg);
4205         this.register(cn);
4206         cn.parentId = this.id;
4207         cn.onRender(this.el, null);
4208         return cn;
4209     },
4210     /**
4211     * register a Navigation item
4212     * @param {Roo.bootstrap.NavItem} the navitem to add
4213     */
4214     register : function(item)
4215     {
4216         this.navItems.push( item);
4217         item.navId = this.navId;
4218     
4219     },
4220     
4221     /**
4222     * clear all the Navigation item
4223     */
4224    
4225     clearAll : function()
4226     {
4227         this.navItems = [];
4228         this.el.dom.innerHTML = '';
4229     },
4230     
4231     getNavItem: function(tabId)
4232     {
4233         var ret = false;
4234         Roo.each(this.navItems, function(e) {
4235             if (e.tabId == tabId) {
4236                ret =  e;
4237                return false;
4238             }
4239             return true;
4240             
4241         });
4242         return ret;
4243     },
4244     
4245     setActiveNext : function()
4246     {
4247         var i = this.indexOfNav(this.getActive());
4248         if (i > this.navItems.length) {
4249             return;
4250         }
4251         this.setActiveItem(this.navItems[i+1]);
4252     },
4253     setActivePrev : function()
4254     {
4255         var i = this.indexOfNav(this.getActive());
4256         if (i  < 1) {
4257             return;
4258         }
4259         this.setActiveItem(this.navItems[i-1]);
4260     },
4261     clearWasActive : function(except) {
4262         Roo.each(this.navItems, function(e) {
4263             if (e.tabId != except.tabId && e.was_active) {
4264                e.was_active = false;
4265                return false;
4266             }
4267             return true;
4268             
4269         });
4270     },
4271     getWasActive : function ()
4272     {
4273         var r = false;
4274         Roo.each(this.navItems, function(e) {
4275             if (e.was_active) {
4276                r = e;
4277                return false;
4278             }
4279             return true;
4280             
4281         });
4282         return r;
4283     }
4284     
4285     
4286 });
4287
4288  
4289 Roo.apply(Roo.bootstrap.NavGroup, {
4290     
4291     groups: {},
4292      /**
4293     * register a Navigation Group
4294     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4295     */
4296     register : function(navgrp)
4297     {
4298         this.groups[navgrp.navId] = navgrp;
4299         
4300     },
4301     /**
4302     * fetch a Navigation Group based on the navigation ID
4303     * @param {string} the navgroup to add
4304     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4305     */
4306     get: function(navId) {
4307         if (typeof(this.groups[navId]) == 'undefined') {
4308             return false;
4309             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4310         }
4311         return this.groups[navId] ;
4312     }
4313     
4314     
4315     
4316 });
4317
4318  /*
4319  * - LGPL
4320  *
4321  * row
4322  * 
4323  */
4324
4325 /**
4326  * @class Roo.bootstrap.NavItem
4327  * @extends Roo.bootstrap.Component
4328  * Bootstrap Navbar.NavItem class
4329  * @cfg {String} href  link to
4330  * @cfg {String} html content of button
4331  * @cfg {String} badge text inside badge
4332  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4333  * @cfg {String} glyphicon name of glyphicon
4334  * @cfg {String} icon name of font awesome icon
4335  * @cfg {Boolean} active Is item active
4336  * @cfg {Boolean} disabled Is item disabled
4337  
4338  * @cfg {Boolean} preventDefault (true | false) default false
4339  * @cfg {String} tabId the tab that this item activates.
4340  * @cfg {String} tagtype (a|span) render as a href or span?
4341  * @cfg {Boolean} animateRef (true|false) link to element default false  
4342   
4343  * @constructor
4344  * Create a new Navbar Item
4345  * @param {Object} config The config object
4346  */
4347 Roo.bootstrap.NavItem = function(config){
4348     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4349     this.addEvents({
4350         // raw events
4351         /**
4352          * @event click
4353          * The raw click event for the entire grid.
4354          * @param {Roo.EventObject} e
4355          */
4356         "click" : true,
4357          /**
4358             * @event changed
4359             * Fires when the active item active state changes
4360             * @param {Roo.bootstrap.NavItem} this
4361             * @param {boolean} state the new state
4362              
4363          */
4364         'changed': true,
4365         /**
4366             * @event scrollto
4367             * Fires when scroll to element
4368             * @param {Roo.bootstrap.NavItem} this
4369             * @param {Object} options
4370             * @param {Roo.EventObject} e
4371              
4372          */
4373         'scrollto': true
4374     });
4375    
4376 };
4377
4378 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4379     
4380     href: false,
4381     html: '',
4382     badge: '',
4383     icon: false,
4384     glyphicon: false,
4385     active: false,
4386     preventDefault : false,
4387     tabId : false,
4388     tagtype : 'a',
4389     disabled : false,
4390     animateRef : false,
4391     was_active : false,
4392     
4393     getAutoCreate : function(){
4394          
4395         var cfg = {
4396             tag: 'li',
4397             cls: 'nav-item'
4398             
4399         };
4400         
4401         if (this.active) {
4402             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4403         }
4404         if (this.disabled) {
4405             cfg.cls += ' disabled';
4406         }
4407         
4408         if (this.href || this.html || this.glyphicon || this.icon) {
4409             cfg.cn = [
4410                 {
4411                     tag: this.tagtype,
4412                     href : this.href || "#",
4413                     html: this.html || ''
4414                 }
4415             ];
4416             
4417             if (this.icon) {
4418                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4419             }
4420
4421             if(this.glyphicon) {
4422                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4423             }
4424             
4425             if (this.menu) {
4426                 
4427                 cfg.cn[0].html += " <span class='caret'></span>";
4428              
4429             }
4430             
4431             if (this.badge !== '') {
4432                  
4433                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4434             }
4435         }
4436         
4437         
4438         
4439         return cfg;
4440     },
4441     initEvents: function() 
4442     {
4443         if (typeof (this.menu) != 'undefined') {
4444             this.menu.parentType = this.xtype;
4445             this.menu.triggerEl = this.el;
4446             this.menu = this.addxtype(Roo.apply({}, this.menu));
4447         }
4448         
4449         this.el.select('a',true).on('click', this.onClick, this);
4450         
4451         if(this.tagtype == 'span'){
4452             this.el.select('span',true).on('click', this.onClick, this);
4453         }
4454        
4455         // at this point parent should be available..
4456         this.parent().register(this);
4457     },
4458     
4459     onClick : function(e)
4460     {
4461         if (e.getTarget('.dropdown-menu-item')) {
4462             // did you click on a menu itemm.... - then don't trigger onclick..
4463             return;
4464         }
4465         
4466         if(
4467                 this.preventDefault || 
4468                 this.href == '#' 
4469         ){
4470             Roo.log("NavItem - prevent Default?");
4471             e.preventDefault();
4472         }
4473         
4474         if (this.disabled) {
4475             return;
4476         }
4477         
4478         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4479         if (tg && tg.transition) {
4480             Roo.log("waiting for the transitionend");
4481             return;
4482         }
4483         
4484         
4485         
4486         //Roo.log("fire event clicked");
4487         if(this.fireEvent('click', this, e) === false){
4488             return;
4489         };
4490         
4491         if(this.tagtype == 'span'){
4492             return;
4493         }
4494         
4495         //Roo.log(this.href);
4496         var ael = this.el.select('a',true).first();
4497         //Roo.log(ael);
4498         
4499         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4500             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4501             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4502                 return; // ignore... - it's a 'hash' to another page.
4503             }
4504             Roo.log("NavItem - prevent Default?");
4505             e.preventDefault();
4506             this.scrollToElement(e);
4507         }
4508         
4509         
4510         var p =  this.parent();
4511    
4512         if (['tabs','pills'].indexOf(p.type)!==-1) {
4513             if (typeof(p.setActiveItem) !== 'undefined') {
4514                 p.setActiveItem(this);
4515             }
4516         }
4517         
4518         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4519         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4520             // remove the collapsed menu expand...
4521             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4522         }
4523     },
4524     
4525     isActive: function () {
4526         return this.active
4527     },
4528     setActive : function(state, fire, is_was_active)
4529     {
4530         if (this.active && !state && this.navId) {
4531             this.was_active = true;
4532             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4533             if (nv) {
4534                 nv.clearWasActive(this);
4535             }
4536             
4537         }
4538         this.active = state;
4539         
4540         if (!state ) {
4541             this.el.removeClass('active');
4542         } else if (!this.el.hasClass('active')) {
4543             this.el.addClass('active');
4544         }
4545         if (fire) {
4546             this.fireEvent('changed', this, state);
4547         }
4548         
4549         // show a panel if it's registered and related..
4550         
4551         if (!this.navId || !this.tabId || !state || is_was_active) {
4552             return;
4553         }
4554         
4555         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4556         if (!tg) {
4557             return;
4558         }
4559         var pan = tg.getPanelByName(this.tabId);
4560         if (!pan) {
4561             return;
4562         }
4563         // if we can not flip to new panel - go back to old nav highlight..
4564         if (false == tg.showPanel(pan)) {
4565             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4566             if (nv) {
4567                 var onav = nv.getWasActive();
4568                 if (onav) {
4569                     onav.setActive(true, false, true);
4570                 }
4571             }
4572             
4573         }
4574         
4575         
4576         
4577     },
4578      // this should not be here...
4579     setDisabled : function(state)
4580     {
4581         this.disabled = state;
4582         if (!state ) {
4583             this.el.removeClass('disabled');
4584         } else if (!this.el.hasClass('disabled')) {
4585             this.el.addClass('disabled');
4586         }
4587         
4588     },
4589     
4590     /**
4591      * Fetch the element to display the tooltip on.
4592      * @return {Roo.Element} defaults to this.el
4593      */
4594     tooltipEl : function()
4595     {
4596         return this.el.select('' + this.tagtype + '', true).first();
4597     },
4598     
4599     scrollToElement : function(e)
4600     {
4601         var c = document.body;
4602         
4603         /*
4604          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4605          */
4606         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4607             c = document.documentElement;
4608         }
4609         
4610         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4611         
4612         if(!target){
4613             return;
4614         }
4615
4616         var o = target.calcOffsetsTo(c);
4617         
4618         var options = {
4619             target : target,
4620             value : o[1]
4621         };
4622         
4623         this.fireEvent('scrollto', this, options, e);
4624         
4625         Roo.get(c).scrollTo('top', options.value, true);
4626         
4627         return;
4628     }
4629 });
4630  
4631
4632  /*
4633  * - LGPL
4634  *
4635  * sidebar item
4636  *
4637  *  li
4638  *    <span> icon </span>
4639  *    <span> text </span>
4640  *    <span>badge </span>
4641  */
4642
4643 /**
4644  * @class Roo.bootstrap.NavSidebarItem
4645  * @extends Roo.bootstrap.NavItem
4646  * Bootstrap Navbar.NavSidebarItem class
4647  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4648  * {Boolean} open is the menu open
4649  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4650  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4651  * {String} buttonSize (sm|md|lg)the extra classes for the button
4652  * {Boolean} showArrow show arrow next to the text (default true)
4653  * @constructor
4654  * Create a new Navbar Button
4655  * @param {Object} config The config object
4656  */
4657 Roo.bootstrap.NavSidebarItem = function(config){
4658     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4659     this.addEvents({
4660         // raw events
4661         /**
4662          * @event click
4663          * The raw click event for the entire grid.
4664          * @param {Roo.EventObject} e
4665          */
4666         "click" : true,
4667          /**
4668             * @event changed
4669             * Fires when the active item active state changes
4670             * @param {Roo.bootstrap.NavSidebarItem} this
4671             * @param {boolean} state the new state
4672              
4673          */
4674         'changed': true
4675     });
4676    
4677 };
4678
4679 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4680     
4681     badgeWeight : 'default',
4682     
4683     open: false,
4684     
4685     buttonView : false,
4686     
4687     buttonWeight : 'default',
4688     
4689     buttonSize : 'md',
4690     
4691     showArrow : true,
4692     
4693     getAutoCreate : function(){
4694         
4695         
4696         var a = {
4697                 tag: 'a',
4698                 href : this.href || '#',
4699                 cls: '',
4700                 html : '',
4701                 cn : []
4702         };
4703         
4704         if(this.buttonView){
4705             a = {
4706                 tag: 'button',
4707                 href : this.href || '#',
4708                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4709                 html : this.html,
4710                 cn : []
4711             };
4712         }
4713         
4714         var cfg = {
4715             tag: 'li',
4716             cls: '',
4717             cn: [ a ]
4718         };
4719         
4720         if (this.active) {
4721             cfg.cls += ' active';
4722         }
4723         
4724         if (this.disabled) {
4725             cfg.cls += ' disabled';
4726         }
4727         if (this.open) {
4728             cfg.cls += ' open x-open';
4729         }
4730         // left icon..
4731         if (this.glyphicon || this.icon) {
4732             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4733             a.cn.push({ tag : 'i', cls : c }) ;
4734         }
4735         
4736         if(!this.buttonView){
4737             var span = {
4738                 tag: 'span',
4739                 html : this.html || ''
4740             };
4741
4742             a.cn.push(span);
4743             
4744         }
4745         
4746         if (this.badge !== '') {
4747             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4748         }
4749         
4750         if (this.menu) {
4751             
4752             if(this.showArrow){
4753                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4754             }
4755             
4756             a.cls += ' dropdown-toggle treeview' ;
4757         }
4758         
4759         return cfg;
4760     },
4761     
4762     initEvents : function()
4763     { 
4764         if (typeof (this.menu) != 'undefined') {
4765             this.menu.parentType = this.xtype;
4766             this.menu.triggerEl = this.el;
4767             this.menu = this.addxtype(Roo.apply({}, this.menu));
4768         }
4769         
4770         this.el.on('click', this.onClick, this);
4771         
4772         if(this.badge !== ''){
4773             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4774         }
4775         
4776     },
4777     
4778     onClick : function(e)
4779     {
4780         if(this.disabled){
4781             e.preventDefault();
4782             return;
4783         }
4784         
4785         if(this.preventDefault){
4786             e.preventDefault();
4787         }
4788         
4789         this.fireEvent('click', this);
4790     },
4791     
4792     disable : function()
4793     {
4794         this.setDisabled(true);
4795     },
4796     
4797     enable : function()
4798     {
4799         this.setDisabled(false);
4800     },
4801     
4802     setDisabled : function(state)
4803     {
4804         if(this.disabled == state){
4805             return;
4806         }
4807         
4808         this.disabled = state;
4809         
4810         if (state) {
4811             this.el.addClass('disabled');
4812             return;
4813         }
4814         
4815         this.el.removeClass('disabled');
4816         
4817         return;
4818     },
4819     
4820     setActive : function(state)
4821     {
4822         if(this.active == state){
4823             return;
4824         }
4825         
4826         this.active = state;
4827         
4828         if (state) {
4829             this.el.addClass('active');
4830             return;
4831         }
4832         
4833         this.el.removeClass('active');
4834         
4835         return;
4836     },
4837     
4838     isActive: function () 
4839     {
4840         return this.active;
4841     },
4842     
4843     setBadge : function(str)
4844     {
4845         if(!this.badgeEl){
4846             return;
4847         }
4848         
4849         this.badgeEl.dom.innerHTML = str;
4850     }
4851     
4852    
4853      
4854  
4855 });
4856  
4857
4858  /*
4859  * - LGPL
4860  *
4861  * row
4862  * 
4863  */
4864
4865 /**
4866  * @class Roo.bootstrap.Row
4867  * @extends Roo.bootstrap.Component
4868  * Bootstrap Row class (contains columns...)
4869  * 
4870  * @constructor
4871  * Create a new Row
4872  * @param {Object} config The config object
4873  */
4874
4875 Roo.bootstrap.Row = function(config){
4876     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4877 };
4878
4879 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4880     
4881     getAutoCreate : function(){
4882        return {
4883             cls: 'row clearfix'
4884        };
4885     }
4886     
4887     
4888 });
4889
4890  
4891
4892  /*
4893  * - LGPL
4894  *
4895  * element
4896  * 
4897  */
4898
4899 /**
4900  * @class Roo.bootstrap.Element
4901  * @extends Roo.bootstrap.Component
4902  * Bootstrap Element class
4903  * @cfg {String} html contents of the element
4904  * @cfg {String} tag tag of the element
4905  * @cfg {String} cls class of the element
4906  * @cfg {Boolean} preventDefault (true|false) default false
4907  * @cfg {Boolean} clickable (true|false) default false
4908  * 
4909  * @constructor
4910  * Create a new Element
4911  * @param {Object} config The config object
4912  */
4913
4914 Roo.bootstrap.Element = function(config){
4915     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4916     
4917     this.addEvents({
4918         // raw events
4919         /**
4920          * @event click
4921          * When a element is chick
4922          * @param {Roo.bootstrap.Element} this
4923          * @param {Roo.EventObject} e
4924          */
4925         "click" : true
4926     });
4927 };
4928
4929 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4930     
4931     tag: 'div',
4932     cls: '',
4933     html: '',
4934     preventDefault: false, 
4935     clickable: false,
4936     
4937     getAutoCreate : function(){
4938         
4939         var cfg = {
4940             tag: this.tag,
4941             // cls: this.cls, double assign in parent class Component.js :: onRender
4942             html: this.html
4943         };
4944         
4945         return cfg;
4946     },
4947     
4948     initEvents: function() 
4949     {
4950         Roo.bootstrap.Element.superclass.initEvents.call(this);
4951         
4952         if(this.clickable){
4953             this.el.on('click', this.onClick, this);
4954         }
4955         
4956     },
4957     
4958     onClick : function(e)
4959     {
4960         if(this.preventDefault){
4961             e.preventDefault();
4962         }
4963         
4964         this.fireEvent('click', this, e);
4965     },
4966     
4967     getValue : function()
4968     {
4969         return this.el.dom.innerHTML;
4970     },
4971     
4972     setValue : function(value)
4973     {
4974         this.el.dom.innerHTML = value;
4975     }
4976    
4977 });
4978
4979  
4980
4981  /*
4982  * - LGPL
4983  *
4984  * pagination
4985  * 
4986  */
4987
4988 /**
4989  * @class Roo.bootstrap.Pagination
4990  * @extends Roo.bootstrap.Component
4991  * Bootstrap Pagination class
4992  * @cfg {String} size xs | sm | md | lg
4993  * @cfg {Boolean} inverse false | true
4994  * 
4995  * @constructor
4996  * Create a new Pagination
4997  * @param {Object} config The config object
4998  */
4999
5000 Roo.bootstrap.Pagination = function(config){
5001     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5002 };
5003
5004 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5005     
5006     cls: false,
5007     size: false,
5008     inverse: false,
5009     
5010     getAutoCreate : function(){
5011         var cfg = {
5012             tag: 'ul',
5013                 cls: 'pagination'
5014         };
5015         if (this.inverse) {
5016             cfg.cls += ' inverse';
5017         }
5018         if (this.html) {
5019             cfg.html=this.html;
5020         }
5021         if (this.cls) {
5022             cfg.cls += " " + this.cls;
5023         }
5024         return cfg;
5025     }
5026    
5027 });
5028
5029  
5030
5031  /*
5032  * - LGPL
5033  *
5034  * Pagination item
5035  * 
5036  */
5037
5038
5039 /**
5040  * @class Roo.bootstrap.PaginationItem
5041  * @extends Roo.bootstrap.Component
5042  * Bootstrap PaginationItem class
5043  * @cfg {String} html text
5044  * @cfg {String} href the link
5045  * @cfg {Boolean} preventDefault (true | false) default true
5046  * @cfg {Boolean} active (true | false) default false
5047  * @cfg {Boolean} disabled default false
5048  * 
5049  * 
5050  * @constructor
5051  * Create a new PaginationItem
5052  * @param {Object} config The config object
5053  */
5054
5055
5056 Roo.bootstrap.PaginationItem = function(config){
5057     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5058     this.addEvents({
5059         // raw events
5060         /**
5061          * @event click
5062          * The raw click event for the entire grid.
5063          * @param {Roo.EventObject} e
5064          */
5065         "click" : true
5066     });
5067 };
5068
5069 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5070     
5071     href : false,
5072     html : false,
5073     preventDefault: true,
5074     active : false,
5075     cls : false,
5076     disabled: false,
5077     
5078     getAutoCreate : function(){
5079         var cfg= {
5080             tag: 'li',
5081             cn: [
5082                 {
5083                     tag : 'a',
5084                     href : this.href ? this.href : '#',
5085                     html : this.html ? this.html : ''
5086                 }
5087             ]
5088         };
5089         
5090         if(this.cls){
5091             cfg.cls = this.cls;
5092         }
5093         
5094         if(this.disabled){
5095             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5096         }
5097         
5098         if(this.active){
5099             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5100         }
5101         
5102         return cfg;
5103     },
5104     
5105     initEvents: function() {
5106         
5107         this.el.on('click', this.onClick, this);
5108         
5109     },
5110     onClick : function(e)
5111     {
5112         Roo.log('PaginationItem on click ');
5113         if(this.preventDefault){
5114             e.preventDefault();
5115         }
5116         
5117         if(this.disabled){
5118             return;
5119         }
5120         
5121         this.fireEvent('click', this, e);
5122     }
5123    
5124 });
5125
5126  
5127
5128  /*
5129  * - LGPL
5130  *
5131  * slider
5132  * 
5133  */
5134
5135
5136 /**
5137  * @class Roo.bootstrap.Slider
5138  * @extends Roo.bootstrap.Component
5139  * Bootstrap Slider class
5140  *    
5141  * @constructor
5142  * Create a new Slider
5143  * @param {Object} config The config object
5144  */
5145
5146 Roo.bootstrap.Slider = function(config){
5147     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5148 };
5149
5150 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5151     
5152     getAutoCreate : function(){
5153         
5154         var cfg = {
5155             tag: 'div',
5156             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5157             cn: [
5158                 {
5159                     tag: 'a',
5160                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5161                 }
5162             ]
5163         };
5164         
5165         return cfg;
5166     }
5167    
5168 });
5169
5170  /*
5171  * Based on:
5172  * Ext JS Library 1.1.1
5173  * Copyright(c) 2006-2007, Ext JS, LLC.
5174  *
5175  * Originally Released Under LGPL - original licence link has changed is not relivant.
5176  *
5177  * Fork - LGPL
5178  * <script type="text/javascript">
5179  */
5180  
5181
5182 /**
5183  * @class Roo.grid.ColumnModel
5184  * @extends Roo.util.Observable
5185  * This is the default implementation of a ColumnModel used by the Grid. It defines
5186  * the columns in the grid.
5187  * <br>Usage:<br>
5188  <pre><code>
5189  var colModel = new Roo.grid.ColumnModel([
5190         {header: "Ticker", width: 60, sortable: true, locked: true},
5191         {header: "Company Name", width: 150, sortable: true},
5192         {header: "Market Cap.", width: 100, sortable: true},
5193         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5194         {header: "Employees", width: 100, sortable: true, resizable: false}
5195  ]);
5196  </code></pre>
5197  * <p>
5198  
5199  * The config options listed for this class are options which may appear in each
5200  * individual column definition.
5201  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5202  * @constructor
5203  * @param {Object} config An Array of column config objects. See this class's
5204  * config objects for details.
5205 */
5206 Roo.grid.ColumnModel = function(config){
5207         /**
5208      * The config passed into the constructor
5209      */
5210     this.config = config;
5211     this.lookup = {};
5212
5213     // if no id, create one
5214     // if the column does not have a dataIndex mapping,
5215     // map it to the order it is in the config
5216     for(var i = 0, len = config.length; i < len; i++){
5217         var c = config[i];
5218         if(typeof c.dataIndex == "undefined"){
5219             c.dataIndex = i;
5220         }
5221         if(typeof c.renderer == "string"){
5222             c.renderer = Roo.util.Format[c.renderer];
5223         }
5224         if(typeof c.id == "undefined"){
5225             c.id = Roo.id();
5226         }
5227         if(c.editor && c.editor.xtype){
5228             c.editor  = Roo.factory(c.editor, Roo.grid);
5229         }
5230         if(c.editor && c.editor.isFormField){
5231             c.editor = new Roo.grid.GridEditor(c.editor);
5232         }
5233         this.lookup[c.id] = c;
5234     }
5235
5236     /**
5237      * The width of columns which have no width specified (defaults to 100)
5238      * @type Number
5239      */
5240     this.defaultWidth = 100;
5241
5242     /**
5243      * Default sortable of columns which have no sortable specified (defaults to false)
5244      * @type Boolean
5245      */
5246     this.defaultSortable = false;
5247
5248     this.addEvents({
5249         /**
5250              * @event widthchange
5251              * Fires when the width of a column changes.
5252              * @param {ColumnModel} this
5253              * @param {Number} columnIndex The column index
5254              * @param {Number} newWidth The new width
5255              */
5256             "widthchange": true,
5257         /**
5258              * @event headerchange
5259              * Fires when the text of a header changes.
5260              * @param {ColumnModel} this
5261              * @param {Number} columnIndex The column index
5262              * @param {Number} newText The new header text
5263              */
5264             "headerchange": true,
5265         /**
5266              * @event hiddenchange
5267              * Fires when a column is hidden or "unhidden".
5268              * @param {ColumnModel} this
5269              * @param {Number} columnIndex The column index
5270              * @param {Boolean} hidden true if hidden, false otherwise
5271              */
5272             "hiddenchange": true,
5273             /**
5274          * @event columnmoved
5275          * Fires when a column is moved.
5276          * @param {ColumnModel} this
5277          * @param {Number} oldIndex
5278          * @param {Number} newIndex
5279          */
5280         "columnmoved" : true,
5281         /**
5282          * @event columlockchange
5283          * Fires when a column's locked state is changed
5284          * @param {ColumnModel} this
5285          * @param {Number} colIndex
5286          * @param {Boolean} locked true if locked
5287          */
5288         "columnlockchange" : true
5289     });
5290     Roo.grid.ColumnModel.superclass.constructor.call(this);
5291 };
5292 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5293     /**
5294      * @cfg {String} header The header text to display in the Grid view.
5295      */
5296     /**
5297      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5298      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5299      * specified, the column's index is used as an index into the Record's data Array.
5300      */
5301     /**
5302      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5303      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5304      */
5305     /**
5306      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5307      * Defaults to the value of the {@link #defaultSortable} property.
5308      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5309      */
5310     /**
5311      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5312      */
5313     /**
5314      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5315      */
5316     /**
5317      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5318      */
5319     /**
5320      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5321      */
5322     /**
5323      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5324      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5325      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5326      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5327      */
5328        /**
5329      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5330      */
5331     /**
5332      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5333      */
5334     /**
5335      * @cfg {String} cursor (Optional)
5336      */
5337     /**
5338      * @cfg {String} tooltip (Optional)
5339      */
5340     /**
5341      * @cfg {Number} xs (Optional)
5342      */
5343     /**
5344      * @cfg {Number} sm (Optional)
5345      */
5346     /**
5347      * @cfg {Number} md (Optional)
5348      */
5349     /**
5350      * @cfg {Number} lg (Optional)
5351      */
5352     /**
5353      * Returns the id of the column at the specified index.
5354      * @param {Number} index The column index
5355      * @return {String} the id
5356      */
5357     getColumnId : function(index){
5358         return this.config[index].id;
5359     },
5360
5361     /**
5362      * Returns the column for a specified id.
5363      * @param {String} id The column id
5364      * @return {Object} the column
5365      */
5366     getColumnById : function(id){
5367         return this.lookup[id];
5368     },
5369
5370     
5371     /**
5372      * Returns the column for a specified dataIndex.
5373      * @param {String} dataIndex The column dataIndex
5374      * @return {Object|Boolean} the column or false if not found
5375      */
5376     getColumnByDataIndex: function(dataIndex){
5377         var index = this.findColumnIndex(dataIndex);
5378         return index > -1 ? this.config[index] : false;
5379     },
5380     
5381     /**
5382      * Returns the index for a specified column id.
5383      * @param {String} id The column id
5384      * @return {Number} the index, or -1 if not found
5385      */
5386     getIndexById : function(id){
5387         for(var i = 0, len = this.config.length; i < len; i++){
5388             if(this.config[i].id == id){
5389                 return i;
5390             }
5391         }
5392         return -1;
5393     },
5394     
5395     /**
5396      * Returns the index for a specified column dataIndex.
5397      * @param {String} dataIndex The column dataIndex
5398      * @return {Number} the index, or -1 if not found
5399      */
5400     
5401     findColumnIndex : function(dataIndex){
5402         for(var i = 0, len = this.config.length; i < len; i++){
5403             if(this.config[i].dataIndex == dataIndex){
5404                 return i;
5405             }
5406         }
5407         return -1;
5408     },
5409     
5410     
5411     moveColumn : function(oldIndex, newIndex){
5412         var c = this.config[oldIndex];
5413         this.config.splice(oldIndex, 1);
5414         this.config.splice(newIndex, 0, c);
5415         this.dataMap = null;
5416         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5417     },
5418
5419     isLocked : function(colIndex){
5420         return this.config[colIndex].locked === true;
5421     },
5422
5423     setLocked : function(colIndex, value, suppressEvent){
5424         if(this.isLocked(colIndex) == value){
5425             return;
5426         }
5427         this.config[colIndex].locked = value;
5428         if(!suppressEvent){
5429             this.fireEvent("columnlockchange", this, colIndex, value);
5430         }
5431     },
5432
5433     getTotalLockedWidth : function(){
5434         var totalWidth = 0;
5435         for(var i = 0; i < this.config.length; i++){
5436             if(this.isLocked(i) && !this.isHidden(i)){
5437                 this.totalWidth += this.getColumnWidth(i);
5438             }
5439         }
5440         return totalWidth;
5441     },
5442
5443     getLockedCount : function(){
5444         for(var i = 0, len = this.config.length; i < len; i++){
5445             if(!this.isLocked(i)){
5446                 return i;
5447             }
5448         }
5449         
5450         return this.config.length;
5451     },
5452
5453     /**
5454      * Returns the number of columns.
5455      * @return {Number}
5456      */
5457     getColumnCount : function(visibleOnly){
5458         if(visibleOnly === true){
5459             var c = 0;
5460             for(var i = 0, len = this.config.length; i < len; i++){
5461                 if(!this.isHidden(i)){
5462                     c++;
5463                 }
5464             }
5465             return c;
5466         }
5467         return this.config.length;
5468     },
5469
5470     /**
5471      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5472      * @param {Function} fn
5473      * @param {Object} scope (optional)
5474      * @return {Array} result
5475      */
5476     getColumnsBy : function(fn, scope){
5477         var r = [];
5478         for(var i = 0, len = this.config.length; i < len; i++){
5479             var c = this.config[i];
5480             if(fn.call(scope||this, c, i) === true){
5481                 r[r.length] = c;
5482             }
5483         }
5484         return r;
5485     },
5486
5487     /**
5488      * Returns true if the specified column is sortable.
5489      * @param {Number} col The column index
5490      * @return {Boolean}
5491      */
5492     isSortable : function(col){
5493         if(typeof this.config[col].sortable == "undefined"){
5494             return this.defaultSortable;
5495         }
5496         return this.config[col].sortable;
5497     },
5498
5499     /**
5500      * Returns the rendering (formatting) function defined for the column.
5501      * @param {Number} col The column index.
5502      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5503      */
5504     getRenderer : function(col){
5505         if(!this.config[col].renderer){
5506             return Roo.grid.ColumnModel.defaultRenderer;
5507         }
5508         return this.config[col].renderer;
5509     },
5510
5511     /**
5512      * Sets the rendering (formatting) function for a column.
5513      * @param {Number} col The column index
5514      * @param {Function} fn The function to use to process the cell's raw data
5515      * to return HTML markup for the grid view. The render function is called with
5516      * the following parameters:<ul>
5517      * <li>Data value.</li>
5518      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5519      * <li>css A CSS style string to apply to the table cell.</li>
5520      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5521      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5522      * <li>Row index</li>
5523      * <li>Column index</li>
5524      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5525      */
5526     setRenderer : function(col, fn){
5527         this.config[col].renderer = fn;
5528     },
5529
5530     /**
5531      * Returns the width for the specified column.
5532      * @param {Number} col The column index
5533      * @return {Number}
5534      */
5535     getColumnWidth : function(col){
5536         return this.config[col].width * 1 || this.defaultWidth;
5537     },
5538
5539     /**
5540      * Sets the width for a column.
5541      * @param {Number} col The column index
5542      * @param {Number} width The new width
5543      */
5544     setColumnWidth : function(col, width, suppressEvent){
5545         this.config[col].width = width;
5546         this.totalWidth = null;
5547         if(!suppressEvent){
5548              this.fireEvent("widthchange", this, col, width);
5549         }
5550     },
5551
5552     /**
5553      * Returns the total width of all columns.
5554      * @param {Boolean} includeHidden True to include hidden column widths
5555      * @return {Number}
5556      */
5557     getTotalWidth : function(includeHidden){
5558         if(!this.totalWidth){
5559             this.totalWidth = 0;
5560             for(var i = 0, len = this.config.length; i < len; i++){
5561                 if(includeHidden || !this.isHidden(i)){
5562                     this.totalWidth += this.getColumnWidth(i);
5563                 }
5564             }
5565         }
5566         return this.totalWidth;
5567     },
5568
5569     /**
5570      * Returns the header for the specified column.
5571      * @param {Number} col The column index
5572      * @return {String}
5573      */
5574     getColumnHeader : function(col){
5575         return this.config[col].header;
5576     },
5577
5578     /**
5579      * Sets the header for a column.
5580      * @param {Number} col The column index
5581      * @param {String} header The new header
5582      */
5583     setColumnHeader : function(col, header){
5584         this.config[col].header = header;
5585         this.fireEvent("headerchange", this, col, header);
5586     },
5587
5588     /**
5589      * Returns the tooltip for the specified column.
5590      * @param {Number} col The column index
5591      * @return {String}
5592      */
5593     getColumnTooltip : function(col){
5594             return this.config[col].tooltip;
5595     },
5596     /**
5597      * Sets the tooltip for a column.
5598      * @param {Number} col The column index
5599      * @param {String} tooltip The new tooltip
5600      */
5601     setColumnTooltip : function(col, tooltip){
5602             this.config[col].tooltip = tooltip;
5603     },
5604
5605     /**
5606      * Returns the dataIndex for the specified column.
5607      * @param {Number} col The column index
5608      * @return {Number}
5609      */
5610     getDataIndex : function(col){
5611         return this.config[col].dataIndex;
5612     },
5613
5614     /**
5615      * Sets the dataIndex for a column.
5616      * @param {Number} col The column index
5617      * @param {Number} dataIndex The new dataIndex
5618      */
5619     setDataIndex : function(col, dataIndex){
5620         this.config[col].dataIndex = dataIndex;
5621     },
5622
5623     
5624     
5625     /**
5626      * Returns true if the cell is editable.
5627      * @param {Number} colIndex The column index
5628      * @param {Number} rowIndex The row index - this is nto actually used..?
5629      * @return {Boolean}
5630      */
5631     isCellEditable : function(colIndex, rowIndex){
5632         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5633     },
5634
5635     /**
5636      * Returns the editor defined for the cell/column.
5637      * return false or null to disable editing.
5638      * @param {Number} colIndex The column index
5639      * @param {Number} rowIndex The row index
5640      * @return {Object}
5641      */
5642     getCellEditor : function(colIndex, rowIndex){
5643         return this.config[colIndex].editor;
5644     },
5645
5646     /**
5647      * Sets if a column is editable.
5648      * @param {Number} col The column index
5649      * @param {Boolean} editable True if the column is editable
5650      */
5651     setEditable : function(col, editable){
5652         this.config[col].editable = editable;
5653     },
5654
5655
5656     /**
5657      * Returns true if the column is hidden.
5658      * @param {Number} colIndex The column index
5659      * @return {Boolean}
5660      */
5661     isHidden : function(colIndex){
5662         return this.config[colIndex].hidden;
5663     },
5664
5665
5666     /**
5667      * Returns true if the column width cannot be changed
5668      */
5669     isFixed : function(colIndex){
5670         return this.config[colIndex].fixed;
5671     },
5672
5673     /**
5674      * Returns true if the column can be resized
5675      * @return {Boolean}
5676      */
5677     isResizable : function(colIndex){
5678         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5679     },
5680     /**
5681      * Sets if a column is hidden.
5682      * @param {Number} colIndex The column index
5683      * @param {Boolean} hidden True if the column is hidden
5684      */
5685     setHidden : function(colIndex, hidden){
5686         this.config[colIndex].hidden = hidden;
5687         this.totalWidth = null;
5688         this.fireEvent("hiddenchange", this, colIndex, hidden);
5689     },
5690
5691     /**
5692      * Sets the editor for a column.
5693      * @param {Number} col The column index
5694      * @param {Object} editor The editor object
5695      */
5696     setEditor : function(col, editor){
5697         this.config[col].editor = editor;
5698     }
5699 });
5700
5701 Roo.grid.ColumnModel.defaultRenderer = function(value)
5702 {
5703     if(typeof value == "object") {
5704         return value;
5705     }
5706         if(typeof value == "string" && value.length < 1){
5707             return "&#160;";
5708         }
5709     
5710         return String.format("{0}", value);
5711 };
5712
5713 // Alias for backwards compatibility
5714 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5715 /*
5716  * Based on:
5717  * Ext JS Library 1.1.1
5718  * Copyright(c) 2006-2007, Ext JS, LLC.
5719  *
5720  * Originally Released Under LGPL - original licence link has changed is not relivant.
5721  *
5722  * Fork - LGPL
5723  * <script type="text/javascript">
5724  */
5725  
5726 /**
5727  * @class Roo.LoadMask
5728  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5729  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5730  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5731  * element's UpdateManager load indicator and will be destroyed after the initial load.
5732  * @constructor
5733  * Create a new LoadMask
5734  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5735  * @param {Object} config The config object
5736  */
5737 Roo.LoadMask = function(el, config){
5738     this.el = Roo.get(el);
5739     Roo.apply(this, config);
5740     if(this.store){
5741         this.store.on('beforeload', this.onBeforeLoad, this);
5742         this.store.on('load', this.onLoad, this);
5743         this.store.on('loadexception', this.onLoadException, this);
5744         this.removeMask = false;
5745     }else{
5746         var um = this.el.getUpdateManager();
5747         um.showLoadIndicator = false; // disable the default indicator
5748         um.on('beforeupdate', this.onBeforeLoad, this);
5749         um.on('update', this.onLoad, this);
5750         um.on('failure', this.onLoad, this);
5751         this.removeMask = true;
5752     }
5753 };
5754
5755 Roo.LoadMask.prototype = {
5756     /**
5757      * @cfg {Boolean} removeMask
5758      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5759      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5760      */
5761     /**
5762      * @cfg {String} msg
5763      * The text to display in a centered loading message box (defaults to 'Loading...')
5764      */
5765     msg : 'Loading...',
5766     /**
5767      * @cfg {String} msgCls
5768      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5769      */
5770     msgCls : 'x-mask-loading',
5771
5772     /**
5773      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5774      * @type Boolean
5775      */
5776     disabled: false,
5777
5778     /**
5779      * Disables the mask to prevent it from being displayed
5780      */
5781     disable : function(){
5782        this.disabled = true;
5783     },
5784
5785     /**
5786      * Enables the mask so that it can be displayed
5787      */
5788     enable : function(){
5789         this.disabled = false;
5790     },
5791     
5792     onLoadException : function()
5793     {
5794         Roo.log(arguments);
5795         
5796         if (typeof(arguments[3]) != 'undefined') {
5797             Roo.MessageBox.alert("Error loading",arguments[3]);
5798         } 
5799         /*
5800         try {
5801             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5802                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5803             }   
5804         } catch(e) {
5805             
5806         }
5807         */
5808     
5809         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5810     },
5811     // private
5812     onLoad : function()
5813     {
5814         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5815     },
5816
5817     // private
5818     onBeforeLoad : function(){
5819         if(!this.disabled){
5820             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5821         }
5822     },
5823
5824     // private
5825     destroy : function(){
5826         if(this.store){
5827             this.store.un('beforeload', this.onBeforeLoad, this);
5828             this.store.un('load', this.onLoad, this);
5829             this.store.un('loadexception', this.onLoadException, this);
5830         }else{
5831             var um = this.el.getUpdateManager();
5832             um.un('beforeupdate', this.onBeforeLoad, this);
5833             um.un('update', this.onLoad, this);
5834             um.un('failure', this.onLoad, this);
5835         }
5836     }
5837 };/*
5838  * - LGPL
5839  *
5840  * table
5841  * 
5842  */
5843
5844 /**
5845  * @class Roo.bootstrap.Table
5846  * @extends Roo.bootstrap.Component
5847  * Bootstrap Table class
5848  * @cfg {String} cls table class
5849  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5850  * @cfg {String} bgcolor Specifies the background color for a table
5851  * @cfg {Number} border Specifies whether the table cells should have borders or not
5852  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5853  * @cfg {Number} cellspacing Specifies the space between cells
5854  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5855  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5856  * @cfg {String} sortable Specifies that the table should be sortable
5857  * @cfg {String} summary Specifies a summary of the content of a table
5858  * @cfg {Number} width Specifies the width of a table
5859  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5860  * 
5861  * @cfg {boolean} striped Should the rows be alternative striped
5862  * @cfg {boolean} bordered Add borders to the table
5863  * @cfg {boolean} hover Add hover highlighting
5864  * @cfg {boolean} condensed Format condensed
5865  * @cfg {boolean} responsive Format condensed
5866  * @cfg {Boolean} loadMask (true|false) default false
5867  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5868  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5869  * @cfg {Boolean} rowSelection (true|false) default false
5870  * @cfg {Boolean} cellSelection (true|false) default false
5871  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5872  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5873  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5874  
5875  * 
5876  * @constructor
5877  * Create a new Table
5878  * @param {Object} config The config object
5879  */
5880
5881 Roo.bootstrap.Table = function(config){
5882     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5883     
5884   
5885     
5886     // BC...
5887     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5888     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5889     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5890     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5891     
5892     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5893     if (this.sm) {
5894         this.sm.grid = this;
5895         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5896         this.sm = this.selModel;
5897         this.sm.xmodule = this.xmodule || false;
5898     }
5899     
5900     if (this.cm && typeof(this.cm.config) == 'undefined') {
5901         this.colModel = new Roo.grid.ColumnModel(this.cm);
5902         this.cm = this.colModel;
5903         this.cm.xmodule = this.xmodule || false;
5904     }
5905     if (this.store) {
5906         this.store= Roo.factory(this.store, Roo.data);
5907         this.ds = this.store;
5908         this.ds.xmodule = this.xmodule || false;
5909          
5910     }
5911     if (this.footer && this.store) {
5912         this.footer.dataSource = this.ds;
5913         this.footer = Roo.factory(this.footer);
5914     }
5915     
5916     /** @private */
5917     this.addEvents({
5918         /**
5919          * @event cellclick
5920          * Fires when a cell is clicked
5921          * @param {Roo.bootstrap.Table} this
5922          * @param {Roo.Element} el
5923          * @param {Number} rowIndex
5924          * @param {Number} columnIndex
5925          * @param {Roo.EventObject} e
5926          */
5927         "cellclick" : true,
5928         /**
5929          * @event celldblclick
5930          * Fires when a cell is double clicked
5931          * @param {Roo.bootstrap.Table} this
5932          * @param {Roo.Element} el
5933          * @param {Number} rowIndex
5934          * @param {Number} columnIndex
5935          * @param {Roo.EventObject} e
5936          */
5937         "celldblclick" : true,
5938         /**
5939          * @event rowclick
5940          * Fires when a row is clicked
5941          * @param {Roo.bootstrap.Table} this
5942          * @param {Roo.Element} el
5943          * @param {Number} rowIndex
5944          * @param {Roo.EventObject} e
5945          */
5946         "rowclick" : true,
5947         /**
5948          * @event rowdblclick
5949          * Fires when a row is double clicked
5950          * @param {Roo.bootstrap.Table} this
5951          * @param {Roo.Element} el
5952          * @param {Number} rowIndex
5953          * @param {Roo.EventObject} e
5954          */
5955         "rowdblclick" : true,
5956         /**
5957          * @event mouseover
5958          * Fires when a mouseover occur
5959          * @param {Roo.bootstrap.Table} this
5960          * @param {Roo.Element} el
5961          * @param {Number} rowIndex
5962          * @param {Number} columnIndex
5963          * @param {Roo.EventObject} e
5964          */
5965         "mouseover" : true,
5966         /**
5967          * @event mouseout
5968          * Fires when a mouseout occur
5969          * @param {Roo.bootstrap.Table} this
5970          * @param {Roo.Element} el
5971          * @param {Number} rowIndex
5972          * @param {Number} columnIndex
5973          * @param {Roo.EventObject} e
5974          */
5975         "mouseout" : true,
5976         /**
5977          * @event rowclass
5978          * Fires when a row is rendered, so you can change add a style to it.
5979          * @param {Roo.bootstrap.Table} this
5980          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5981          */
5982         'rowclass' : true,
5983           /**
5984          * @event rowsrendered
5985          * Fires when all the  rows have been rendered
5986          * @param {Roo.bootstrap.Table} this
5987          */
5988         'rowsrendered' : true,
5989         /**
5990          * @event contextmenu
5991          * The raw contextmenu event for the entire grid.
5992          * @param {Roo.EventObject} e
5993          */
5994         "contextmenu" : true,
5995         /**
5996          * @event rowcontextmenu
5997          * Fires when a row is right clicked
5998          * @param {Roo.bootstrap.Table} this
5999          * @param {Number} rowIndex
6000          * @param {Roo.EventObject} e
6001          */
6002         "rowcontextmenu" : true,
6003         /**
6004          * @event cellcontextmenu
6005          * Fires when a cell is right clicked
6006          * @param {Roo.bootstrap.Table} this
6007          * @param {Number} rowIndex
6008          * @param {Number} cellIndex
6009          * @param {Roo.EventObject} e
6010          */
6011          "cellcontextmenu" : true,
6012          /**
6013          * @event headercontextmenu
6014          * Fires when a header is right clicked
6015          * @param {Roo.bootstrap.Table} this
6016          * @param {Number} columnIndex
6017          * @param {Roo.EventObject} e
6018          */
6019         "headercontextmenu" : true
6020     });
6021 };
6022
6023 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6024     
6025     cls: false,
6026     align: false,
6027     bgcolor: false,
6028     border: false,
6029     cellpadding: false,
6030     cellspacing: false,
6031     frame: false,
6032     rules: false,
6033     sortable: false,
6034     summary: false,
6035     width: false,
6036     striped : false,
6037     scrollBody : false,
6038     bordered: false,
6039     hover:  false,
6040     condensed : false,
6041     responsive : false,
6042     sm : false,
6043     cm : false,
6044     store : false,
6045     loadMask : false,
6046     footerShow : true,
6047     headerShow : true,
6048   
6049     rowSelection : false,
6050     cellSelection : false,
6051     layout : false,
6052     
6053     // Roo.Element - the tbody
6054     mainBody: false,
6055     // Roo.Element - thead element
6056     mainHead: false,
6057     
6058     container: false, // used by gridpanel...
6059     
6060     lazyLoad : false,
6061     
6062     CSS : Roo.util.CSS,
6063     
6064     getAutoCreate : function()
6065     {
6066         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6067         
6068         cfg = {
6069             tag: 'table',
6070             cls : 'table',
6071             cn : []
6072         };
6073         if (this.scrollBody) {
6074             cfg.cls += ' table-body-fixed';
6075         }    
6076         if (this.striped) {
6077             cfg.cls += ' table-striped';
6078         }
6079         
6080         if (this.hover) {
6081             cfg.cls += ' table-hover';
6082         }
6083         if (this.bordered) {
6084             cfg.cls += ' table-bordered';
6085         }
6086         if (this.condensed) {
6087             cfg.cls += ' table-condensed';
6088         }
6089         if (this.responsive) {
6090             cfg.cls += ' table-responsive';
6091         }
6092         
6093         if (this.cls) {
6094             cfg.cls+=  ' ' +this.cls;
6095         }
6096         
6097         // this lot should be simplifed...
6098         
6099         if (this.align) {
6100             cfg.align=this.align;
6101         }
6102         if (this.bgcolor) {
6103             cfg.bgcolor=this.bgcolor;
6104         }
6105         if (this.border) {
6106             cfg.border=this.border;
6107         }
6108         if (this.cellpadding) {
6109             cfg.cellpadding=this.cellpadding;
6110         }
6111         if (this.cellspacing) {
6112             cfg.cellspacing=this.cellspacing;
6113         }
6114         if (this.frame) {
6115             cfg.frame=this.frame;
6116         }
6117         if (this.rules) {
6118             cfg.rules=this.rules;
6119         }
6120         if (this.sortable) {
6121             cfg.sortable=this.sortable;
6122         }
6123         if (this.summary) {
6124             cfg.summary=this.summary;
6125         }
6126         if (this.width) {
6127             cfg.width=this.width;
6128         }
6129         if (this.layout) {
6130             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6131         }
6132         
6133         if(this.store || this.cm){
6134             if(this.headerShow){
6135                 cfg.cn.push(this.renderHeader());
6136             }
6137             
6138             cfg.cn.push(this.renderBody());
6139             
6140             if(this.footerShow){
6141                 cfg.cn.push(this.renderFooter());
6142             }
6143             // where does this come from?
6144             //cfg.cls+=  ' TableGrid';
6145         }
6146         
6147         return { cn : [ cfg ] };
6148     },
6149     
6150     initEvents : function()
6151     {   
6152         if(!this.store || !this.cm){
6153             return;
6154         }
6155         if (this.selModel) {
6156             this.selModel.initEvents();
6157         }
6158         
6159         
6160         //Roo.log('initEvents with ds!!!!');
6161         
6162         this.mainBody = this.el.select('tbody', true).first();
6163         this.mainHead = this.el.select('thead', true).first();
6164         
6165         
6166         
6167         
6168         var _this = this;
6169         
6170         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6171             e.on('click', _this.sort, _this);
6172         });
6173         
6174         this.mainBody.on("click", this.onClick, this);
6175         this.mainBody.on("dblclick", this.onDblClick, this);
6176         
6177         // why is this done????? = it breaks dialogs??
6178         //this.parent().el.setStyle('position', 'relative');
6179         
6180         
6181         if (this.footer) {
6182             this.footer.parentId = this.id;
6183             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6184             
6185             if(this.lazyLoad){
6186                 this.el.select('tfoot tr td').first().addClass('hide');
6187             }
6188         } 
6189         
6190         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6191         
6192         this.store.on('load', this.onLoad, this);
6193         this.store.on('beforeload', this.onBeforeLoad, this);
6194         this.store.on('update', this.onUpdate, this);
6195         this.store.on('add', this.onAdd, this);
6196         this.store.on("clear", this.clear, this);
6197         
6198         this.el.on("contextmenu", this.onContextMenu, this);
6199         
6200         this.mainBody.on('scroll', this.onBodyScroll, this);
6201         
6202         this.cm.on("headerchange", this.onHeaderChange, this);
6203         
6204         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6205         
6206     },
6207     
6208     onContextMenu : function(e, t)
6209     {
6210         this.processEvent("contextmenu", e);
6211     },
6212     
6213     processEvent : function(name, e)
6214     {
6215         if (name != 'touchstart' ) {
6216             this.fireEvent(name, e);    
6217         }
6218         
6219         var t = e.getTarget();
6220         
6221         var cell = Roo.get(t);
6222         
6223         if(!cell){
6224             return;
6225         }
6226         
6227         if(cell.findParent('tfoot', false, true)){
6228             return;
6229         }
6230         
6231         if(cell.findParent('thead', false, true)){
6232             
6233             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6234                 cell = Roo.get(t).findParent('th', false, true);
6235                 if (!cell) {
6236                     Roo.log("failed to find th in thead?");
6237                     Roo.log(e.getTarget());
6238                     return;
6239                 }
6240             }
6241             
6242             var cellIndex = cell.dom.cellIndex;
6243             
6244             var ename = name == 'touchstart' ? 'click' : name;
6245             this.fireEvent("header" + ename, this, cellIndex, e);
6246             
6247             return;
6248         }
6249         
6250         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6251             cell = Roo.get(t).findParent('td', false, true);
6252             if (!cell) {
6253                 Roo.log("failed to find th in tbody?");
6254                 Roo.log(e.getTarget());
6255                 return;
6256             }
6257         }
6258         
6259         var row = cell.findParent('tr', false, true);
6260         var cellIndex = cell.dom.cellIndex;
6261         var rowIndex = row.dom.rowIndex - 1;
6262         
6263         if(row !== false){
6264             
6265             this.fireEvent("row" + name, this, rowIndex, e);
6266             
6267             if(cell !== false){
6268             
6269                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6270             }
6271         }
6272         
6273     },
6274     
6275     onMouseover : function(e, el)
6276     {
6277         var cell = Roo.get(el);
6278         
6279         if(!cell){
6280             return;
6281         }
6282         
6283         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6284             cell = cell.findParent('td', false, true);
6285         }
6286         
6287         var row = cell.findParent('tr', false, true);
6288         var cellIndex = cell.dom.cellIndex;
6289         var rowIndex = row.dom.rowIndex - 1; // start from 0
6290         
6291         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6292         
6293     },
6294     
6295     onMouseout : function(e, el)
6296     {
6297         var cell = Roo.get(el);
6298         
6299         if(!cell){
6300             return;
6301         }
6302         
6303         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6304             cell = cell.findParent('td', false, true);
6305         }
6306         
6307         var row = cell.findParent('tr', false, true);
6308         var cellIndex = cell.dom.cellIndex;
6309         var rowIndex = row.dom.rowIndex - 1; // start from 0
6310         
6311         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6312         
6313     },
6314     
6315     onClick : function(e, el)
6316     {
6317         var cell = Roo.get(el);
6318         
6319         if(!cell || (!this.cellSelection && !this.rowSelection)){
6320             return;
6321         }
6322         
6323         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6324             cell = cell.findParent('td', false, true);
6325         }
6326         
6327         if(!cell || typeof(cell) == 'undefined'){
6328             return;
6329         }
6330         
6331         var row = cell.findParent('tr', false, true);
6332         
6333         if(!row || typeof(row) == 'undefined'){
6334             return;
6335         }
6336         
6337         var cellIndex = cell.dom.cellIndex;
6338         var rowIndex = this.getRowIndex(row);
6339         
6340         // why??? - should these not be based on SelectionModel?
6341         if(this.cellSelection){
6342             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6343         }
6344         
6345         if(this.rowSelection){
6346             this.fireEvent('rowclick', this, row, rowIndex, e);
6347         }
6348         
6349         
6350     },
6351         
6352     onDblClick : function(e,el)
6353     {
6354         var cell = Roo.get(el);
6355         
6356         if(!cell || (!this.cellSelection && !this.rowSelection)){
6357             return;
6358         }
6359         
6360         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6361             cell = cell.findParent('td', false, true);
6362         }
6363         
6364         if(!cell || typeof(cell) == 'undefined'){
6365             return;
6366         }
6367         
6368         var row = cell.findParent('tr', false, true);
6369         
6370         if(!row || typeof(row) == 'undefined'){
6371             return;
6372         }
6373         
6374         var cellIndex = cell.dom.cellIndex;
6375         var rowIndex = this.getRowIndex(row);
6376         
6377         if(this.cellSelection){
6378             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6379         }
6380         
6381         if(this.rowSelection){
6382             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6383         }
6384     },
6385     
6386     sort : function(e,el)
6387     {
6388         var col = Roo.get(el);
6389         
6390         if(!col.hasClass('sortable')){
6391             return;
6392         }
6393         
6394         var sort = col.attr('sort');
6395         var dir = 'ASC';
6396         
6397         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6398             dir = 'DESC';
6399         }
6400         
6401         this.store.sortInfo = {field : sort, direction : dir};
6402         
6403         if (this.footer) {
6404             Roo.log("calling footer first");
6405             this.footer.onClick('first');
6406         } else {
6407         
6408             this.store.load({ params : { start : 0 } });
6409         }
6410     },
6411     
6412     renderHeader : function()
6413     {
6414         var header = {
6415             tag: 'thead',
6416             cn : []
6417         };
6418         
6419         var cm = this.cm;
6420         this.totalWidth = 0;
6421         
6422         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6423             
6424             var config = cm.config[i];
6425             
6426             var c = {
6427                 tag: 'th',
6428                 cls : 'x-hcol-' + i,
6429                 style : '',
6430                 html: cm.getColumnHeader(i)
6431             };
6432             
6433             var hh = '';
6434             
6435             if(typeof(config.sortable) != 'undefined' && config.sortable){
6436                 c.cls = 'sortable';
6437                 c.html = '<i class="glyphicon"></i>' + c.html;
6438             }
6439             
6440             if(typeof(config.lgHeader) != 'undefined'){
6441                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6442             }
6443             
6444             if(typeof(config.mdHeader) != 'undefined'){
6445                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6446             }
6447             
6448             if(typeof(config.smHeader) != 'undefined'){
6449                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6450             }
6451             
6452             if(typeof(config.xsHeader) != 'undefined'){
6453                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6454             }
6455             
6456             if(hh.length){
6457                 c.html = hh;
6458             }
6459             
6460             if(typeof(config.tooltip) != 'undefined'){
6461                 c.tooltip = config.tooltip;
6462             }
6463             
6464             if(typeof(config.colspan) != 'undefined'){
6465                 c.colspan = config.colspan;
6466             }
6467             
6468             if(typeof(config.hidden) != 'undefined' && config.hidden){
6469                 c.style += ' display:none;';
6470             }
6471             
6472             if(typeof(config.dataIndex) != 'undefined'){
6473                 c.sort = config.dataIndex;
6474             }
6475             
6476            
6477             
6478             if(typeof(config.align) != 'undefined' && config.align.length){
6479                 c.style += ' text-align:' + config.align + ';';
6480             }
6481             
6482             if(typeof(config.width) != 'undefined'){
6483                 c.style += ' width:' + config.width + 'px;';
6484                 this.totalWidth += config.width;
6485             } else {
6486                 this.totalWidth += 100; // assume minimum of 100 per column?
6487             }
6488             
6489             if(typeof(config.cls) != 'undefined'){
6490                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6491             }
6492             
6493             ['xs','sm','md','lg'].map(function(size){
6494                 
6495                 if(typeof(config[size]) == 'undefined'){
6496                     return;
6497                 }
6498                 
6499                 if (!config[size]) { // 0 = hidden
6500                     c.cls += ' hidden-' + size;
6501                     return;
6502                 }
6503                 
6504                 c.cls += ' col-' + size + '-' + config[size];
6505
6506             });
6507             
6508             header.cn.push(c)
6509         }
6510         
6511         return header;
6512     },
6513     
6514     renderBody : function()
6515     {
6516         var body = {
6517             tag: 'tbody',
6518             cn : [
6519                 {
6520                     tag: 'tr',
6521                     cn : [
6522                         {
6523                             tag : 'td',
6524                             colspan :  this.cm.getColumnCount()
6525                         }
6526                     ]
6527                 }
6528             ]
6529         };
6530         
6531         return body;
6532     },
6533     
6534     renderFooter : function()
6535     {
6536         var footer = {
6537             tag: 'tfoot',
6538             cn : [
6539                 {
6540                     tag: 'tr',
6541                     cn : [
6542                         {
6543                             tag : 'td',
6544                             colspan :  this.cm.getColumnCount()
6545                         }
6546                     ]
6547                 }
6548             ]
6549         };
6550         
6551         return footer;
6552     },
6553     
6554     
6555     
6556     onLoad : function()
6557     {
6558 //        Roo.log('ds onload');
6559         this.clear();
6560         
6561         var _this = this;
6562         var cm = this.cm;
6563         var ds = this.store;
6564         
6565         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6566             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6567             if (_this.store.sortInfo) {
6568                     
6569                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6570                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6571                 }
6572                 
6573                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6574                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6575                 }
6576             }
6577         });
6578         
6579         var tbody =  this.mainBody;
6580               
6581         if(ds.getCount() > 0){
6582             ds.data.each(function(d,rowIndex){
6583                 var row =  this.renderRow(cm, ds, rowIndex);
6584                 
6585                 tbody.createChild(row);
6586                 
6587                 var _this = this;
6588                 
6589                 if(row.cellObjects.length){
6590                     Roo.each(row.cellObjects, function(r){
6591                         _this.renderCellObject(r);
6592                     })
6593                 }
6594                 
6595             }, this);
6596         }
6597         
6598         Roo.each(this.el.select('tbody td', true).elements, function(e){
6599             e.on('mouseover', _this.onMouseover, _this);
6600         });
6601         
6602         Roo.each(this.el.select('tbody td', true).elements, function(e){
6603             e.on('mouseout', _this.onMouseout, _this);
6604         });
6605         this.fireEvent('rowsrendered', this);
6606         //if(this.loadMask){
6607         //    this.maskEl.hide();
6608         //}
6609         
6610         this.autoSize();
6611     },
6612     
6613     
6614     onUpdate : function(ds,record)
6615     {
6616         this.refreshRow(record);
6617         this.autoSize();
6618     },
6619     
6620     onRemove : function(ds, record, index, isUpdate){
6621         if(isUpdate !== true){
6622             this.fireEvent("beforerowremoved", this, index, record);
6623         }
6624         var bt = this.mainBody.dom;
6625         
6626         var rows = this.el.select('tbody > tr', true).elements;
6627         
6628         if(typeof(rows[index]) != 'undefined'){
6629             bt.removeChild(rows[index].dom);
6630         }
6631         
6632 //        if(bt.rows[index]){
6633 //            bt.removeChild(bt.rows[index]);
6634 //        }
6635         
6636         if(isUpdate !== true){
6637             //this.stripeRows(index);
6638             //this.syncRowHeights(index, index);
6639             //this.layout();
6640             this.fireEvent("rowremoved", this, index, record);
6641         }
6642     },
6643     
6644     onAdd : function(ds, records, rowIndex)
6645     {
6646         //Roo.log('on Add called');
6647         // - note this does not handle multiple adding very well..
6648         var bt = this.mainBody.dom;
6649         for (var i =0 ; i < records.length;i++) {
6650             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6651             //Roo.log(records[i]);
6652             //Roo.log(this.store.getAt(rowIndex+i));
6653             this.insertRow(this.store, rowIndex + i, false);
6654             return;
6655         }
6656         
6657     },
6658     
6659     
6660     refreshRow : function(record){
6661         var ds = this.store, index;
6662         if(typeof record == 'number'){
6663             index = record;
6664             record = ds.getAt(index);
6665         }else{
6666             index = ds.indexOf(record);
6667         }
6668         this.insertRow(ds, index, true);
6669         this.autoSize();
6670         this.onRemove(ds, record, index+1, true);
6671         this.autoSize();
6672         //this.syncRowHeights(index, index);
6673         //this.layout();
6674         this.fireEvent("rowupdated", this, index, record);
6675     },
6676     
6677     insertRow : function(dm, rowIndex, isUpdate){
6678         
6679         if(!isUpdate){
6680             this.fireEvent("beforerowsinserted", this, rowIndex);
6681         }
6682             //var s = this.getScrollState();
6683         var row = this.renderRow(this.cm, this.store, rowIndex);
6684         // insert before rowIndex..
6685         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6686         
6687         var _this = this;
6688                 
6689         if(row.cellObjects.length){
6690             Roo.each(row.cellObjects, function(r){
6691                 _this.renderCellObject(r);
6692             })
6693         }
6694             
6695         if(!isUpdate){
6696             this.fireEvent("rowsinserted", this, rowIndex);
6697             //this.syncRowHeights(firstRow, lastRow);
6698             //this.stripeRows(firstRow);
6699             //this.layout();
6700         }
6701         
6702     },
6703     
6704     
6705     getRowDom : function(rowIndex)
6706     {
6707         var rows = this.el.select('tbody > tr', true).elements;
6708         
6709         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6710         
6711     },
6712     // returns the object tree for a tr..
6713   
6714     
6715     renderRow : function(cm, ds, rowIndex) 
6716     {
6717         var d = ds.getAt(rowIndex);
6718         
6719         var row = {
6720             tag : 'tr',
6721             cls : 'x-row-' + rowIndex,
6722             cn : []
6723         };
6724             
6725         var cellObjects = [];
6726         
6727         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6728             var config = cm.config[i];
6729             
6730             var renderer = cm.getRenderer(i);
6731             var value = '';
6732             var id = false;
6733             
6734             if(typeof(renderer) !== 'undefined'){
6735                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6736             }
6737             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6738             // and are rendered into the cells after the row is rendered - using the id for the element.
6739             
6740             if(typeof(value) === 'object'){
6741                 id = Roo.id();
6742                 cellObjects.push({
6743                     container : id,
6744                     cfg : value 
6745                 })
6746             }
6747             
6748             var rowcfg = {
6749                 record: d,
6750                 rowIndex : rowIndex,
6751                 colIndex : i,
6752                 rowClass : ''
6753             };
6754
6755             this.fireEvent('rowclass', this, rowcfg);
6756             
6757             var td = {
6758                 tag: 'td',
6759                 cls : rowcfg.rowClass + ' x-col-' + i,
6760                 style: '',
6761                 html: (typeof(value) === 'object') ? '' : value
6762             };
6763             
6764             if (id) {
6765                 td.id = id;
6766             }
6767             
6768             if(typeof(config.colspan) != 'undefined'){
6769                 td.colspan = config.colspan;
6770             }
6771             
6772             if(typeof(config.hidden) != 'undefined' && config.hidden){
6773                 td.style += ' display:none;';
6774             }
6775             
6776             if(typeof(config.align) != 'undefined' && config.align.length){
6777                 td.style += ' text-align:' + config.align + ';';
6778             }
6779             
6780             if(typeof(config.width) != 'undefined'){
6781                 td.style += ' width:' +  config.width + 'px;';
6782             }
6783             
6784             if(typeof(config.cursor) != 'undefined'){
6785                 td.style += ' cursor:' +  config.cursor + ';';
6786             }
6787             
6788             if(typeof(config.cls) != 'undefined'){
6789                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6790             }
6791             
6792             ['xs','sm','md','lg'].map(function(size){
6793                 
6794                 if(typeof(config[size]) == 'undefined'){
6795                     return;
6796                 }
6797                 
6798                 if (!config[size]) { // 0 = hidden
6799                     td.cls += ' hidden-' + size;
6800                     return;
6801                 }
6802                 
6803                 td.cls += ' col-' + size + '-' + config[size];
6804
6805             });
6806             
6807             row.cn.push(td);
6808            
6809         }
6810         
6811         row.cellObjects = cellObjects;
6812         
6813         return row;
6814           
6815     },
6816     
6817     
6818     
6819     onBeforeLoad : function()
6820     {
6821         //Roo.log('ds onBeforeLoad');
6822         
6823         //this.clear();
6824         
6825         //if(this.loadMask){
6826         //    this.maskEl.show();
6827         //}
6828     },
6829      /**
6830      * Remove all rows
6831      */
6832     clear : function()
6833     {
6834         this.el.select('tbody', true).first().dom.innerHTML = '';
6835     },
6836     /**
6837      * Show or hide a row.
6838      * @param {Number} rowIndex to show or hide
6839      * @param {Boolean} state hide
6840      */
6841     setRowVisibility : function(rowIndex, state)
6842     {
6843         var bt = this.mainBody.dom;
6844         
6845         var rows = this.el.select('tbody > tr', true).elements;
6846         
6847         if(typeof(rows[rowIndex]) == 'undefined'){
6848             return;
6849         }
6850         rows[rowIndex].dom.style.display = state ? '' : 'none';
6851     },
6852     
6853     
6854     getSelectionModel : function(){
6855         if(!this.selModel){
6856             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6857         }
6858         return this.selModel;
6859     },
6860     /*
6861      * Render the Roo.bootstrap object from renderder
6862      */
6863     renderCellObject : function(r)
6864     {
6865         var _this = this;
6866         
6867         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6868         
6869         var t = r.cfg.render(r.container);
6870         
6871         if(r.cfg.cn){
6872             Roo.each(r.cfg.cn, function(c){
6873                 var child = {
6874                     container: t.getChildContainer(),
6875                     cfg: c
6876                 };
6877                 _this.renderCellObject(child);
6878             })
6879         }
6880     },
6881     
6882     getRowIndex : function(row)
6883     {
6884         var rowIndex = -1;
6885         
6886         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6887             if(el != row){
6888                 return;
6889             }
6890             
6891             rowIndex = index;
6892         });
6893         
6894         return rowIndex;
6895     },
6896      /**
6897      * Returns the grid's underlying element = used by panel.Grid
6898      * @return {Element} The element
6899      */
6900     getGridEl : function(){
6901         return this.el;
6902     },
6903      /**
6904      * Forces a resize - used by panel.Grid
6905      * @return {Element} The element
6906      */
6907     autoSize : function()
6908     {
6909         //var ctr = Roo.get(this.container.dom.parentElement);
6910         var ctr = Roo.get(this.el.dom);
6911         
6912         var thd = this.getGridEl().select('thead',true).first();
6913         var tbd = this.getGridEl().select('tbody', true).first();
6914         var tfd = this.getGridEl().select('tfoot', true).first();
6915         
6916         var cw = ctr.getWidth();
6917         
6918         if (tbd) {
6919             
6920             tbd.setSize(ctr.getWidth(),
6921                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6922             );
6923             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6924             cw -= barsize;
6925         }
6926         cw = Math.max(cw, this.totalWidth);
6927         this.getGridEl().select('tr',true).setWidth(cw);
6928         // resize 'expandable coloumn?
6929         
6930         return; // we doe not have a view in this design..
6931         
6932     },
6933     onBodyScroll: function()
6934     {
6935         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6936         if(this.mainHead){
6937             this.mainHead.setStyle({
6938                 'position' : 'relative',
6939                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6940             });
6941         }
6942         
6943         if(this.lazyLoad){
6944             
6945             var scrollHeight = this.mainBody.dom.scrollHeight;
6946             
6947             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6948             
6949             var height = this.mainBody.getHeight();
6950             
6951             if(scrollHeight - height == scrollTop) {
6952                 
6953                 var total = this.ds.getTotalCount();
6954                 
6955                 if(this.footer.cursor + this.footer.pageSize < total){
6956                     
6957                     this.footer.ds.load({
6958                         params : {
6959                             start : this.footer.cursor + this.footer.pageSize,
6960                             limit : this.footer.pageSize
6961                         },
6962                         add : true
6963                     });
6964                 }
6965             }
6966             
6967         }
6968     },
6969     
6970     onHeaderChange : function()
6971     {
6972         var header = this.renderHeader();
6973         var table = this.el.select('table', true).first();
6974         
6975         this.mainHead.remove();
6976         this.mainHead = table.createChild(header, this.mainBody, false);
6977     },
6978     
6979     onHiddenChange : function(colModel, colIndex, hidden)
6980     {
6981         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6982         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6983         
6984         this.CSS.updateRule(thSelector, "display", "");
6985         this.CSS.updateRule(tdSelector, "display", "");
6986         
6987         if(hidden){
6988             this.CSS.updateRule(thSelector, "display", "none");
6989             this.CSS.updateRule(tdSelector, "display", "none");
6990         }
6991         
6992         this.onHeaderChange();
6993         this.onLoad();
6994         
6995     }
6996     
6997 });
6998
6999  
7000
7001  /*
7002  * - LGPL
7003  *
7004  * table cell
7005  * 
7006  */
7007
7008 /**
7009  * @class Roo.bootstrap.TableCell
7010  * @extends Roo.bootstrap.Component
7011  * Bootstrap TableCell class
7012  * @cfg {String} html cell contain text
7013  * @cfg {String} cls cell class
7014  * @cfg {String} tag cell tag (td|th) default td
7015  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7016  * @cfg {String} align Aligns the content in a cell
7017  * @cfg {String} axis Categorizes cells
7018  * @cfg {String} bgcolor Specifies the background color of a cell
7019  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7020  * @cfg {Number} colspan Specifies the number of columns a cell should span
7021  * @cfg {String} headers Specifies one or more header cells a cell is related to
7022  * @cfg {Number} height Sets the height of a cell
7023  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7024  * @cfg {Number} rowspan Sets the number of rows a cell should span
7025  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7026  * @cfg {String} valign Vertical aligns the content in a cell
7027  * @cfg {Number} width Specifies the width of a cell
7028  * 
7029  * @constructor
7030  * Create a new TableCell
7031  * @param {Object} config The config object
7032  */
7033
7034 Roo.bootstrap.TableCell = function(config){
7035     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7036 };
7037
7038 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7039     
7040     html: false,
7041     cls: false,
7042     tag: false,
7043     abbr: false,
7044     align: false,
7045     axis: false,
7046     bgcolor: false,
7047     charoff: false,
7048     colspan: false,
7049     headers: false,
7050     height: false,
7051     nowrap: false,
7052     rowspan: false,
7053     scope: false,
7054     valign: false,
7055     width: false,
7056     
7057     
7058     getAutoCreate : function(){
7059         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7060         
7061         cfg = {
7062             tag: 'td'
7063         };
7064         
7065         if(this.tag){
7066             cfg.tag = this.tag;
7067         }
7068         
7069         if (this.html) {
7070             cfg.html=this.html
7071         }
7072         if (this.cls) {
7073             cfg.cls=this.cls
7074         }
7075         if (this.abbr) {
7076             cfg.abbr=this.abbr
7077         }
7078         if (this.align) {
7079             cfg.align=this.align
7080         }
7081         if (this.axis) {
7082             cfg.axis=this.axis
7083         }
7084         if (this.bgcolor) {
7085             cfg.bgcolor=this.bgcolor
7086         }
7087         if (this.charoff) {
7088             cfg.charoff=this.charoff
7089         }
7090         if (this.colspan) {
7091             cfg.colspan=this.colspan
7092         }
7093         if (this.headers) {
7094             cfg.headers=this.headers
7095         }
7096         if (this.height) {
7097             cfg.height=this.height
7098         }
7099         if (this.nowrap) {
7100             cfg.nowrap=this.nowrap
7101         }
7102         if (this.rowspan) {
7103             cfg.rowspan=this.rowspan
7104         }
7105         if (this.scope) {
7106             cfg.scope=this.scope
7107         }
7108         if (this.valign) {
7109             cfg.valign=this.valign
7110         }
7111         if (this.width) {
7112             cfg.width=this.width
7113         }
7114         
7115         
7116         return cfg;
7117     }
7118    
7119 });
7120
7121  
7122
7123  /*
7124  * - LGPL
7125  *
7126  * table row
7127  * 
7128  */
7129
7130 /**
7131  * @class Roo.bootstrap.TableRow
7132  * @extends Roo.bootstrap.Component
7133  * Bootstrap TableRow class
7134  * @cfg {String} cls row class
7135  * @cfg {String} align Aligns the content in a table row
7136  * @cfg {String} bgcolor Specifies a background color for a table row
7137  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7138  * @cfg {String} valign Vertical aligns the content in a table row
7139  * 
7140  * @constructor
7141  * Create a new TableRow
7142  * @param {Object} config The config object
7143  */
7144
7145 Roo.bootstrap.TableRow = function(config){
7146     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7147 };
7148
7149 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7150     
7151     cls: false,
7152     align: false,
7153     bgcolor: false,
7154     charoff: false,
7155     valign: false,
7156     
7157     getAutoCreate : function(){
7158         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7159         
7160         cfg = {
7161             tag: 'tr'
7162         };
7163             
7164         if(this.cls){
7165             cfg.cls = this.cls;
7166         }
7167         if(this.align){
7168             cfg.align = this.align;
7169         }
7170         if(this.bgcolor){
7171             cfg.bgcolor = this.bgcolor;
7172         }
7173         if(this.charoff){
7174             cfg.charoff = this.charoff;
7175         }
7176         if(this.valign){
7177             cfg.valign = this.valign;
7178         }
7179         
7180         return cfg;
7181     }
7182    
7183 });
7184
7185  
7186
7187  /*
7188  * - LGPL
7189  *
7190  * table body
7191  * 
7192  */
7193
7194 /**
7195  * @class Roo.bootstrap.TableBody
7196  * @extends Roo.bootstrap.Component
7197  * Bootstrap TableBody class
7198  * @cfg {String} cls element class
7199  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7200  * @cfg {String} align Aligns the content inside the element
7201  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7202  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7203  * 
7204  * @constructor
7205  * Create a new TableBody
7206  * @param {Object} config The config object
7207  */
7208
7209 Roo.bootstrap.TableBody = function(config){
7210     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7211 };
7212
7213 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7214     
7215     cls: false,
7216     tag: false,
7217     align: false,
7218     charoff: false,
7219     valign: false,
7220     
7221     getAutoCreate : function(){
7222         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7223         
7224         cfg = {
7225             tag: 'tbody'
7226         };
7227             
7228         if (this.cls) {
7229             cfg.cls=this.cls
7230         }
7231         if(this.tag){
7232             cfg.tag = this.tag;
7233         }
7234         
7235         if(this.align){
7236             cfg.align = this.align;
7237         }
7238         if(this.charoff){
7239             cfg.charoff = this.charoff;
7240         }
7241         if(this.valign){
7242             cfg.valign = this.valign;
7243         }
7244         
7245         return cfg;
7246     }
7247     
7248     
7249 //    initEvents : function()
7250 //    {
7251 //        
7252 //        if(!this.store){
7253 //            return;
7254 //        }
7255 //        
7256 //        this.store = Roo.factory(this.store, Roo.data);
7257 //        this.store.on('load', this.onLoad, this);
7258 //        
7259 //        this.store.load();
7260 //        
7261 //    },
7262 //    
7263 //    onLoad: function () 
7264 //    {   
7265 //        this.fireEvent('load', this);
7266 //    }
7267 //    
7268 //   
7269 });
7270
7271  
7272
7273  /*
7274  * Based on:
7275  * Ext JS Library 1.1.1
7276  * Copyright(c) 2006-2007, Ext JS, LLC.
7277  *
7278  * Originally Released Under LGPL - original licence link has changed is not relivant.
7279  *
7280  * Fork - LGPL
7281  * <script type="text/javascript">
7282  */
7283
7284 // as we use this in bootstrap.
7285 Roo.namespace('Roo.form');
7286  /**
7287  * @class Roo.form.Action
7288  * Internal Class used to handle form actions
7289  * @constructor
7290  * @param {Roo.form.BasicForm} el The form element or its id
7291  * @param {Object} config Configuration options
7292  */
7293
7294  
7295  
7296 // define the action interface
7297 Roo.form.Action = function(form, options){
7298     this.form = form;
7299     this.options = options || {};
7300 };
7301 /**
7302  * Client Validation Failed
7303  * @const 
7304  */
7305 Roo.form.Action.CLIENT_INVALID = 'client';
7306 /**
7307  * Server Validation Failed
7308  * @const 
7309  */
7310 Roo.form.Action.SERVER_INVALID = 'server';
7311  /**
7312  * Connect to Server Failed
7313  * @const 
7314  */
7315 Roo.form.Action.CONNECT_FAILURE = 'connect';
7316 /**
7317  * Reading Data from Server Failed
7318  * @const 
7319  */
7320 Roo.form.Action.LOAD_FAILURE = 'load';
7321
7322 Roo.form.Action.prototype = {
7323     type : 'default',
7324     failureType : undefined,
7325     response : undefined,
7326     result : undefined,
7327
7328     // interface method
7329     run : function(options){
7330
7331     },
7332
7333     // interface method
7334     success : function(response){
7335
7336     },
7337
7338     // interface method
7339     handleResponse : function(response){
7340
7341     },
7342
7343     // default connection failure
7344     failure : function(response){
7345         
7346         this.response = response;
7347         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7348         this.form.afterAction(this, false);
7349     },
7350
7351     processResponse : function(response){
7352         this.response = response;
7353         if(!response.responseText){
7354             return true;
7355         }
7356         this.result = this.handleResponse(response);
7357         return this.result;
7358     },
7359
7360     // utility functions used internally
7361     getUrl : function(appendParams){
7362         var url = this.options.url || this.form.url || this.form.el.dom.action;
7363         if(appendParams){
7364             var p = this.getParams();
7365             if(p){
7366                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7367             }
7368         }
7369         return url;
7370     },
7371
7372     getMethod : function(){
7373         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7374     },
7375
7376     getParams : function(){
7377         var bp = this.form.baseParams;
7378         var p = this.options.params;
7379         if(p){
7380             if(typeof p == "object"){
7381                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7382             }else if(typeof p == 'string' && bp){
7383                 p += '&' + Roo.urlEncode(bp);
7384             }
7385         }else if(bp){
7386             p = Roo.urlEncode(bp);
7387         }
7388         return p;
7389     },
7390
7391     createCallback : function(){
7392         return {
7393             success: this.success,
7394             failure: this.failure,
7395             scope: this,
7396             timeout: (this.form.timeout*1000),
7397             upload: this.form.fileUpload ? this.success : undefined
7398         };
7399     }
7400 };
7401
7402 Roo.form.Action.Submit = function(form, options){
7403     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7404 };
7405
7406 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7407     type : 'submit',
7408
7409     haveProgress : false,
7410     uploadComplete : false,
7411     
7412     // uploadProgress indicator.
7413     uploadProgress : function()
7414     {
7415         if (!this.form.progressUrl) {
7416             return;
7417         }
7418         
7419         if (!this.haveProgress) {
7420             Roo.MessageBox.progress("Uploading", "Uploading");
7421         }
7422         if (this.uploadComplete) {
7423            Roo.MessageBox.hide();
7424            return;
7425         }
7426         
7427         this.haveProgress = true;
7428    
7429         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7430         
7431         var c = new Roo.data.Connection();
7432         c.request({
7433             url : this.form.progressUrl,
7434             params: {
7435                 id : uid
7436             },
7437             method: 'GET',
7438             success : function(req){
7439                //console.log(data);
7440                 var rdata = false;
7441                 var edata;
7442                 try  {
7443                    rdata = Roo.decode(req.responseText)
7444                 } catch (e) {
7445                     Roo.log("Invalid data from server..");
7446                     Roo.log(edata);
7447                     return;
7448                 }
7449                 if (!rdata || !rdata.success) {
7450                     Roo.log(rdata);
7451                     Roo.MessageBox.alert(Roo.encode(rdata));
7452                     return;
7453                 }
7454                 var data = rdata.data;
7455                 
7456                 if (this.uploadComplete) {
7457                    Roo.MessageBox.hide();
7458                    return;
7459                 }
7460                    
7461                 if (data){
7462                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7463                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7464                     );
7465                 }
7466                 this.uploadProgress.defer(2000,this);
7467             },
7468        
7469             failure: function(data) {
7470                 Roo.log('progress url failed ');
7471                 Roo.log(data);
7472             },
7473             scope : this
7474         });
7475            
7476     },
7477     
7478     
7479     run : function()
7480     {
7481         // run get Values on the form, so it syncs any secondary forms.
7482         this.form.getValues();
7483         
7484         var o = this.options;
7485         var method = this.getMethod();
7486         var isPost = method == 'POST';
7487         if(o.clientValidation === false || this.form.isValid()){
7488             
7489             if (this.form.progressUrl) {
7490                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7491                     (new Date() * 1) + '' + Math.random());
7492                     
7493             } 
7494             
7495             
7496             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7497                 form:this.form.el.dom,
7498                 url:this.getUrl(!isPost),
7499                 method: method,
7500                 params:isPost ? this.getParams() : null,
7501                 isUpload: this.form.fileUpload
7502             }));
7503             
7504             this.uploadProgress();
7505
7506         }else if (o.clientValidation !== false){ // client validation failed
7507             this.failureType = Roo.form.Action.CLIENT_INVALID;
7508             this.form.afterAction(this, false);
7509         }
7510     },
7511
7512     success : function(response)
7513     {
7514         this.uploadComplete= true;
7515         if (this.haveProgress) {
7516             Roo.MessageBox.hide();
7517         }
7518         
7519         
7520         var result = this.processResponse(response);
7521         if(result === true || result.success){
7522             this.form.afterAction(this, true);
7523             return;
7524         }
7525         if(result.errors){
7526             this.form.markInvalid(result.errors);
7527             this.failureType = Roo.form.Action.SERVER_INVALID;
7528         }
7529         this.form.afterAction(this, false);
7530     },
7531     failure : function(response)
7532     {
7533         this.uploadComplete= true;
7534         if (this.haveProgress) {
7535             Roo.MessageBox.hide();
7536         }
7537         
7538         this.response = response;
7539         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7540         this.form.afterAction(this, false);
7541     },
7542     
7543     handleResponse : function(response){
7544         if(this.form.errorReader){
7545             var rs = this.form.errorReader.read(response);
7546             var errors = [];
7547             if(rs.records){
7548                 for(var i = 0, len = rs.records.length; i < len; i++) {
7549                     var r = rs.records[i];
7550                     errors[i] = r.data;
7551                 }
7552             }
7553             if(errors.length < 1){
7554                 errors = null;
7555             }
7556             return {
7557                 success : rs.success,
7558                 errors : errors
7559             };
7560         }
7561         var ret = false;
7562         try {
7563             ret = Roo.decode(response.responseText);
7564         } catch (e) {
7565             ret = {
7566                 success: false,
7567                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7568                 errors : []
7569             };
7570         }
7571         return ret;
7572         
7573     }
7574 });
7575
7576
7577 Roo.form.Action.Load = function(form, options){
7578     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7579     this.reader = this.form.reader;
7580 };
7581
7582 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7583     type : 'load',
7584
7585     run : function(){
7586         
7587         Roo.Ajax.request(Roo.apply(
7588                 this.createCallback(), {
7589                     method:this.getMethod(),
7590                     url:this.getUrl(false),
7591                     params:this.getParams()
7592         }));
7593     },
7594
7595     success : function(response){
7596         
7597         var result = this.processResponse(response);
7598         if(result === true || !result.success || !result.data){
7599             this.failureType = Roo.form.Action.LOAD_FAILURE;
7600             this.form.afterAction(this, false);
7601             return;
7602         }
7603         this.form.clearInvalid();
7604         this.form.setValues(result.data);
7605         this.form.afterAction(this, true);
7606     },
7607
7608     handleResponse : function(response){
7609         if(this.form.reader){
7610             var rs = this.form.reader.read(response);
7611             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7612             return {
7613                 success : rs.success,
7614                 data : data
7615             };
7616         }
7617         return Roo.decode(response.responseText);
7618     }
7619 });
7620
7621 Roo.form.Action.ACTION_TYPES = {
7622     'load' : Roo.form.Action.Load,
7623     'submit' : Roo.form.Action.Submit
7624 };/*
7625  * - LGPL
7626  *
7627  * form
7628  *
7629  */
7630
7631 /**
7632  * @class Roo.bootstrap.Form
7633  * @extends Roo.bootstrap.Component
7634  * Bootstrap Form class
7635  * @cfg {String} method  GET | POST (default POST)
7636  * @cfg {String} labelAlign top | left (default top)
7637  * @cfg {String} align left  | right - for navbars
7638  * @cfg {Boolean} loadMask load mask when submit (default true)
7639
7640  *
7641  * @constructor
7642  * Create a new Form
7643  * @param {Object} config The config object
7644  */
7645
7646
7647 Roo.bootstrap.Form = function(config){
7648     
7649     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7650     
7651     Roo.bootstrap.Form.popover.apply();
7652     
7653     this.addEvents({
7654         /**
7655          * @event clientvalidation
7656          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7657          * @param {Form} this
7658          * @param {Boolean} valid true if the form has passed client-side validation
7659          */
7660         clientvalidation: true,
7661         /**
7662          * @event beforeaction
7663          * Fires before any action is performed. Return false to cancel the action.
7664          * @param {Form} this
7665          * @param {Action} action The action to be performed
7666          */
7667         beforeaction: true,
7668         /**
7669          * @event actionfailed
7670          * Fires when an action fails.
7671          * @param {Form} this
7672          * @param {Action} action The action that failed
7673          */
7674         actionfailed : true,
7675         /**
7676          * @event actioncomplete
7677          * Fires when an action is completed.
7678          * @param {Form} this
7679          * @param {Action} action The action that completed
7680          */
7681         actioncomplete : true
7682     });
7683 };
7684
7685 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7686
7687      /**
7688      * @cfg {String} method
7689      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7690      */
7691     method : 'POST',
7692     /**
7693      * @cfg {String} url
7694      * The URL to use for form actions if one isn't supplied in the action options.
7695      */
7696     /**
7697      * @cfg {Boolean} fileUpload
7698      * Set to true if this form is a file upload.
7699      */
7700
7701     /**
7702      * @cfg {Object} baseParams
7703      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7704      */
7705
7706     /**
7707      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7708      */
7709     timeout: 30,
7710     /**
7711      * @cfg {Sting} align (left|right) for navbar forms
7712      */
7713     align : 'left',
7714
7715     // private
7716     activeAction : null,
7717
7718     /**
7719      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7720      * element by passing it or its id or mask the form itself by passing in true.
7721      * @type Mixed
7722      */
7723     waitMsgTarget : false,
7724
7725     loadMask : true,
7726     
7727     /**
7728      * @cfg {Boolean} errorMask (true|false) default false
7729      */
7730     errorMask : false,
7731     
7732     /**
7733      * @cfg {Number} maskOffset Default 100
7734      */
7735     maskOffset : 100,
7736     
7737     /**
7738      * @cfg {Boolean} maskBody
7739      */
7740     maskBody : false,
7741
7742     getAutoCreate : function(){
7743
7744         var cfg = {
7745             tag: 'form',
7746             method : this.method || 'POST',
7747             id : this.id || Roo.id(),
7748             cls : ''
7749         };
7750         if (this.parent().xtype.match(/^Nav/)) {
7751             cfg.cls = 'navbar-form navbar-' + this.align;
7752
7753         }
7754
7755         if (this.labelAlign == 'left' ) {
7756             cfg.cls += ' form-horizontal';
7757         }
7758
7759
7760         return cfg;
7761     },
7762     initEvents : function()
7763     {
7764         this.el.on('submit', this.onSubmit, this);
7765         // this was added as random key presses on the form where triggering form submit.
7766         this.el.on('keypress', function(e) {
7767             if (e.getCharCode() != 13) {
7768                 return true;
7769             }
7770             // we might need to allow it for textareas.. and some other items.
7771             // check e.getTarget().
7772
7773             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7774                 return true;
7775             }
7776
7777             Roo.log("keypress blocked");
7778
7779             e.preventDefault();
7780             return false;
7781         });
7782         
7783     },
7784     // private
7785     onSubmit : function(e){
7786         e.stopEvent();
7787     },
7788
7789      /**
7790      * Returns true if client-side validation on the form is successful.
7791      * @return Boolean
7792      */
7793     isValid : function(){
7794         var items = this.getItems();
7795         var valid = true;
7796         var target = false;
7797         
7798         items.each(function(f){
7799             if(f.validate()){
7800                 return;
7801             }
7802             valid = false;
7803
7804             if(!target && f.el.isVisible(true)){
7805                 target = f;
7806             }
7807            
7808         });
7809         
7810         if(this.errorMask && !valid){
7811             Roo.bootstrap.Form.popover.mask(this, target);
7812         }
7813         
7814         return valid;
7815     },
7816     
7817     /**
7818      * Returns true if any fields in this form have changed since their original load.
7819      * @return Boolean
7820      */
7821     isDirty : function(){
7822         var dirty = false;
7823         var items = this.getItems();
7824         items.each(function(f){
7825            if(f.isDirty()){
7826                dirty = true;
7827                return false;
7828            }
7829            return true;
7830         });
7831         return dirty;
7832     },
7833      /**
7834      * Performs a predefined action (submit or load) or custom actions you define on this form.
7835      * @param {String} actionName The name of the action type
7836      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7837      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7838      * accept other config options):
7839      * <pre>
7840 Property          Type             Description
7841 ----------------  ---------------  ----------------------------------------------------------------------------------
7842 url               String           The url for the action (defaults to the form's url)
7843 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7844 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7845 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7846                                    validate the form on the client (defaults to false)
7847      * </pre>
7848      * @return {BasicForm} this
7849      */
7850     doAction : function(action, options){
7851         if(typeof action == 'string'){
7852             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7853         }
7854         if(this.fireEvent('beforeaction', this, action) !== false){
7855             this.beforeAction(action);
7856             action.run.defer(100, action);
7857         }
7858         return this;
7859     },
7860
7861     // private
7862     beforeAction : function(action){
7863         var o = action.options;
7864         
7865         if(this.loadMask){
7866             
7867             if(this.maskBody){
7868                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7869             } else {
7870                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7871             }
7872         }
7873         // not really supported yet.. ??
7874
7875         //if(this.waitMsgTarget === true){
7876         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7877         //}else if(this.waitMsgTarget){
7878         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7879         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7880         //}else {
7881         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7882        // }
7883
7884     },
7885
7886     // private
7887     afterAction : function(action, success){
7888         this.activeAction = null;
7889         var o = action.options;
7890
7891         if(this.loadMask){
7892             
7893             if(this.maskBody){
7894                 Roo.get(document.body).unmask();
7895             } else {
7896                 this.el.unmask();
7897             }
7898         }
7899         
7900         //if(this.waitMsgTarget === true){
7901 //            this.el.unmask();
7902         //}else if(this.waitMsgTarget){
7903         //    this.waitMsgTarget.unmask();
7904         //}else{
7905         //    Roo.MessageBox.updateProgress(1);
7906         //    Roo.MessageBox.hide();
7907        // }
7908         //
7909         if(success){
7910             if(o.reset){
7911                 this.reset();
7912             }
7913             Roo.callback(o.success, o.scope, [this, action]);
7914             this.fireEvent('actioncomplete', this, action);
7915
7916         }else{
7917
7918             // failure condition..
7919             // we have a scenario where updates need confirming.
7920             // eg. if a locking scenario exists..
7921             // we look for { errors : { needs_confirm : true }} in the response.
7922             if (
7923                 (typeof(action.result) != 'undefined')  &&
7924                 (typeof(action.result.errors) != 'undefined')  &&
7925                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7926            ){
7927                 var _t = this;
7928                 Roo.log("not supported yet");
7929                  /*
7930
7931                 Roo.MessageBox.confirm(
7932                     "Change requires confirmation",
7933                     action.result.errorMsg,
7934                     function(r) {
7935                         if (r != 'yes') {
7936                             return;
7937                         }
7938                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7939                     }
7940
7941                 );
7942                 */
7943
7944
7945                 return;
7946             }
7947
7948             Roo.callback(o.failure, o.scope, [this, action]);
7949             // show an error message if no failed handler is set..
7950             if (!this.hasListener('actionfailed')) {
7951                 Roo.log("need to add dialog support");
7952                 /*
7953                 Roo.MessageBox.alert("Error",
7954                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7955                         action.result.errorMsg :
7956                         "Saving Failed, please check your entries or try again"
7957                 );
7958                 */
7959             }
7960
7961             this.fireEvent('actionfailed', this, action);
7962         }
7963
7964     },
7965     /**
7966      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7967      * @param {String} id The value to search for
7968      * @return Field
7969      */
7970     findField : function(id){
7971         var items = this.getItems();
7972         var field = items.get(id);
7973         if(!field){
7974              items.each(function(f){
7975                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7976                     field = f;
7977                     return false;
7978                 }
7979                 return true;
7980             });
7981         }
7982         return field || null;
7983     },
7984      /**
7985      * Mark fields in this form invalid in bulk.
7986      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7987      * @return {BasicForm} this
7988      */
7989     markInvalid : function(errors){
7990         if(errors instanceof Array){
7991             for(var i = 0, len = errors.length; i < len; i++){
7992                 var fieldError = errors[i];
7993                 var f = this.findField(fieldError.id);
7994                 if(f){
7995                     f.markInvalid(fieldError.msg);
7996                 }
7997             }
7998         }else{
7999             var field, id;
8000             for(id in errors){
8001                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8002                     field.markInvalid(errors[id]);
8003                 }
8004             }
8005         }
8006         //Roo.each(this.childForms || [], function (f) {
8007         //    f.markInvalid(errors);
8008         //});
8009
8010         return this;
8011     },
8012
8013     /**
8014      * Set values for fields in this form in bulk.
8015      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8016      * @return {BasicForm} this
8017      */
8018     setValues : function(values){
8019         if(values instanceof Array){ // array of objects
8020             for(var i = 0, len = values.length; i < len; i++){
8021                 var v = values[i];
8022                 var f = this.findField(v.id);
8023                 if(f){
8024                     f.setValue(v.value);
8025                     if(this.trackResetOnLoad){
8026                         f.originalValue = f.getValue();
8027                     }
8028                 }
8029             }
8030         }else{ // object hash
8031             var field, id;
8032             for(id in values){
8033                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8034
8035                     if (field.setFromData &&
8036                         field.valueField &&
8037                         field.displayField &&
8038                         // combos' with local stores can
8039                         // be queried via setValue()
8040                         // to set their value..
8041                         (field.store && !field.store.isLocal)
8042                         ) {
8043                         // it's a combo
8044                         var sd = { };
8045                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8046                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8047                         field.setFromData(sd);
8048
8049                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8050                         
8051                         field.setFromData(values);
8052                         
8053                     } else {
8054                         field.setValue(values[id]);
8055                     }
8056
8057
8058                     if(this.trackResetOnLoad){
8059                         field.originalValue = field.getValue();
8060                     }
8061                 }
8062             }
8063         }
8064
8065         //Roo.each(this.childForms || [], function (f) {
8066         //    f.setValues(values);
8067         //});
8068
8069         return this;
8070     },
8071
8072     /**
8073      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8074      * they are returned as an array.
8075      * @param {Boolean} asString
8076      * @return {Object}
8077      */
8078     getValues : function(asString){
8079         //if (this.childForms) {
8080             // copy values from the child forms
8081         //    Roo.each(this.childForms, function (f) {
8082         //        this.setValues(f.getValues());
8083         //    }, this);
8084         //}
8085
8086
8087
8088         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8089         if(asString === true){
8090             return fs;
8091         }
8092         return Roo.urlDecode(fs);
8093     },
8094
8095     /**
8096      * Returns the fields in this form as an object with key/value pairs.
8097      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8098      * @return {Object}
8099      */
8100     getFieldValues : function(with_hidden)
8101     {
8102         var items = this.getItems();
8103         var ret = {};
8104         items.each(function(f){
8105             
8106             if (!f.getName()) {
8107                 return;
8108             }
8109             
8110             var v = f.getValue();
8111             
8112             if (f.inputType =='radio') {
8113                 if (typeof(ret[f.getName()]) == 'undefined') {
8114                     ret[f.getName()] = ''; // empty..
8115                 }
8116
8117                 if (!f.el.dom.checked) {
8118                     return;
8119
8120                 }
8121                 v = f.el.dom.value;
8122
8123             }
8124             
8125             if(f.xtype == 'MoneyField'){
8126                 ret[f.currencyName] = f.getCurrency();
8127             }
8128
8129             // not sure if this supported any more..
8130             if ((typeof(v) == 'object') && f.getRawValue) {
8131                 v = f.getRawValue() ; // dates..
8132             }
8133             // combo boxes where name != hiddenName...
8134             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8135                 ret[f.name] = f.getRawValue();
8136             }
8137             ret[f.getName()] = v;
8138         });
8139
8140         return ret;
8141     },
8142
8143     /**
8144      * Clears all invalid messages in this form.
8145      * @return {BasicForm} this
8146      */
8147     clearInvalid : function(){
8148         var items = this.getItems();
8149
8150         items.each(function(f){
8151            f.clearInvalid();
8152         });
8153
8154         return this;
8155     },
8156
8157     /**
8158      * Resets this form.
8159      * @return {BasicForm} this
8160      */
8161     reset : function(){
8162         var items = this.getItems();
8163         items.each(function(f){
8164             f.reset();
8165         });
8166
8167         Roo.each(this.childForms || [], function (f) {
8168             f.reset();
8169         });
8170
8171
8172         return this;
8173     },
8174     
8175     getItems : function()
8176     {
8177         var r=new Roo.util.MixedCollection(false, function(o){
8178             return o.id || (o.id = Roo.id());
8179         });
8180         var iter = function(el) {
8181             if (el.inputEl) {
8182                 r.add(el);
8183             }
8184             if (!el.items) {
8185                 return;
8186             }
8187             Roo.each(el.items,function(e) {
8188                 iter(e);
8189             });
8190         };
8191
8192         iter(this);
8193         return r;
8194     },
8195     
8196     hideFields : function(items)
8197     {
8198         Roo.each(items, function(i){
8199             
8200             var f = this.findField(i);
8201             
8202             if(!f){
8203                 return;
8204             }
8205             
8206             if(f.xtype == 'DateField'){
8207                 f.setVisible(false);
8208                 return;
8209             }
8210             
8211             f.hide();
8212             
8213         }, this);
8214     },
8215     
8216     showFields : function(items)
8217     {
8218         Roo.each(items, function(i){
8219             
8220             var f = this.findField(i);
8221             
8222             if(!f){
8223                 return;
8224             }
8225             
8226             if(f.xtype == 'DateField'){
8227                 f.setVisible(true);
8228                 return;
8229             }
8230             
8231             f.show();
8232             
8233         }, this);
8234     }
8235
8236 });
8237
8238 Roo.apply(Roo.bootstrap.Form, {
8239     
8240     popover : {
8241         
8242         padding : 5,
8243         
8244         isApplied : false,
8245         
8246         isMasked : false,
8247         
8248         form : false,
8249         
8250         target : false,
8251         
8252         toolTip : false,
8253         
8254         intervalID : false,
8255         
8256         maskEl : false,
8257         
8258         apply : function()
8259         {
8260             if(this.isApplied){
8261                 return;
8262             }
8263             
8264             this.maskEl = {
8265                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8266                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8267                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8268                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8269             };
8270             
8271             this.maskEl.top.enableDisplayMode("block");
8272             this.maskEl.left.enableDisplayMode("block");
8273             this.maskEl.bottom.enableDisplayMode("block");
8274             this.maskEl.right.enableDisplayMode("block");
8275             
8276             this.toolTip = new Roo.bootstrap.Tooltip({
8277                 cls : 'roo-form-error-popover',
8278                 alignment : {
8279                     'left' : ['r-l', [-2,0], 'right'],
8280                     'right' : ['l-r', [2,0], 'left'],
8281                     'bottom' : ['tl-bl', [0,2], 'top'],
8282                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8283                 }
8284             });
8285             
8286             this.toolTip.render(Roo.get(document.body));
8287
8288             this.toolTip.el.enableDisplayMode("block");
8289             
8290             Roo.get(document.body).on('click', function(){
8291                 this.unmask();
8292             }, this);
8293             
8294             Roo.get(document.body).on('touchstart', function(){
8295                 this.unmask();
8296             }, this);
8297             
8298             this.isApplied = true
8299         },
8300         
8301         mask : function(form, target)
8302         {
8303             this.form = form;
8304             
8305             this.target = target;
8306             
8307             if(!this.form.errorMask || !target.el){
8308                 return;
8309             }
8310             
8311             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8312             
8313             Roo.log(scrollable);
8314             
8315             var ot = this.target.el.calcOffsetsTo(scrollable);
8316             
8317             var scrollTo = ot[1] - this.form.maskOffset;
8318             
8319             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8320             
8321             scrollable.scrollTo('top', scrollTo);
8322             
8323             var box = this.target.el.getBox();
8324             Roo.log(box);
8325             var zIndex = Roo.bootstrap.Modal.zIndex++;
8326
8327             
8328             this.maskEl.top.setStyle('position', 'absolute');
8329             this.maskEl.top.setStyle('z-index', zIndex);
8330             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8331             this.maskEl.top.setLeft(0);
8332             this.maskEl.top.setTop(0);
8333             this.maskEl.top.show();
8334             
8335             this.maskEl.left.setStyle('position', 'absolute');
8336             this.maskEl.left.setStyle('z-index', zIndex);
8337             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8338             this.maskEl.left.setLeft(0);
8339             this.maskEl.left.setTop(box.y - this.padding);
8340             this.maskEl.left.show();
8341
8342             this.maskEl.bottom.setStyle('position', 'absolute');
8343             this.maskEl.bottom.setStyle('z-index', zIndex);
8344             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8345             this.maskEl.bottom.setLeft(0);
8346             this.maskEl.bottom.setTop(box.bottom + this.padding);
8347             this.maskEl.bottom.show();
8348
8349             this.maskEl.right.setStyle('position', 'absolute');
8350             this.maskEl.right.setStyle('z-index', zIndex);
8351             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8352             this.maskEl.right.setLeft(box.right + this.padding);
8353             this.maskEl.right.setTop(box.y - this.padding);
8354             this.maskEl.right.show();
8355
8356             this.toolTip.bindEl = this.target.el;
8357
8358             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8359
8360             var tip = this.target.blankText;
8361
8362             if(this.target.getValue() !== '' ) {
8363                 
8364                 if (this.target.invalidText.length) {
8365                     tip = this.target.invalidText;
8366                 } else if (this.target.regexText.length){
8367                     tip = this.target.regexText;
8368                 }
8369             }
8370
8371             this.toolTip.show(tip);
8372
8373             this.intervalID = window.setInterval(function() {
8374                 Roo.bootstrap.Form.popover.unmask();
8375             }, 10000);
8376
8377             window.onwheel = function(){ return false;};
8378             
8379             (function(){ this.isMasked = true; }).defer(500, this);
8380             
8381         },
8382         
8383         unmask : function()
8384         {
8385             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8386                 return;
8387             }
8388             
8389             this.maskEl.top.setStyle('position', 'absolute');
8390             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8391             this.maskEl.top.hide();
8392
8393             this.maskEl.left.setStyle('position', 'absolute');
8394             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8395             this.maskEl.left.hide();
8396
8397             this.maskEl.bottom.setStyle('position', 'absolute');
8398             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8399             this.maskEl.bottom.hide();
8400
8401             this.maskEl.right.setStyle('position', 'absolute');
8402             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8403             this.maskEl.right.hide();
8404             
8405             this.toolTip.hide();
8406             
8407             this.toolTip.el.hide();
8408             
8409             window.onwheel = function(){ return true;};
8410             
8411             if(this.intervalID){
8412                 window.clearInterval(this.intervalID);
8413                 this.intervalID = false;
8414             }
8415             
8416             this.isMasked = false;
8417             
8418         }
8419         
8420     }
8421     
8422 });
8423
8424 /*
8425  * Based on:
8426  * Ext JS Library 1.1.1
8427  * Copyright(c) 2006-2007, Ext JS, LLC.
8428  *
8429  * Originally Released Under LGPL - original licence link has changed is not relivant.
8430  *
8431  * Fork - LGPL
8432  * <script type="text/javascript">
8433  */
8434 /**
8435  * @class Roo.form.VTypes
8436  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8437  * @singleton
8438  */
8439 Roo.form.VTypes = function(){
8440     // closure these in so they are only created once.
8441     var alpha = /^[a-zA-Z_]+$/;
8442     var alphanum = /^[a-zA-Z0-9_]+$/;
8443     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8444     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8445
8446     // All these messages and functions are configurable
8447     return {
8448         /**
8449          * The function used to validate email addresses
8450          * @param {String} value The email address
8451          */
8452         'email' : function(v){
8453             return email.test(v);
8454         },
8455         /**
8456          * The error text to display when the email validation function returns false
8457          * @type String
8458          */
8459         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8460         /**
8461          * The keystroke filter mask to be applied on email input
8462          * @type RegExp
8463          */
8464         'emailMask' : /[a-z0-9_\.\-@]/i,
8465
8466         /**
8467          * The function used to validate URLs
8468          * @param {String} value The URL
8469          */
8470         'url' : function(v){
8471             return url.test(v);
8472         },
8473         /**
8474          * The error text to display when the url validation function returns false
8475          * @type String
8476          */
8477         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8478         
8479         /**
8480          * The function used to validate alpha values
8481          * @param {String} value The value
8482          */
8483         'alpha' : function(v){
8484             return alpha.test(v);
8485         },
8486         /**
8487          * The error text to display when the alpha validation function returns false
8488          * @type String
8489          */
8490         'alphaText' : 'This field should only contain letters and _',
8491         /**
8492          * The keystroke filter mask to be applied on alpha input
8493          * @type RegExp
8494          */
8495         'alphaMask' : /[a-z_]/i,
8496
8497         /**
8498          * The function used to validate alphanumeric values
8499          * @param {String} value The value
8500          */
8501         'alphanum' : function(v){
8502             return alphanum.test(v);
8503         },
8504         /**
8505          * The error text to display when the alphanumeric validation function returns false
8506          * @type String
8507          */
8508         'alphanumText' : 'This field should only contain letters, numbers and _',
8509         /**
8510          * The keystroke filter mask to be applied on alphanumeric input
8511          * @type RegExp
8512          */
8513         'alphanumMask' : /[a-z0-9_]/i
8514     };
8515 }();/*
8516  * - LGPL
8517  *
8518  * Input
8519  * 
8520  */
8521
8522 /**
8523  * @class Roo.bootstrap.Input
8524  * @extends Roo.bootstrap.Component
8525  * Bootstrap Input class
8526  * @cfg {Boolean} disabled is it disabled
8527  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8528  * @cfg {String} name name of the input
8529  * @cfg {string} fieldLabel - the label associated
8530  * @cfg {string} placeholder - placeholder to put in text.
8531  * @cfg {string}  before - input group add on before
8532  * @cfg {string} after - input group add on after
8533  * @cfg {string} size - (lg|sm) or leave empty..
8534  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8535  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8536  * @cfg {Number} md colspan out of 12 for computer-sized screens
8537  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8538  * @cfg {string} value default value of the input
8539  * @cfg {Number} labelWidth set the width of label 
8540  * @cfg {Number} labellg set the width of label (1-12)
8541  * @cfg {Number} labelmd set the width of label (1-12)
8542  * @cfg {Number} labelsm set the width of label (1-12)
8543  * @cfg {Number} labelxs set the width of label (1-12)
8544  * @cfg {String} labelAlign (top|left)
8545  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8546  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8547  * @cfg {String} indicatorpos (left|right) default left
8548
8549  * @cfg {String} align (left|center|right) Default left
8550  * @cfg {Boolean} forceFeedback (true|false) Default false
8551  * 
8552  * @constructor
8553  * Create a new Input
8554  * @param {Object} config The config object
8555  */
8556
8557 Roo.bootstrap.Input = function(config){
8558     
8559     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8560     
8561     this.addEvents({
8562         /**
8563          * @event focus
8564          * Fires when this field receives input focus.
8565          * @param {Roo.form.Field} this
8566          */
8567         focus : true,
8568         /**
8569          * @event blur
8570          * Fires when this field loses input focus.
8571          * @param {Roo.form.Field} this
8572          */
8573         blur : true,
8574         /**
8575          * @event specialkey
8576          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8577          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8578          * @param {Roo.form.Field} this
8579          * @param {Roo.EventObject} e The event object
8580          */
8581         specialkey : true,
8582         /**
8583          * @event change
8584          * Fires just before the field blurs if the field value has changed.
8585          * @param {Roo.form.Field} this
8586          * @param {Mixed} newValue The new value
8587          * @param {Mixed} oldValue The original value
8588          */
8589         change : true,
8590         /**
8591          * @event invalid
8592          * Fires after the field has been marked as invalid.
8593          * @param {Roo.form.Field} this
8594          * @param {String} msg The validation message
8595          */
8596         invalid : true,
8597         /**
8598          * @event valid
8599          * Fires after the field has been validated with no errors.
8600          * @param {Roo.form.Field} this
8601          */
8602         valid : true,
8603          /**
8604          * @event keyup
8605          * Fires after the key up
8606          * @param {Roo.form.Field} this
8607          * @param {Roo.EventObject}  e The event Object
8608          */
8609         keyup : true
8610     });
8611 };
8612
8613 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8614      /**
8615      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8616       automatic validation (defaults to "keyup").
8617      */
8618     validationEvent : "keyup",
8619      /**
8620      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8621      */
8622     validateOnBlur : true,
8623     /**
8624      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8625      */
8626     validationDelay : 250,
8627      /**
8628      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8629      */
8630     focusClass : "x-form-focus",  // not needed???
8631     
8632        
8633     /**
8634      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8635      */
8636     invalidClass : "has-warning",
8637     
8638     /**
8639      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8640      */
8641     validClass : "has-success",
8642     
8643     /**
8644      * @cfg {Boolean} hasFeedback (true|false) default true
8645      */
8646     hasFeedback : true,
8647     
8648     /**
8649      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8650      */
8651     invalidFeedbackClass : "glyphicon-warning-sign",
8652     
8653     /**
8654      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8655      */
8656     validFeedbackClass : "glyphicon-ok",
8657     
8658     /**
8659      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8660      */
8661     selectOnFocus : false,
8662     
8663      /**
8664      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8665      */
8666     maskRe : null,
8667        /**
8668      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8669      */
8670     vtype : null,
8671     
8672       /**
8673      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8674      */
8675     disableKeyFilter : false,
8676     
8677        /**
8678      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8679      */
8680     disabled : false,
8681      /**
8682      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8683      */
8684     allowBlank : true,
8685     /**
8686      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8687      */
8688     blankText : "Please complete this mandatory field",
8689     
8690      /**
8691      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8692      */
8693     minLength : 0,
8694     /**
8695      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8696      */
8697     maxLength : Number.MAX_VALUE,
8698     /**
8699      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8700      */
8701     minLengthText : "The minimum length for this field is {0}",
8702     /**
8703      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8704      */
8705     maxLengthText : "The maximum length for this field is {0}",
8706   
8707     
8708     /**
8709      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8710      * If available, this function will be called only after the basic validators all return true, and will be passed the
8711      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8712      */
8713     validator : null,
8714     /**
8715      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8716      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8717      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8718      */
8719     regex : null,
8720     /**
8721      * @cfg {String} regexText -- Depricated - use Invalid Text
8722      */
8723     regexText : "",
8724     
8725     /**
8726      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8727      */
8728     invalidText : "",
8729     
8730     
8731     
8732     autocomplete: false,
8733     
8734     
8735     fieldLabel : '',
8736     inputType : 'text',
8737     
8738     name : false,
8739     placeholder: false,
8740     before : false,
8741     after : false,
8742     size : false,
8743     hasFocus : false,
8744     preventMark: false,
8745     isFormField : true,
8746     value : '',
8747     labelWidth : 2,
8748     labelAlign : false,
8749     readOnly : false,
8750     align : false,
8751     formatedValue : false,
8752     forceFeedback : false,
8753     
8754     indicatorpos : 'left',
8755     
8756     labellg : 0,
8757     labelmd : 0,
8758     labelsm : 0,
8759     labelxs : 0,
8760     
8761     parentLabelAlign : function()
8762     {
8763         var parent = this;
8764         while (parent.parent()) {
8765             parent = parent.parent();
8766             if (typeof(parent.labelAlign) !='undefined') {
8767                 return parent.labelAlign;
8768             }
8769         }
8770         return 'left';
8771         
8772     },
8773     
8774     getAutoCreate : function()
8775     {
8776         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8777         
8778         var id = Roo.id();
8779         
8780         var cfg = {};
8781         
8782         if(this.inputType != 'hidden'){
8783             cfg.cls = 'form-group' //input-group
8784         }
8785         
8786         var input =  {
8787             tag: 'input',
8788             id : id,
8789             type : this.inputType,
8790             value : this.value,
8791             cls : 'form-control',
8792             placeholder : this.placeholder || '',
8793             autocomplete : this.autocomplete || 'new-password'
8794         };
8795         
8796         if(this.align){
8797             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8798         }
8799         
8800         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8801             input.maxLength = this.maxLength;
8802         }
8803         
8804         if (this.disabled) {
8805             input.disabled=true;
8806         }
8807         
8808         if (this.readOnly) {
8809             input.readonly=true;
8810         }
8811         
8812         if (this.name) {
8813             input.name = this.name;
8814         }
8815         
8816         if (this.size) {
8817             input.cls += ' input-' + this.size;
8818         }
8819         
8820         var settings=this;
8821         ['xs','sm','md','lg'].map(function(size){
8822             if (settings[size]) {
8823                 cfg.cls += ' col-' + size + '-' + settings[size];
8824             }
8825         });
8826         
8827         var inputblock = input;
8828         
8829         var feedback = {
8830             tag: 'span',
8831             cls: 'glyphicon form-control-feedback'
8832         };
8833             
8834         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8835             
8836             inputblock = {
8837                 cls : 'has-feedback',
8838                 cn :  [
8839                     input,
8840                     feedback
8841                 ] 
8842             };  
8843         }
8844         
8845         if (this.before || this.after) {
8846             
8847             inputblock = {
8848                 cls : 'input-group',
8849                 cn :  [] 
8850             };
8851             
8852             if (this.before && typeof(this.before) == 'string') {
8853                 
8854                 inputblock.cn.push({
8855                     tag :'span',
8856                     cls : 'roo-input-before input-group-addon',
8857                     html : this.before
8858                 });
8859             }
8860             if (this.before && typeof(this.before) == 'object') {
8861                 this.before = Roo.factory(this.before);
8862                 
8863                 inputblock.cn.push({
8864                     tag :'span',
8865                     cls : 'roo-input-before input-group-' +
8866                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8867                 });
8868             }
8869             
8870             inputblock.cn.push(input);
8871             
8872             if (this.after && typeof(this.after) == 'string') {
8873                 inputblock.cn.push({
8874                     tag :'span',
8875                     cls : 'roo-input-after input-group-addon',
8876                     html : this.after
8877                 });
8878             }
8879             if (this.after && typeof(this.after) == 'object') {
8880                 this.after = Roo.factory(this.after);
8881                 
8882                 inputblock.cn.push({
8883                     tag :'span',
8884                     cls : 'roo-input-after input-group-' +
8885                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8886                 });
8887             }
8888             
8889             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8890                 inputblock.cls += ' has-feedback';
8891                 inputblock.cn.push(feedback);
8892             }
8893         };
8894         
8895         if (align ==='left' && this.fieldLabel.length) {
8896             
8897             cfg.cls += ' roo-form-group-label-left';
8898             
8899             cfg.cn = [
8900                 {
8901                     tag : 'i',
8902                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8903                     tooltip : 'This field is required'
8904                 },
8905                 {
8906                     tag: 'label',
8907                     'for' :  id,
8908                     cls : 'control-label',
8909                     html : this.fieldLabel
8910
8911                 },
8912                 {
8913                     cls : "", 
8914                     cn: [
8915                         inputblock
8916                     ]
8917                 }
8918             ];
8919             
8920             var labelCfg = cfg.cn[1];
8921             var contentCfg = cfg.cn[2];
8922             
8923             if(this.indicatorpos == 'right'){
8924                 cfg.cn = [
8925                     {
8926                         tag: 'label',
8927                         'for' :  id,
8928                         cls : 'control-label',
8929                         cn : [
8930                             {
8931                                 tag : 'span',
8932                                 html : this.fieldLabel
8933                             },
8934                             {
8935                                 tag : 'i',
8936                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8937                                 tooltip : 'This field is required'
8938                             }
8939                         ]
8940                     },
8941                     {
8942                         cls : "",
8943                         cn: [
8944                             inputblock
8945                         ]
8946                     }
8947
8948                 ];
8949                 
8950                 labelCfg = cfg.cn[0];
8951                 contentCfg = cfg.cn[1];
8952             
8953             }
8954             
8955             if(this.labelWidth > 12){
8956                 labelCfg.style = "width: " + this.labelWidth + 'px';
8957             }
8958             
8959             if(this.labelWidth < 13 && this.labelmd == 0){
8960                 this.labelmd = this.labelWidth;
8961             }
8962             
8963             if(this.labellg > 0){
8964                 labelCfg.cls += ' col-lg-' + this.labellg;
8965                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8966             }
8967             
8968             if(this.labelmd > 0){
8969                 labelCfg.cls += ' col-md-' + this.labelmd;
8970                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8971             }
8972             
8973             if(this.labelsm > 0){
8974                 labelCfg.cls += ' col-sm-' + this.labelsm;
8975                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8976             }
8977             
8978             if(this.labelxs > 0){
8979                 labelCfg.cls += ' col-xs-' + this.labelxs;
8980                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8981             }
8982             
8983             
8984         } else if ( this.fieldLabel.length) {
8985                 
8986             cfg.cn = [
8987                 {
8988                     tag : 'i',
8989                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8990                     tooltip : 'This field is required'
8991                 },
8992                 {
8993                     tag: 'label',
8994                    //cls : 'input-group-addon',
8995                     html : this.fieldLabel
8996
8997                 },
8998
8999                inputblock
9000
9001            ];
9002            
9003            if(this.indicatorpos == 'right'){
9004                 
9005                 cfg.cn = [
9006                     {
9007                         tag: 'label',
9008                        //cls : 'input-group-addon',
9009                         html : this.fieldLabel
9010
9011                     },
9012                     {
9013                         tag : 'i',
9014                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9015                         tooltip : 'This field is required'
9016                     },
9017
9018                    inputblock
9019
9020                ];
9021
9022             }
9023
9024         } else {
9025             
9026             cfg.cn = [
9027
9028                     inputblock
9029
9030             ];
9031                 
9032                 
9033         };
9034         
9035         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9036            cfg.cls += ' navbar-form';
9037         }
9038         
9039         if (this.parentType === 'NavGroup') {
9040            cfg.cls += ' navbar-form';
9041            cfg.tag = 'li';
9042         }
9043         
9044         return cfg;
9045         
9046     },
9047     /**
9048      * return the real input element.
9049      */
9050     inputEl: function ()
9051     {
9052         return this.el.select('input.form-control',true).first();
9053     },
9054     
9055     tooltipEl : function()
9056     {
9057         return this.inputEl();
9058     },
9059     
9060     indicatorEl : function()
9061     {
9062         var indicator = this.el.select('i.roo-required-indicator',true).first();
9063         
9064         if(!indicator){
9065             return false;
9066         }
9067         
9068         return indicator;
9069         
9070     },
9071     
9072     setDisabled : function(v)
9073     {
9074         var i  = this.inputEl().dom;
9075         if (!v) {
9076             i.removeAttribute('disabled');
9077             return;
9078             
9079         }
9080         i.setAttribute('disabled','true');
9081     },
9082     initEvents : function()
9083     {
9084           
9085         this.inputEl().on("keydown" , this.fireKey,  this);
9086         this.inputEl().on("focus", this.onFocus,  this);
9087         this.inputEl().on("blur", this.onBlur,  this);
9088         
9089         this.inputEl().relayEvent('keyup', this);
9090         
9091         this.indicator = this.indicatorEl();
9092         
9093         if(this.indicator){
9094             this.indicator.addClass('invisible');
9095         }
9096  
9097         // reference to original value for reset
9098         this.originalValue = this.getValue();
9099         //Roo.form.TextField.superclass.initEvents.call(this);
9100         if(this.validationEvent == 'keyup'){
9101             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9102             this.inputEl().on('keyup', this.filterValidation, this);
9103         }
9104         else if(this.validationEvent !== false){
9105             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9106         }
9107         
9108         if(this.selectOnFocus){
9109             this.on("focus", this.preFocus, this);
9110             
9111         }
9112         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9113             this.inputEl().on("keypress", this.filterKeys, this);
9114         } else {
9115             this.inputEl().relayEvent('keypress', this);
9116         }
9117        /* if(this.grow){
9118             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9119             this.el.on("click", this.autoSize,  this);
9120         }
9121         */
9122         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9123             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9124         }
9125         
9126         if (typeof(this.before) == 'object') {
9127             this.before.render(this.el.select('.roo-input-before',true).first());
9128         }
9129         if (typeof(this.after) == 'object') {
9130             this.after.render(this.el.select('.roo-input-after',true).first());
9131         }
9132         
9133         
9134     },
9135     filterValidation : function(e){
9136         if(!e.isNavKeyPress()){
9137             this.validationTask.delay(this.validationDelay);
9138         }
9139     },
9140      /**
9141      * Validates the field value
9142      * @return {Boolean} True if the value is valid, else false
9143      */
9144     validate : function(){
9145         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9146         if(this.disabled || this.validateValue(this.getRawValue())){
9147             this.markValid();
9148             return true;
9149         }
9150         
9151         this.markInvalid();
9152         return false;
9153     },
9154     
9155     
9156     /**
9157      * Validates a value according to the field's validation rules and marks the field as invalid
9158      * if the validation fails
9159      * @param {Mixed} value The value to validate
9160      * @return {Boolean} True if the value is valid, else false
9161      */
9162     validateValue : function(value)
9163     {
9164         if(this.getVisibilityEl().hasClass('hidden')){
9165             return true;
9166         }
9167         
9168         if(value.length < 1)  { // if it's blank
9169             if(this.allowBlank){
9170                 return true;
9171             }
9172             return false;
9173         }
9174         
9175         if(value.length < this.minLength){
9176             return false;
9177         }
9178         if(value.length > this.maxLength){
9179             return false;
9180         }
9181         if(this.vtype){
9182             var vt = Roo.form.VTypes;
9183             if(!vt[this.vtype](value, this)){
9184                 return false;
9185             }
9186         }
9187         if(typeof this.validator == "function"){
9188             var msg = this.validator(value);
9189             if(msg !== true){
9190                 return false;
9191             }
9192             if (typeof(msg) == 'string') {
9193                 this.invalidText = msg;
9194             }
9195         }
9196         
9197         if(this.regex && !this.regex.test(value)){
9198             return false;
9199         }
9200         
9201         return true;
9202     },
9203     
9204      // private
9205     fireKey : function(e){
9206         //Roo.log('field ' + e.getKey());
9207         if(e.isNavKeyPress()){
9208             this.fireEvent("specialkey", this, e);
9209         }
9210     },
9211     focus : function (selectText){
9212         if(this.rendered){
9213             this.inputEl().focus();
9214             if(selectText === true){
9215                 this.inputEl().dom.select();
9216             }
9217         }
9218         return this;
9219     } ,
9220     
9221     onFocus : function(){
9222         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9223            // this.el.addClass(this.focusClass);
9224         }
9225         if(!this.hasFocus){
9226             this.hasFocus = true;
9227             this.startValue = this.getValue();
9228             this.fireEvent("focus", this);
9229         }
9230     },
9231     
9232     beforeBlur : Roo.emptyFn,
9233
9234     
9235     // private
9236     onBlur : function(){
9237         this.beforeBlur();
9238         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9239             //this.el.removeClass(this.focusClass);
9240         }
9241         this.hasFocus = false;
9242         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9243             this.validate();
9244         }
9245         var v = this.getValue();
9246         if(String(v) !== String(this.startValue)){
9247             this.fireEvent('change', this, v, this.startValue);
9248         }
9249         this.fireEvent("blur", this);
9250     },
9251     
9252     /**
9253      * Resets the current field value to the originally loaded value and clears any validation messages
9254      */
9255     reset : function(){
9256         this.setValue(this.originalValue);
9257         this.validate();
9258     },
9259      /**
9260      * Returns the name of the field
9261      * @return {Mixed} name The name field
9262      */
9263     getName: function(){
9264         return this.name;
9265     },
9266      /**
9267      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9268      * @return {Mixed} value The field value
9269      */
9270     getValue : function(){
9271         
9272         var v = this.inputEl().getValue();
9273         
9274         return v;
9275     },
9276     /**
9277      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9278      * @return {Mixed} value The field value
9279      */
9280     getRawValue : function(){
9281         var v = this.inputEl().getValue();
9282         
9283         return v;
9284     },
9285     
9286     /**
9287      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9288      * @param {Mixed} value The value to set
9289      */
9290     setRawValue : function(v){
9291         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9292     },
9293     
9294     selectText : function(start, end){
9295         var v = this.getRawValue();
9296         if(v.length > 0){
9297             start = start === undefined ? 0 : start;
9298             end = end === undefined ? v.length : end;
9299             var d = this.inputEl().dom;
9300             if(d.setSelectionRange){
9301                 d.setSelectionRange(start, end);
9302             }else if(d.createTextRange){
9303                 var range = d.createTextRange();
9304                 range.moveStart("character", start);
9305                 range.moveEnd("character", v.length-end);
9306                 range.select();
9307             }
9308         }
9309     },
9310     
9311     /**
9312      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9313      * @param {Mixed} value The value to set
9314      */
9315     setValue : function(v){
9316         this.value = v;
9317         if(this.rendered){
9318             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9319             this.validate();
9320         }
9321     },
9322     
9323     /*
9324     processValue : function(value){
9325         if(this.stripCharsRe){
9326             var newValue = value.replace(this.stripCharsRe, '');
9327             if(newValue !== value){
9328                 this.setRawValue(newValue);
9329                 return newValue;
9330             }
9331         }
9332         return value;
9333     },
9334   */
9335     preFocus : function(){
9336         
9337         if(this.selectOnFocus){
9338             this.inputEl().dom.select();
9339         }
9340     },
9341     filterKeys : function(e){
9342         var k = e.getKey();
9343         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9344             return;
9345         }
9346         var c = e.getCharCode(), cc = String.fromCharCode(c);
9347         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9348             return;
9349         }
9350         if(!this.maskRe.test(cc)){
9351             e.stopEvent();
9352         }
9353     },
9354      /**
9355      * Clear any invalid styles/messages for this field
9356      */
9357     clearInvalid : function(){
9358         
9359         if(!this.el || this.preventMark){ // not rendered
9360             return;
9361         }
9362         
9363      
9364         this.el.removeClass(this.invalidClass);
9365         
9366         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9367             
9368             var feedback = this.el.select('.form-control-feedback', true).first();
9369             
9370             if(feedback){
9371                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9372             }
9373             
9374         }
9375         
9376         this.fireEvent('valid', this);
9377     },
9378     
9379      /**
9380      * Mark this field as valid
9381      */
9382     markValid : function()
9383     {
9384         if(!this.el  || this.preventMark){ // not rendered...
9385             return;
9386         }
9387         
9388         this.el.removeClass([this.invalidClass, this.validClass]);
9389         
9390         var feedback = this.el.select('.form-control-feedback', true).first();
9391             
9392         if(feedback){
9393             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9394         }
9395         
9396         if(this.indicator){
9397             this.indicator.removeClass('visible');
9398             this.indicator.addClass('invisible');
9399         }
9400         
9401         if(this.disabled){
9402             return;
9403         }
9404         
9405         if(this.allowBlank && !this.getRawValue().length){
9406             return;
9407         }
9408         
9409         this.el.addClass(this.validClass);
9410         
9411         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9412             
9413             var feedback = this.el.select('.form-control-feedback', true).first();
9414             
9415             if(feedback){
9416                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9417                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9418             }
9419             
9420         }
9421         
9422         this.fireEvent('valid', this);
9423     },
9424     
9425      /**
9426      * Mark this field as invalid
9427      * @param {String} msg The validation message
9428      */
9429     markInvalid : function(msg)
9430     {
9431         if(!this.el  || this.preventMark){ // not rendered
9432             return;
9433         }
9434         
9435         this.el.removeClass([this.invalidClass, this.validClass]);
9436         
9437         var feedback = this.el.select('.form-control-feedback', true).first();
9438             
9439         if(feedback){
9440             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9441         }
9442
9443         if(this.disabled){
9444             return;
9445         }
9446         
9447         if(this.allowBlank && !this.getRawValue().length){
9448             return;
9449         }
9450         
9451         if(this.indicator){
9452             this.indicator.removeClass('invisible');
9453             this.indicator.addClass('visible');
9454         }
9455         
9456         this.el.addClass(this.invalidClass);
9457         
9458         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9459             
9460             var feedback = this.el.select('.form-control-feedback', true).first();
9461             
9462             if(feedback){
9463                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9464                 
9465                 if(this.getValue().length || this.forceFeedback){
9466                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9467                 }
9468                 
9469             }
9470             
9471         }
9472         
9473         this.fireEvent('invalid', this, msg);
9474     },
9475     // private
9476     SafariOnKeyDown : function(event)
9477     {
9478         // this is a workaround for a password hang bug on chrome/ webkit.
9479         if (this.inputEl().dom.type != 'password') {
9480             return;
9481         }
9482         
9483         var isSelectAll = false;
9484         
9485         if(this.inputEl().dom.selectionEnd > 0){
9486             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9487         }
9488         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9489             event.preventDefault();
9490             this.setValue('');
9491             return;
9492         }
9493         
9494         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9495             
9496             event.preventDefault();
9497             // this is very hacky as keydown always get's upper case.
9498             //
9499             var cc = String.fromCharCode(event.getCharCode());
9500             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9501             
9502         }
9503     },
9504     adjustWidth : function(tag, w){
9505         tag = tag.toLowerCase();
9506         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9507             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9508                 if(tag == 'input'){
9509                     return w + 2;
9510                 }
9511                 if(tag == 'textarea'){
9512                     return w-2;
9513                 }
9514             }else if(Roo.isOpera){
9515                 if(tag == 'input'){
9516                     return w + 2;
9517                 }
9518                 if(tag == 'textarea'){
9519                     return w-2;
9520                 }
9521             }
9522         }
9523         return w;
9524     },
9525     
9526     setFieldLabel : function(v)
9527     {
9528         if(!this.rendered){
9529             return;
9530         }
9531         
9532         if(this.indicator){
9533             var ar = this.el.select('label > span',true);
9534             
9535             if (ar.elements.length) {
9536                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9537                 this.fieldLabel = v;
9538                 return;
9539             }
9540             
9541             var br = this.el.select('label',true);
9542             
9543             if(br.elements.length) {
9544                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9545                 this.fieldLabel = v;
9546                 return;
9547             }
9548             
9549             Roo.log('Cannot Found any of label > span || label in input');
9550             return;
9551         }
9552         
9553         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9554         this.fieldLabel = v;
9555         
9556         
9557     }
9558 });
9559
9560  
9561 /*
9562  * - LGPL
9563  *
9564  * Input
9565  * 
9566  */
9567
9568 /**
9569  * @class Roo.bootstrap.TextArea
9570  * @extends Roo.bootstrap.Input
9571  * Bootstrap TextArea class
9572  * @cfg {Number} cols Specifies the visible width of a text area
9573  * @cfg {Number} rows Specifies the visible number of lines in a text area
9574  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9575  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9576  * @cfg {string} html text
9577  * 
9578  * @constructor
9579  * Create a new TextArea
9580  * @param {Object} config The config object
9581  */
9582
9583 Roo.bootstrap.TextArea = function(config){
9584     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9585    
9586 };
9587
9588 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9589      
9590     cols : false,
9591     rows : 5,
9592     readOnly : false,
9593     warp : 'soft',
9594     resize : false,
9595     value: false,
9596     html: false,
9597     
9598     getAutoCreate : function(){
9599         
9600         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9601         
9602         var id = Roo.id();
9603         
9604         var cfg = {};
9605         
9606         if(this.inputType != 'hidden'){
9607             cfg.cls = 'form-group' //input-group
9608         }
9609         
9610         var input =  {
9611             tag: 'textarea',
9612             id : id,
9613             warp : this.warp,
9614             rows : this.rows,
9615             value : this.value || '',
9616             html: this.html || '',
9617             cls : 'form-control',
9618             placeholder : this.placeholder || '' 
9619             
9620         };
9621         
9622         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9623             input.maxLength = this.maxLength;
9624         }
9625         
9626         if(this.resize){
9627             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9628         }
9629         
9630         if(this.cols){
9631             input.cols = this.cols;
9632         }
9633         
9634         if (this.readOnly) {
9635             input.readonly = true;
9636         }
9637         
9638         if (this.name) {
9639             input.name = this.name;
9640         }
9641         
9642         if (this.size) {
9643             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9644         }
9645         
9646         var settings=this;
9647         ['xs','sm','md','lg'].map(function(size){
9648             if (settings[size]) {
9649                 cfg.cls += ' col-' + size + '-' + settings[size];
9650             }
9651         });
9652         
9653         var inputblock = input;
9654         
9655         if(this.hasFeedback && !this.allowBlank){
9656             
9657             var feedback = {
9658                 tag: 'span',
9659                 cls: 'glyphicon form-control-feedback'
9660             };
9661
9662             inputblock = {
9663                 cls : 'has-feedback',
9664                 cn :  [
9665                     input,
9666                     feedback
9667                 ] 
9668             };  
9669         }
9670         
9671         
9672         if (this.before || this.after) {
9673             
9674             inputblock = {
9675                 cls : 'input-group',
9676                 cn :  [] 
9677             };
9678             if (this.before) {
9679                 inputblock.cn.push({
9680                     tag :'span',
9681                     cls : 'input-group-addon',
9682                     html : this.before
9683                 });
9684             }
9685             
9686             inputblock.cn.push(input);
9687             
9688             if(this.hasFeedback && !this.allowBlank){
9689                 inputblock.cls += ' has-feedback';
9690                 inputblock.cn.push(feedback);
9691             }
9692             
9693             if (this.after) {
9694                 inputblock.cn.push({
9695                     tag :'span',
9696                     cls : 'input-group-addon',
9697                     html : this.after
9698                 });
9699             }
9700             
9701         }
9702         
9703         if (align ==='left' && this.fieldLabel.length) {
9704             cfg.cn = [
9705                 {
9706                     tag: 'label',
9707                     'for' :  id,
9708                     cls : 'control-label',
9709                     html : this.fieldLabel
9710                 },
9711                 {
9712                     cls : "",
9713                     cn: [
9714                         inputblock
9715                     ]
9716                 }
9717
9718             ];
9719             
9720             if(this.labelWidth > 12){
9721                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9722             }
9723
9724             if(this.labelWidth < 13 && this.labelmd == 0){
9725                 this.labelmd = this.labelWidth;
9726             }
9727
9728             if(this.labellg > 0){
9729                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9730                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9731             }
9732
9733             if(this.labelmd > 0){
9734                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9735                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9736             }
9737
9738             if(this.labelsm > 0){
9739                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9740                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9741             }
9742
9743             if(this.labelxs > 0){
9744                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9745                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9746             }
9747             
9748         } else if ( this.fieldLabel.length) {
9749             cfg.cn = [
9750
9751                {
9752                    tag: 'label',
9753                    //cls : 'input-group-addon',
9754                    html : this.fieldLabel
9755
9756                },
9757
9758                inputblock
9759
9760            ];
9761
9762         } else {
9763
9764             cfg.cn = [
9765
9766                 inputblock
9767
9768             ];
9769                 
9770         }
9771         
9772         if (this.disabled) {
9773             input.disabled=true;
9774         }
9775         
9776         return cfg;
9777         
9778     },
9779     /**
9780      * return the real textarea element.
9781      */
9782     inputEl: function ()
9783     {
9784         return this.el.select('textarea.form-control',true).first();
9785     },
9786     
9787     /**
9788      * Clear any invalid styles/messages for this field
9789      */
9790     clearInvalid : function()
9791     {
9792         
9793         if(!this.el || this.preventMark){ // not rendered
9794             return;
9795         }
9796         
9797         var label = this.el.select('label', true).first();
9798         var icon = this.el.select('i.fa-star', true).first();
9799         
9800         if(label && icon){
9801             icon.remove();
9802         }
9803         
9804         this.el.removeClass(this.invalidClass);
9805         
9806         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9807             
9808             var feedback = this.el.select('.form-control-feedback', true).first();
9809             
9810             if(feedback){
9811                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9812             }
9813             
9814         }
9815         
9816         this.fireEvent('valid', this);
9817     },
9818     
9819      /**
9820      * Mark this field as valid
9821      */
9822     markValid : function()
9823     {
9824         if(!this.el  || this.preventMark){ // not rendered
9825             return;
9826         }
9827         
9828         this.el.removeClass([this.invalidClass, this.validClass]);
9829         
9830         var feedback = this.el.select('.form-control-feedback', true).first();
9831             
9832         if(feedback){
9833             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9834         }
9835
9836         if(this.disabled || this.allowBlank){
9837             return;
9838         }
9839         
9840         var label = this.el.select('label', true).first();
9841         var icon = this.el.select('i.fa-star', true).first();
9842         
9843         if(label && icon){
9844             icon.remove();
9845         }
9846         
9847         this.el.addClass(this.validClass);
9848         
9849         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
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                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9856             }
9857             
9858         }
9859         
9860         this.fireEvent('valid', this);
9861     },
9862     
9863      /**
9864      * Mark this field as invalid
9865      * @param {String} msg The validation message
9866      */
9867     markInvalid : function(msg)
9868     {
9869         if(!this.el  || this.preventMark){ // not rendered
9870             return;
9871         }
9872         
9873         this.el.removeClass([this.invalidClass, this.validClass]);
9874         
9875         var feedback = this.el.select('.form-control-feedback', true).first();
9876             
9877         if(feedback){
9878             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9879         }
9880
9881         if(this.disabled || this.allowBlank){
9882             return;
9883         }
9884         
9885         var label = this.el.select('label', true).first();
9886         var icon = this.el.select('i.fa-star', true).first();
9887         
9888         if(!this.getValue().length && label && !icon){
9889             this.el.createChild({
9890                 tag : 'i',
9891                 cls : 'text-danger fa fa-lg fa-star',
9892                 tooltip : 'This field is required',
9893                 style : 'margin-right:5px;'
9894             }, label, true);
9895         }
9896
9897         this.el.addClass(this.invalidClass);
9898         
9899         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9900             
9901             var feedback = this.el.select('.form-control-feedback', true).first();
9902             
9903             if(feedback){
9904                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9905                 
9906                 if(this.getValue().length || this.forceFeedback){
9907                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9908                 }
9909                 
9910             }
9911             
9912         }
9913         
9914         this.fireEvent('invalid', this, msg);
9915     }
9916 });
9917
9918  
9919 /*
9920  * - LGPL
9921  *
9922  * trigger field - base class for combo..
9923  * 
9924  */
9925  
9926 /**
9927  * @class Roo.bootstrap.TriggerField
9928  * @extends Roo.bootstrap.Input
9929  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9930  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9931  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9932  * for which you can provide a custom implementation.  For example:
9933  * <pre><code>
9934 var trigger = new Roo.bootstrap.TriggerField();
9935 trigger.onTriggerClick = myTriggerFn;
9936 trigger.applyTo('my-field');
9937 </code></pre>
9938  *
9939  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9940  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9941  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9942  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9943  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9944
9945  * @constructor
9946  * Create a new TriggerField.
9947  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9948  * to the base TextField)
9949  */
9950 Roo.bootstrap.TriggerField = function(config){
9951     this.mimicing = false;
9952     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9953 };
9954
9955 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9956     /**
9957      * @cfg {String} triggerClass A CSS class to apply to the trigger
9958      */
9959      /**
9960      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9961      */
9962     hideTrigger:false,
9963
9964     /**
9965      * @cfg {Boolean} removable (true|false) special filter default false
9966      */
9967     removable : false,
9968     
9969     /** @cfg {Boolean} grow @hide */
9970     /** @cfg {Number} growMin @hide */
9971     /** @cfg {Number} growMax @hide */
9972
9973     /**
9974      * @hide 
9975      * @method
9976      */
9977     autoSize: Roo.emptyFn,
9978     // private
9979     monitorTab : true,
9980     // private
9981     deferHeight : true,
9982
9983     
9984     actionMode : 'wrap',
9985     
9986     caret : false,
9987     
9988     
9989     getAutoCreate : function(){
9990        
9991         var align = this.labelAlign || this.parentLabelAlign();
9992         
9993         var id = Roo.id();
9994         
9995         var cfg = {
9996             cls: 'form-group' //input-group
9997         };
9998         
9999         
10000         var input =  {
10001             tag: 'input',
10002             id : id,
10003             type : this.inputType,
10004             cls : 'form-control',
10005             autocomplete: 'new-password',
10006             placeholder : this.placeholder || '' 
10007             
10008         };
10009         if (this.name) {
10010             input.name = this.name;
10011         }
10012         if (this.size) {
10013             input.cls += ' input-' + this.size;
10014         }
10015         
10016         if (this.disabled) {
10017             input.disabled=true;
10018         }
10019         
10020         var inputblock = input;
10021         
10022         if(this.hasFeedback && !this.allowBlank){
10023             
10024             var feedback = {
10025                 tag: 'span',
10026                 cls: 'glyphicon form-control-feedback'
10027             };
10028             
10029             if(this.removable && !this.editable && !this.tickable){
10030                 inputblock = {
10031                     cls : 'has-feedback',
10032                     cn :  [
10033                         inputblock,
10034                         {
10035                             tag: 'button',
10036                             html : 'x',
10037                             cls : 'roo-combo-removable-btn close'
10038                         },
10039                         feedback
10040                     ] 
10041                 };
10042             } else {
10043                 inputblock = {
10044                     cls : 'has-feedback',
10045                     cn :  [
10046                         inputblock,
10047                         feedback
10048                     ] 
10049                 };
10050             }
10051
10052         } else {
10053             if(this.removable && !this.editable && !this.tickable){
10054                 inputblock = {
10055                     cls : 'roo-removable',
10056                     cn :  [
10057                         inputblock,
10058                         {
10059                             tag: 'button',
10060                             html : 'x',
10061                             cls : 'roo-combo-removable-btn close'
10062                         }
10063                     ] 
10064                 };
10065             }
10066         }
10067         
10068         if (this.before || this.after) {
10069             
10070             inputblock = {
10071                 cls : 'input-group',
10072                 cn :  [] 
10073             };
10074             if (this.before) {
10075                 inputblock.cn.push({
10076                     tag :'span',
10077                     cls : 'input-group-addon',
10078                     html : this.before
10079                 });
10080             }
10081             
10082             inputblock.cn.push(input);
10083             
10084             if(this.hasFeedback && !this.allowBlank){
10085                 inputblock.cls += ' has-feedback';
10086                 inputblock.cn.push(feedback);
10087             }
10088             
10089             if (this.after) {
10090                 inputblock.cn.push({
10091                     tag :'span',
10092                     cls : 'input-group-addon',
10093                     html : this.after
10094                 });
10095             }
10096             
10097         };
10098         
10099         var box = {
10100             tag: 'div',
10101             cn: [
10102                 {
10103                     tag: 'input',
10104                     type : 'hidden',
10105                     cls: 'form-hidden-field'
10106                 },
10107                 inputblock
10108             ]
10109             
10110         };
10111         
10112         if(this.multiple){
10113             box = {
10114                 tag: 'div',
10115                 cn: [
10116                     {
10117                         tag: 'input',
10118                         type : 'hidden',
10119                         cls: 'form-hidden-field'
10120                     },
10121                     {
10122                         tag: 'ul',
10123                         cls: 'roo-select2-choices',
10124                         cn:[
10125                             {
10126                                 tag: 'li',
10127                                 cls: 'roo-select2-search-field',
10128                                 cn: [
10129
10130                                     inputblock
10131                                 ]
10132                             }
10133                         ]
10134                     }
10135                 ]
10136             }
10137         };
10138         
10139         var combobox = {
10140             cls: 'roo-select2-container input-group',
10141             cn: [
10142                 box
10143 //                {
10144 //                    tag: 'ul',
10145 //                    cls: 'typeahead typeahead-long dropdown-menu',
10146 //                    style: 'display:none'
10147 //                }
10148             ]
10149         };
10150         
10151         if(!this.multiple && this.showToggleBtn){
10152             
10153             var caret = {
10154                         tag: 'span',
10155                         cls: 'caret'
10156              };
10157             if (this.caret != false) {
10158                 caret = {
10159                      tag: 'i',
10160                      cls: 'fa fa-' + this.caret
10161                 };
10162                 
10163             }
10164             
10165             combobox.cn.push({
10166                 tag :'span',
10167                 cls : 'input-group-addon btn dropdown-toggle',
10168                 cn : [
10169                     caret,
10170                     {
10171                         tag: 'span',
10172                         cls: 'combobox-clear',
10173                         cn  : [
10174                             {
10175                                 tag : 'i',
10176                                 cls: 'icon-remove'
10177                             }
10178                         ]
10179                     }
10180                 ]
10181
10182             })
10183         }
10184         
10185         if(this.multiple){
10186             combobox.cls += ' roo-select2-container-multi';
10187         }
10188         
10189         if (align ==='left' && this.fieldLabel.length) {
10190             
10191             cfg.cls += ' roo-form-group-label-left';
10192
10193             cfg.cn = [
10194                 {
10195                     tag : 'i',
10196                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10197                     tooltip : 'This field is required'
10198                 },
10199                 {
10200                     tag: 'label',
10201                     'for' :  id,
10202                     cls : 'control-label',
10203                     html : this.fieldLabel
10204
10205                 },
10206                 {
10207                     cls : "", 
10208                     cn: [
10209                         combobox
10210                     ]
10211                 }
10212
10213             ];
10214             
10215             var labelCfg = cfg.cn[1];
10216             var contentCfg = cfg.cn[2];
10217             
10218             if(this.indicatorpos == 'right'){
10219                 cfg.cn = [
10220                     {
10221                         tag: 'label',
10222                         'for' :  id,
10223                         cls : 'control-label',
10224                         cn : [
10225                             {
10226                                 tag : 'span',
10227                                 html : this.fieldLabel
10228                             },
10229                             {
10230                                 tag : 'i',
10231                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10232                                 tooltip : 'This field is required'
10233                             }
10234                         ]
10235                     },
10236                     {
10237                         cls : "", 
10238                         cn: [
10239                             combobox
10240                         ]
10241                     }
10242
10243                 ];
10244                 
10245                 labelCfg = cfg.cn[0];
10246                 contentCfg = cfg.cn[1];
10247             }
10248             
10249             if(this.labelWidth > 12){
10250                 labelCfg.style = "width: " + this.labelWidth + 'px';
10251             }
10252             
10253             if(this.labelWidth < 13 && this.labelmd == 0){
10254                 this.labelmd = this.labelWidth;
10255             }
10256             
10257             if(this.labellg > 0){
10258                 labelCfg.cls += ' col-lg-' + this.labellg;
10259                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10260             }
10261             
10262             if(this.labelmd > 0){
10263                 labelCfg.cls += ' col-md-' + this.labelmd;
10264                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10265             }
10266             
10267             if(this.labelsm > 0){
10268                 labelCfg.cls += ' col-sm-' + this.labelsm;
10269                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10270             }
10271             
10272             if(this.labelxs > 0){
10273                 labelCfg.cls += ' col-xs-' + this.labelxs;
10274                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10275             }
10276             
10277         } else if ( this.fieldLabel.length) {
10278 //                Roo.log(" label");
10279             cfg.cn = [
10280                 {
10281                    tag : 'i',
10282                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10283                    tooltip : 'This field is required'
10284                },
10285                {
10286                    tag: 'label',
10287                    //cls : 'input-group-addon',
10288                    html : this.fieldLabel
10289
10290                },
10291
10292                combobox
10293
10294             ];
10295             
10296             if(this.indicatorpos == 'right'){
10297                 
10298                 cfg.cn = [
10299                     {
10300                        tag: 'label',
10301                        cn : [
10302                            {
10303                                tag : 'span',
10304                                html : this.fieldLabel
10305                            },
10306                            {
10307                               tag : 'i',
10308                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10309                               tooltip : 'This field is required'
10310                            }
10311                        ]
10312
10313                     },
10314                     combobox
10315
10316                 ];
10317
10318             }
10319
10320         } else {
10321             
10322 //                Roo.log(" no label && no align");
10323                 cfg = combobox
10324                      
10325                 
10326         }
10327         
10328         var settings=this;
10329         ['xs','sm','md','lg'].map(function(size){
10330             if (settings[size]) {
10331                 cfg.cls += ' col-' + size + '-' + settings[size];
10332             }
10333         });
10334         
10335         return cfg;
10336         
10337     },
10338     
10339     
10340     
10341     // private
10342     onResize : function(w, h){
10343 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10344 //        if(typeof w == 'number'){
10345 //            var x = w - this.trigger.getWidth();
10346 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10347 //            this.trigger.setStyle('left', x+'px');
10348 //        }
10349     },
10350
10351     // private
10352     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10353
10354     // private
10355     getResizeEl : function(){
10356         return this.inputEl();
10357     },
10358
10359     // private
10360     getPositionEl : function(){
10361         return this.inputEl();
10362     },
10363
10364     // private
10365     alignErrorIcon : function(){
10366         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10367     },
10368
10369     // private
10370     initEvents : function(){
10371         
10372         this.createList();
10373         
10374         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10375         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10376         if(!this.multiple && this.showToggleBtn){
10377             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10378             if(this.hideTrigger){
10379                 this.trigger.setDisplayed(false);
10380             }
10381             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10382         }
10383         
10384         if(this.multiple){
10385             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10386         }
10387         
10388         if(this.removable && !this.editable && !this.tickable){
10389             var close = this.closeTriggerEl();
10390             
10391             if(close){
10392                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10393                 close.on('click', this.removeBtnClick, this, close);
10394             }
10395         }
10396         
10397         //this.trigger.addClassOnOver('x-form-trigger-over');
10398         //this.trigger.addClassOnClick('x-form-trigger-click');
10399         
10400         //if(!this.width){
10401         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10402         //}
10403     },
10404     
10405     closeTriggerEl : function()
10406     {
10407         var close = this.el.select('.roo-combo-removable-btn', true).first();
10408         return close ? close : false;
10409     },
10410     
10411     removeBtnClick : function(e, h, el)
10412     {
10413         e.preventDefault();
10414         
10415         if(this.fireEvent("remove", this) !== false){
10416             this.reset();
10417             this.fireEvent("afterremove", this)
10418         }
10419     },
10420     
10421     createList : function()
10422     {
10423         this.list = Roo.get(document.body).createChild({
10424             tag: 'ul',
10425             cls: 'typeahead typeahead-long dropdown-menu',
10426             style: 'display:none'
10427         });
10428         
10429         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10430         
10431     },
10432
10433     // private
10434     initTrigger : function(){
10435        
10436     },
10437
10438     // private
10439     onDestroy : function(){
10440         if(this.trigger){
10441             this.trigger.removeAllListeners();
10442           //  this.trigger.remove();
10443         }
10444         //if(this.wrap){
10445         //    this.wrap.remove();
10446         //}
10447         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10448     },
10449
10450     // private
10451     onFocus : function(){
10452         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10453         /*
10454         if(!this.mimicing){
10455             this.wrap.addClass('x-trigger-wrap-focus');
10456             this.mimicing = true;
10457             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10458             if(this.monitorTab){
10459                 this.el.on("keydown", this.checkTab, this);
10460             }
10461         }
10462         */
10463     },
10464
10465     // private
10466     checkTab : function(e){
10467         if(e.getKey() == e.TAB){
10468             this.triggerBlur();
10469         }
10470     },
10471
10472     // private
10473     onBlur : function(){
10474         // do nothing
10475     },
10476
10477     // private
10478     mimicBlur : function(e, t){
10479         /*
10480         if(!this.wrap.contains(t) && this.validateBlur()){
10481             this.triggerBlur();
10482         }
10483         */
10484     },
10485
10486     // private
10487     triggerBlur : function(){
10488         this.mimicing = false;
10489         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10490         if(this.monitorTab){
10491             this.el.un("keydown", this.checkTab, this);
10492         }
10493         //this.wrap.removeClass('x-trigger-wrap-focus');
10494         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10495     },
10496
10497     // private
10498     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10499     validateBlur : function(e, t){
10500         return true;
10501     },
10502
10503     // private
10504     onDisable : function(){
10505         this.inputEl().dom.disabled = true;
10506         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10507         //if(this.wrap){
10508         //    this.wrap.addClass('x-item-disabled');
10509         //}
10510     },
10511
10512     // private
10513     onEnable : function(){
10514         this.inputEl().dom.disabled = false;
10515         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10516         //if(this.wrap){
10517         //    this.el.removeClass('x-item-disabled');
10518         //}
10519     },
10520
10521     // private
10522     onShow : function(){
10523         var ae = this.getActionEl();
10524         
10525         if(ae){
10526             ae.dom.style.display = '';
10527             ae.dom.style.visibility = 'visible';
10528         }
10529     },
10530
10531     // private
10532     
10533     onHide : function(){
10534         var ae = this.getActionEl();
10535         ae.dom.style.display = 'none';
10536     },
10537
10538     /**
10539      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10540      * by an implementing function.
10541      * @method
10542      * @param {EventObject} e
10543      */
10544     onTriggerClick : Roo.emptyFn
10545 });
10546  /*
10547  * Based on:
10548  * Ext JS Library 1.1.1
10549  * Copyright(c) 2006-2007, Ext JS, LLC.
10550  *
10551  * Originally Released Under LGPL - original licence link has changed is not relivant.
10552  *
10553  * Fork - LGPL
10554  * <script type="text/javascript">
10555  */
10556
10557
10558 /**
10559  * @class Roo.data.SortTypes
10560  * @singleton
10561  * Defines the default sorting (casting?) comparison functions used when sorting data.
10562  */
10563 Roo.data.SortTypes = {
10564     /**
10565      * Default sort that does nothing
10566      * @param {Mixed} s The value being converted
10567      * @return {Mixed} The comparison value
10568      */
10569     none : function(s){
10570         return s;
10571     },
10572     
10573     /**
10574      * The regular expression used to strip tags
10575      * @type {RegExp}
10576      * @property
10577      */
10578     stripTagsRE : /<\/?[^>]+>/gi,
10579     
10580     /**
10581      * Strips all HTML tags to sort on text only
10582      * @param {Mixed} s The value being converted
10583      * @return {String} The comparison value
10584      */
10585     asText : function(s){
10586         return String(s).replace(this.stripTagsRE, "");
10587     },
10588     
10589     /**
10590      * Strips all HTML tags to sort on text only - Case insensitive
10591      * @param {Mixed} s The value being converted
10592      * @return {String} The comparison value
10593      */
10594     asUCText : function(s){
10595         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10596     },
10597     
10598     /**
10599      * Case insensitive string
10600      * @param {Mixed} s The value being converted
10601      * @return {String} The comparison value
10602      */
10603     asUCString : function(s) {
10604         return String(s).toUpperCase();
10605     },
10606     
10607     /**
10608      * Date sorting
10609      * @param {Mixed} s The value being converted
10610      * @return {Number} The comparison value
10611      */
10612     asDate : function(s) {
10613         if(!s){
10614             return 0;
10615         }
10616         if(s instanceof Date){
10617             return s.getTime();
10618         }
10619         return Date.parse(String(s));
10620     },
10621     
10622     /**
10623      * Float sorting
10624      * @param {Mixed} s The value being converted
10625      * @return {Float} The comparison value
10626      */
10627     asFloat : function(s) {
10628         var val = parseFloat(String(s).replace(/,/g, ""));
10629         if(isNaN(val)) {
10630             val = 0;
10631         }
10632         return val;
10633     },
10634     
10635     /**
10636      * Integer sorting
10637      * @param {Mixed} s The value being converted
10638      * @return {Number} The comparison value
10639      */
10640     asInt : function(s) {
10641         var val = parseInt(String(s).replace(/,/g, ""));
10642         if(isNaN(val)) {
10643             val = 0;
10644         }
10645         return val;
10646     }
10647 };/*
10648  * Based on:
10649  * Ext JS Library 1.1.1
10650  * Copyright(c) 2006-2007, Ext JS, LLC.
10651  *
10652  * Originally Released Under LGPL - original licence link has changed is not relivant.
10653  *
10654  * Fork - LGPL
10655  * <script type="text/javascript">
10656  */
10657
10658 /**
10659 * @class Roo.data.Record
10660  * Instances of this class encapsulate both record <em>definition</em> information, and record
10661  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10662  * to access Records cached in an {@link Roo.data.Store} object.<br>
10663  * <p>
10664  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10665  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10666  * objects.<br>
10667  * <p>
10668  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10669  * @constructor
10670  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10671  * {@link #create}. The parameters are the same.
10672  * @param {Array} data An associative Array of data values keyed by the field name.
10673  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10674  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10675  * not specified an integer id is generated.
10676  */
10677 Roo.data.Record = function(data, id){
10678     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10679     this.data = data;
10680 };
10681
10682 /**
10683  * Generate a constructor for a specific record layout.
10684  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10685  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10686  * Each field definition object may contain the following properties: <ul>
10687  * <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,
10688  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10689  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10690  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10691  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10692  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10693  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10694  * this may be omitted.</p></li>
10695  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10696  * <ul><li>auto (Default, implies no conversion)</li>
10697  * <li>string</li>
10698  * <li>int</li>
10699  * <li>float</li>
10700  * <li>boolean</li>
10701  * <li>date</li></ul></p></li>
10702  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10703  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10704  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10705  * by the Reader into an object that will be stored in the Record. It is passed the
10706  * following parameters:<ul>
10707  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10708  * </ul></p></li>
10709  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10710  * </ul>
10711  * <br>usage:<br><pre><code>
10712 var TopicRecord = Roo.data.Record.create(
10713     {name: 'title', mapping: 'topic_title'},
10714     {name: 'author', mapping: 'username'},
10715     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10716     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10717     {name: 'lastPoster', mapping: 'user2'},
10718     {name: 'excerpt', mapping: 'post_text'}
10719 );
10720
10721 var myNewRecord = new TopicRecord({
10722     title: 'Do my job please',
10723     author: 'noobie',
10724     totalPosts: 1,
10725     lastPost: new Date(),
10726     lastPoster: 'Animal',
10727     excerpt: 'No way dude!'
10728 });
10729 myStore.add(myNewRecord);
10730 </code></pre>
10731  * @method create
10732  * @static
10733  */
10734 Roo.data.Record.create = function(o){
10735     var f = function(){
10736         f.superclass.constructor.apply(this, arguments);
10737     };
10738     Roo.extend(f, Roo.data.Record);
10739     var p = f.prototype;
10740     p.fields = new Roo.util.MixedCollection(false, function(field){
10741         return field.name;
10742     });
10743     for(var i = 0, len = o.length; i < len; i++){
10744         p.fields.add(new Roo.data.Field(o[i]));
10745     }
10746     f.getField = function(name){
10747         return p.fields.get(name);  
10748     };
10749     return f;
10750 };
10751
10752 Roo.data.Record.AUTO_ID = 1000;
10753 Roo.data.Record.EDIT = 'edit';
10754 Roo.data.Record.REJECT = 'reject';
10755 Roo.data.Record.COMMIT = 'commit';
10756
10757 Roo.data.Record.prototype = {
10758     /**
10759      * Readonly flag - true if this record has been modified.
10760      * @type Boolean
10761      */
10762     dirty : false,
10763     editing : false,
10764     error: null,
10765     modified: null,
10766
10767     // private
10768     join : function(store){
10769         this.store = store;
10770     },
10771
10772     /**
10773      * Set the named field to the specified value.
10774      * @param {String} name The name of the field to set.
10775      * @param {Object} value The value to set the field to.
10776      */
10777     set : function(name, value){
10778         if(this.data[name] == value){
10779             return;
10780         }
10781         this.dirty = true;
10782         if(!this.modified){
10783             this.modified = {};
10784         }
10785         if(typeof this.modified[name] == 'undefined'){
10786             this.modified[name] = this.data[name];
10787         }
10788         this.data[name] = value;
10789         if(!this.editing && this.store){
10790             this.store.afterEdit(this);
10791         }       
10792     },
10793
10794     /**
10795      * Get the value of the named field.
10796      * @param {String} name The name of the field to get the value of.
10797      * @return {Object} The value of the field.
10798      */
10799     get : function(name){
10800         return this.data[name]; 
10801     },
10802
10803     // private
10804     beginEdit : function(){
10805         this.editing = true;
10806         this.modified = {}; 
10807     },
10808
10809     // private
10810     cancelEdit : function(){
10811         this.editing = false;
10812         delete this.modified;
10813     },
10814
10815     // private
10816     endEdit : function(){
10817         this.editing = false;
10818         if(this.dirty && this.store){
10819             this.store.afterEdit(this);
10820         }
10821     },
10822
10823     /**
10824      * Usually called by the {@link Roo.data.Store} which owns the Record.
10825      * Rejects all changes made to the Record since either creation, or the last commit operation.
10826      * Modified fields are reverted to their original values.
10827      * <p>
10828      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10829      * of reject operations.
10830      */
10831     reject : function(){
10832         var m = this.modified;
10833         for(var n in m){
10834             if(typeof m[n] != "function"){
10835                 this.data[n] = m[n];
10836             }
10837         }
10838         this.dirty = false;
10839         delete this.modified;
10840         this.editing = false;
10841         if(this.store){
10842             this.store.afterReject(this);
10843         }
10844     },
10845
10846     /**
10847      * Usually called by the {@link Roo.data.Store} which owns the Record.
10848      * Commits all changes made to the Record since either creation, or the last commit operation.
10849      * <p>
10850      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10851      * of commit operations.
10852      */
10853     commit : function(){
10854         this.dirty = false;
10855         delete this.modified;
10856         this.editing = false;
10857         if(this.store){
10858             this.store.afterCommit(this);
10859         }
10860     },
10861
10862     // private
10863     hasError : function(){
10864         return this.error != null;
10865     },
10866
10867     // private
10868     clearError : function(){
10869         this.error = null;
10870     },
10871
10872     /**
10873      * Creates a copy of this record.
10874      * @param {String} id (optional) A new record id if you don't want to use this record's id
10875      * @return {Record}
10876      */
10877     copy : function(newId) {
10878         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10879     }
10880 };/*
10881  * Based on:
10882  * Ext JS Library 1.1.1
10883  * Copyright(c) 2006-2007, Ext JS, LLC.
10884  *
10885  * Originally Released Under LGPL - original licence link has changed is not relivant.
10886  *
10887  * Fork - LGPL
10888  * <script type="text/javascript">
10889  */
10890
10891
10892
10893 /**
10894  * @class Roo.data.Store
10895  * @extends Roo.util.Observable
10896  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10897  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10898  * <p>
10899  * 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
10900  * has no knowledge of the format of the data returned by the Proxy.<br>
10901  * <p>
10902  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10903  * instances from the data object. These records are cached and made available through accessor functions.
10904  * @constructor
10905  * Creates a new Store.
10906  * @param {Object} config A config object containing the objects needed for the Store to access data,
10907  * and read the data into Records.
10908  */
10909 Roo.data.Store = function(config){
10910     this.data = new Roo.util.MixedCollection(false);
10911     this.data.getKey = function(o){
10912         return o.id;
10913     };
10914     this.baseParams = {};
10915     // private
10916     this.paramNames = {
10917         "start" : "start",
10918         "limit" : "limit",
10919         "sort" : "sort",
10920         "dir" : "dir",
10921         "multisort" : "_multisort"
10922     };
10923
10924     if(config && config.data){
10925         this.inlineData = config.data;
10926         delete config.data;
10927     }
10928
10929     Roo.apply(this, config);
10930     
10931     if(this.reader){ // reader passed
10932         this.reader = Roo.factory(this.reader, Roo.data);
10933         this.reader.xmodule = this.xmodule || false;
10934         if(!this.recordType){
10935             this.recordType = this.reader.recordType;
10936         }
10937         if(this.reader.onMetaChange){
10938             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10939         }
10940     }
10941
10942     if(this.recordType){
10943         this.fields = this.recordType.prototype.fields;
10944     }
10945     this.modified = [];
10946
10947     this.addEvents({
10948         /**
10949          * @event datachanged
10950          * Fires when the data cache has changed, and a widget which is using this Store
10951          * as a Record cache should refresh its view.
10952          * @param {Store} this
10953          */
10954         datachanged : true,
10955         /**
10956          * @event metachange
10957          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10958          * @param {Store} this
10959          * @param {Object} meta The JSON metadata
10960          */
10961         metachange : true,
10962         /**
10963          * @event add
10964          * Fires when Records have been added to the Store
10965          * @param {Store} this
10966          * @param {Roo.data.Record[]} records The array of Records added
10967          * @param {Number} index The index at which the record(s) were added
10968          */
10969         add : true,
10970         /**
10971          * @event remove
10972          * Fires when a Record has been removed from the Store
10973          * @param {Store} this
10974          * @param {Roo.data.Record} record The Record that was removed
10975          * @param {Number} index The index at which the record was removed
10976          */
10977         remove : true,
10978         /**
10979          * @event update
10980          * Fires when a Record has been updated
10981          * @param {Store} this
10982          * @param {Roo.data.Record} record The Record that was updated
10983          * @param {String} operation The update operation being performed.  Value may be one of:
10984          * <pre><code>
10985  Roo.data.Record.EDIT
10986  Roo.data.Record.REJECT
10987  Roo.data.Record.COMMIT
10988          * </code></pre>
10989          */
10990         update : true,
10991         /**
10992          * @event clear
10993          * Fires when the data cache has been cleared.
10994          * @param {Store} this
10995          */
10996         clear : true,
10997         /**
10998          * @event beforeload
10999          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11000          * the load action will be canceled.
11001          * @param {Store} this
11002          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11003          */
11004         beforeload : true,
11005         /**
11006          * @event beforeloadadd
11007          * Fires after a new set of Records has been loaded.
11008          * @param {Store} this
11009          * @param {Roo.data.Record[]} records The Records that were loaded
11010          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11011          */
11012         beforeloadadd : true,
11013         /**
11014          * @event load
11015          * Fires after a new set of Records has been loaded, before they are added to the store.
11016          * @param {Store} this
11017          * @param {Roo.data.Record[]} records The Records that were loaded
11018          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11019          * @params {Object} return from reader
11020          */
11021         load : true,
11022         /**
11023          * @event loadexception
11024          * Fires if an exception occurs in the Proxy during loading.
11025          * Called with the signature of the Proxy's "loadexception" event.
11026          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11027          * 
11028          * @param {Proxy} 
11029          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11030          * @param {Object} load options 
11031          * @param {Object} jsonData from your request (normally this contains the Exception)
11032          */
11033         loadexception : true
11034     });
11035     
11036     if(this.proxy){
11037         this.proxy = Roo.factory(this.proxy, Roo.data);
11038         this.proxy.xmodule = this.xmodule || false;
11039         this.relayEvents(this.proxy,  ["loadexception"]);
11040     }
11041     this.sortToggle = {};
11042     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11043
11044     Roo.data.Store.superclass.constructor.call(this);
11045
11046     if(this.inlineData){
11047         this.loadData(this.inlineData);
11048         delete this.inlineData;
11049     }
11050 };
11051
11052 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11053      /**
11054     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11055     * without a remote query - used by combo/forms at present.
11056     */
11057     
11058     /**
11059     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11060     */
11061     /**
11062     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11063     */
11064     /**
11065     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11066     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11067     */
11068     /**
11069     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11070     * on any HTTP request
11071     */
11072     /**
11073     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11074     */
11075     /**
11076     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11077     */
11078     multiSort: false,
11079     /**
11080     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11081     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11082     */
11083     remoteSort : false,
11084
11085     /**
11086     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11087      * loaded or when a record is removed. (defaults to false).
11088     */
11089     pruneModifiedRecords : false,
11090
11091     // private
11092     lastOptions : null,
11093
11094     /**
11095      * Add Records to the Store and fires the add event.
11096      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11097      */
11098     add : function(records){
11099         records = [].concat(records);
11100         for(var i = 0, len = records.length; i < len; i++){
11101             records[i].join(this);
11102         }
11103         var index = this.data.length;
11104         this.data.addAll(records);
11105         this.fireEvent("add", this, records, index);
11106     },
11107
11108     /**
11109      * Remove a Record from the Store and fires the remove event.
11110      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11111      */
11112     remove : function(record){
11113         var index = this.data.indexOf(record);
11114         this.data.removeAt(index);
11115         if(this.pruneModifiedRecords){
11116             this.modified.remove(record);
11117         }
11118         this.fireEvent("remove", this, record, index);
11119     },
11120
11121     /**
11122      * Remove all Records from the Store and fires the clear event.
11123      */
11124     removeAll : function(){
11125         this.data.clear();
11126         if(this.pruneModifiedRecords){
11127             this.modified = [];
11128         }
11129         this.fireEvent("clear", this);
11130     },
11131
11132     /**
11133      * Inserts Records to the Store at the given index and fires the add event.
11134      * @param {Number} index The start index at which to insert the passed Records.
11135      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11136      */
11137     insert : function(index, records){
11138         records = [].concat(records);
11139         for(var i = 0, len = records.length; i < len; i++){
11140             this.data.insert(index, records[i]);
11141             records[i].join(this);
11142         }
11143         this.fireEvent("add", this, records, index);
11144     },
11145
11146     /**
11147      * Get the index within the cache of the passed Record.
11148      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11149      * @return {Number} The index of the passed Record. Returns -1 if not found.
11150      */
11151     indexOf : function(record){
11152         return this.data.indexOf(record);
11153     },
11154
11155     /**
11156      * Get the index within the cache of the Record with the passed id.
11157      * @param {String} id The id of the Record to find.
11158      * @return {Number} The index of the Record. Returns -1 if not found.
11159      */
11160     indexOfId : function(id){
11161         return this.data.indexOfKey(id);
11162     },
11163
11164     /**
11165      * Get the Record with the specified id.
11166      * @param {String} id The id of the Record to find.
11167      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11168      */
11169     getById : function(id){
11170         return this.data.key(id);
11171     },
11172
11173     /**
11174      * Get the Record at the specified index.
11175      * @param {Number} index The index of the Record to find.
11176      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11177      */
11178     getAt : function(index){
11179         return this.data.itemAt(index);
11180     },
11181
11182     /**
11183      * Returns a range of Records between specified indices.
11184      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11185      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11186      * @return {Roo.data.Record[]} An array of Records
11187      */
11188     getRange : function(start, end){
11189         return this.data.getRange(start, end);
11190     },
11191
11192     // private
11193     storeOptions : function(o){
11194         o = Roo.apply({}, o);
11195         delete o.callback;
11196         delete o.scope;
11197         this.lastOptions = o;
11198     },
11199
11200     /**
11201      * Loads the Record cache from the configured Proxy using the configured Reader.
11202      * <p>
11203      * If using remote paging, then the first load call must specify the <em>start</em>
11204      * and <em>limit</em> properties in the options.params property to establish the initial
11205      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11206      * <p>
11207      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11208      * and this call will return before the new data has been loaded. Perform any post-processing
11209      * in a callback function, or in a "load" event handler.</strong>
11210      * <p>
11211      * @param {Object} options An object containing properties which control loading options:<ul>
11212      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11213      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11214      * passed the following arguments:<ul>
11215      * <li>r : Roo.data.Record[]</li>
11216      * <li>options: Options object from the load call</li>
11217      * <li>success: Boolean success indicator</li></ul></li>
11218      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11219      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11220      * </ul>
11221      */
11222     load : function(options){
11223         options = options || {};
11224         if(this.fireEvent("beforeload", this, options) !== false){
11225             this.storeOptions(options);
11226             var p = Roo.apply(options.params || {}, this.baseParams);
11227             // if meta was not loaded from remote source.. try requesting it.
11228             if (!this.reader.metaFromRemote) {
11229                 p._requestMeta = 1;
11230             }
11231             if(this.sortInfo && this.remoteSort){
11232                 var pn = this.paramNames;
11233                 p[pn["sort"]] = this.sortInfo.field;
11234                 p[pn["dir"]] = this.sortInfo.direction;
11235             }
11236             if (this.multiSort) {
11237                 var pn = this.paramNames;
11238                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11239             }
11240             
11241             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11242         }
11243     },
11244
11245     /**
11246      * Reloads the Record cache from the configured Proxy using the configured Reader and
11247      * the options from the last load operation performed.
11248      * @param {Object} options (optional) An object containing properties which may override the options
11249      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11250      * the most recently used options are reused).
11251      */
11252     reload : function(options){
11253         this.load(Roo.applyIf(options||{}, this.lastOptions));
11254     },
11255
11256     // private
11257     // Called as a callback by the Reader during a load operation.
11258     loadRecords : function(o, options, success){
11259         if(!o || success === false){
11260             if(success !== false){
11261                 this.fireEvent("load", this, [], options, o);
11262             }
11263             if(options.callback){
11264                 options.callback.call(options.scope || this, [], options, false);
11265             }
11266             return;
11267         }
11268         // if data returned failure - throw an exception.
11269         if (o.success === false) {
11270             // show a message if no listener is registered.
11271             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11272                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11273             }
11274             // loadmask wil be hooked into this..
11275             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11276             return;
11277         }
11278         var r = o.records, t = o.totalRecords || r.length;
11279         
11280         this.fireEvent("beforeloadadd", this, r, options, o);
11281         
11282         if(!options || options.add !== true){
11283             if(this.pruneModifiedRecords){
11284                 this.modified = [];
11285             }
11286             for(var i = 0, len = r.length; i < len; i++){
11287                 r[i].join(this);
11288             }
11289             if(this.snapshot){
11290                 this.data = this.snapshot;
11291                 delete this.snapshot;
11292             }
11293             this.data.clear();
11294             this.data.addAll(r);
11295             this.totalLength = t;
11296             this.applySort();
11297             this.fireEvent("datachanged", this);
11298         }else{
11299             this.totalLength = Math.max(t, this.data.length+r.length);
11300             this.add(r);
11301         }
11302         
11303         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11304                 
11305             var e = new Roo.data.Record({});
11306
11307             e.set(this.parent.displayField, this.parent.emptyTitle);
11308             e.set(this.parent.valueField, '');
11309
11310             this.insert(0, e);
11311         }
11312             
11313         this.fireEvent("load", this, r, options, o);
11314         if(options.callback){
11315             options.callback.call(options.scope || this, r, options, true);
11316         }
11317     },
11318
11319
11320     /**
11321      * Loads data from a passed data block. A Reader which understands the format of the data
11322      * must have been configured in the constructor.
11323      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11324      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11325      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11326      */
11327     loadData : function(o, append){
11328         var r = this.reader.readRecords(o);
11329         this.loadRecords(r, {add: append}, true);
11330     },
11331
11332     /**
11333      * Gets the number of cached records.
11334      * <p>
11335      * <em>If using paging, this may not be the total size of the dataset. If the data object
11336      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11337      * the data set size</em>
11338      */
11339     getCount : function(){
11340         return this.data.length || 0;
11341     },
11342
11343     /**
11344      * Gets the total number of records in the dataset as returned by the server.
11345      * <p>
11346      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11347      * the dataset size</em>
11348      */
11349     getTotalCount : function(){
11350         return this.totalLength || 0;
11351     },
11352
11353     /**
11354      * Returns the sort state of the Store as an object with two properties:
11355      * <pre><code>
11356  field {String} The name of the field by which the Records are sorted
11357  direction {String} The sort order, "ASC" or "DESC"
11358      * </code></pre>
11359      */
11360     getSortState : function(){
11361         return this.sortInfo;
11362     },
11363
11364     // private
11365     applySort : function(){
11366         if(this.sortInfo && !this.remoteSort){
11367             var s = this.sortInfo, f = s.field;
11368             var st = this.fields.get(f).sortType;
11369             var fn = function(r1, r2){
11370                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11371                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11372             };
11373             this.data.sort(s.direction, fn);
11374             if(this.snapshot && this.snapshot != this.data){
11375                 this.snapshot.sort(s.direction, fn);
11376             }
11377         }
11378     },
11379
11380     /**
11381      * Sets the default sort column and order to be used by the next load operation.
11382      * @param {String} fieldName The name of the field to sort by.
11383      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11384      */
11385     setDefaultSort : function(field, dir){
11386         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11387     },
11388
11389     /**
11390      * Sort the Records.
11391      * If remote sorting is used, the sort is performed on the server, and the cache is
11392      * reloaded. If local sorting is used, the cache is sorted internally.
11393      * @param {String} fieldName The name of the field to sort by.
11394      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11395      */
11396     sort : function(fieldName, dir){
11397         var f = this.fields.get(fieldName);
11398         if(!dir){
11399             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11400             
11401             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11402                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11403             }else{
11404                 dir = f.sortDir;
11405             }
11406         }
11407         this.sortToggle[f.name] = dir;
11408         this.sortInfo = {field: f.name, direction: dir};
11409         if(!this.remoteSort){
11410             this.applySort();
11411             this.fireEvent("datachanged", this);
11412         }else{
11413             this.load(this.lastOptions);
11414         }
11415     },
11416
11417     /**
11418      * Calls the specified function for each of the Records in the cache.
11419      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11420      * Returning <em>false</em> aborts and exits the iteration.
11421      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11422      */
11423     each : function(fn, scope){
11424         this.data.each(fn, scope);
11425     },
11426
11427     /**
11428      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11429      * (e.g., during paging).
11430      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11431      */
11432     getModifiedRecords : function(){
11433         return this.modified;
11434     },
11435
11436     // private
11437     createFilterFn : function(property, value, anyMatch){
11438         if(!value.exec){ // not a regex
11439             value = String(value);
11440             if(value.length == 0){
11441                 return false;
11442             }
11443             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11444         }
11445         return function(r){
11446             return value.test(r.data[property]);
11447         };
11448     },
11449
11450     /**
11451      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11452      * @param {String} property A field on your records
11453      * @param {Number} start The record index to start at (defaults to 0)
11454      * @param {Number} end The last record index to include (defaults to length - 1)
11455      * @return {Number} The sum
11456      */
11457     sum : function(property, start, end){
11458         var rs = this.data.items, v = 0;
11459         start = start || 0;
11460         end = (end || end === 0) ? end : rs.length-1;
11461
11462         for(var i = start; i <= end; i++){
11463             v += (rs[i].data[property] || 0);
11464         }
11465         return v;
11466     },
11467
11468     /**
11469      * Filter the records by a specified property.
11470      * @param {String} field A field on your records
11471      * @param {String/RegExp} value Either a string that the field
11472      * should start with or a RegExp to test against the field
11473      * @param {Boolean} anyMatch True to match any part not just the beginning
11474      */
11475     filter : function(property, value, anyMatch){
11476         var fn = this.createFilterFn(property, value, anyMatch);
11477         return fn ? this.filterBy(fn) : this.clearFilter();
11478     },
11479
11480     /**
11481      * Filter by a function. The specified function will be called with each
11482      * record in this data source. If the function returns true the record is included,
11483      * otherwise it is filtered.
11484      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11485      * @param {Object} scope (optional) The scope of the function (defaults to this)
11486      */
11487     filterBy : function(fn, scope){
11488         this.snapshot = this.snapshot || this.data;
11489         this.data = this.queryBy(fn, scope||this);
11490         this.fireEvent("datachanged", this);
11491     },
11492
11493     /**
11494      * Query the records by a specified property.
11495      * @param {String} field A field on your records
11496      * @param {String/RegExp} value Either a string that the field
11497      * should start with or a RegExp to test against the field
11498      * @param {Boolean} anyMatch True to match any part not just the beginning
11499      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11500      */
11501     query : function(property, value, anyMatch){
11502         var fn = this.createFilterFn(property, value, anyMatch);
11503         return fn ? this.queryBy(fn) : this.data.clone();
11504     },
11505
11506     /**
11507      * Query by a function. The specified function will be called with each
11508      * record in this data source. If the function returns true the record is included
11509      * in the results.
11510      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11511      * @param {Object} scope (optional) The scope of the function (defaults to this)
11512       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11513      **/
11514     queryBy : function(fn, scope){
11515         var data = this.snapshot || this.data;
11516         return data.filterBy(fn, scope||this);
11517     },
11518
11519     /**
11520      * Collects unique values for a particular dataIndex from this store.
11521      * @param {String} dataIndex The property to collect
11522      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11523      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11524      * @return {Array} An array of the unique values
11525      **/
11526     collect : function(dataIndex, allowNull, bypassFilter){
11527         var d = (bypassFilter === true && this.snapshot) ?
11528                 this.snapshot.items : this.data.items;
11529         var v, sv, r = [], l = {};
11530         for(var i = 0, len = d.length; i < len; i++){
11531             v = d[i].data[dataIndex];
11532             sv = String(v);
11533             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11534                 l[sv] = true;
11535                 r[r.length] = v;
11536             }
11537         }
11538         return r;
11539     },
11540
11541     /**
11542      * Revert to a view of the Record cache with no filtering applied.
11543      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11544      */
11545     clearFilter : function(suppressEvent){
11546         if(this.snapshot && this.snapshot != this.data){
11547             this.data = this.snapshot;
11548             delete this.snapshot;
11549             if(suppressEvent !== true){
11550                 this.fireEvent("datachanged", this);
11551             }
11552         }
11553     },
11554
11555     // private
11556     afterEdit : function(record){
11557         if(this.modified.indexOf(record) == -1){
11558             this.modified.push(record);
11559         }
11560         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11561     },
11562     
11563     // private
11564     afterReject : function(record){
11565         this.modified.remove(record);
11566         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11567     },
11568
11569     // private
11570     afterCommit : function(record){
11571         this.modified.remove(record);
11572         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11573     },
11574
11575     /**
11576      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11577      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11578      */
11579     commitChanges : function(){
11580         var m = this.modified.slice(0);
11581         this.modified = [];
11582         for(var i = 0, len = m.length; i < len; i++){
11583             m[i].commit();
11584         }
11585     },
11586
11587     /**
11588      * Cancel outstanding changes on all changed records.
11589      */
11590     rejectChanges : function(){
11591         var m = this.modified.slice(0);
11592         this.modified = [];
11593         for(var i = 0, len = m.length; i < len; i++){
11594             m[i].reject();
11595         }
11596     },
11597
11598     onMetaChange : function(meta, rtype, o){
11599         this.recordType = rtype;
11600         this.fields = rtype.prototype.fields;
11601         delete this.snapshot;
11602         this.sortInfo = meta.sortInfo || this.sortInfo;
11603         this.modified = [];
11604         this.fireEvent('metachange', this, this.reader.meta);
11605     },
11606     
11607     moveIndex : function(data, type)
11608     {
11609         var index = this.indexOf(data);
11610         
11611         var newIndex = index + type;
11612         
11613         this.remove(data);
11614         
11615         this.insert(newIndex, data);
11616         
11617     }
11618 });/*
11619  * Based on:
11620  * Ext JS Library 1.1.1
11621  * Copyright(c) 2006-2007, Ext JS, LLC.
11622  *
11623  * Originally Released Under LGPL - original licence link has changed is not relivant.
11624  *
11625  * Fork - LGPL
11626  * <script type="text/javascript">
11627  */
11628
11629 /**
11630  * @class Roo.data.SimpleStore
11631  * @extends Roo.data.Store
11632  * Small helper class to make creating Stores from Array data easier.
11633  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11634  * @cfg {Array} fields An array of field definition objects, or field name strings.
11635  * @cfg {Array} data The multi-dimensional array of data
11636  * @constructor
11637  * @param {Object} config
11638  */
11639 Roo.data.SimpleStore = function(config){
11640     Roo.data.SimpleStore.superclass.constructor.call(this, {
11641         isLocal : true,
11642         reader: new Roo.data.ArrayReader({
11643                 id: config.id
11644             },
11645             Roo.data.Record.create(config.fields)
11646         ),
11647         proxy : new Roo.data.MemoryProxy(config.data)
11648     });
11649     this.load();
11650 };
11651 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11652  * Based on:
11653  * Ext JS Library 1.1.1
11654  * Copyright(c) 2006-2007, Ext JS, LLC.
11655  *
11656  * Originally Released Under LGPL - original licence link has changed is not relivant.
11657  *
11658  * Fork - LGPL
11659  * <script type="text/javascript">
11660  */
11661
11662 /**
11663 /**
11664  * @extends Roo.data.Store
11665  * @class Roo.data.JsonStore
11666  * Small helper class to make creating Stores for JSON data easier. <br/>
11667 <pre><code>
11668 var store = new Roo.data.JsonStore({
11669     url: 'get-images.php',
11670     root: 'images',
11671     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11672 });
11673 </code></pre>
11674  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11675  * JsonReader and HttpProxy (unless inline data is provided).</b>
11676  * @cfg {Array} fields An array of field definition objects, or field name strings.
11677  * @constructor
11678  * @param {Object} config
11679  */
11680 Roo.data.JsonStore = function(c){
11681     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11682         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11683         reader: new Roo.data.JsonReader(c, c.fields)
11684     }));
11685 };
11686 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11687  * Based on:
11688  * Ext JS Library 1.1.1
11689  * Copyright(c) 2006-2007, Ext JS, LLC.
11690  *
11691  * Originally Released Under LGPL - original licence link has changed is not relivant.
11692  *
11693  * Fork - LGPL
11694  * <script type="text/javascript">
11695  */
11696
11697  
11698 Roo.data.Field = function(config){
11699     if(typeof config == "string"){
11700         config = {name: config};
11701     }
11702     Roo.apply(this, config);
11703     
11704     if(!this.type){
11705         this.type = "auto";
11706     }
11707     
11708     var st = Roo.data.SortTypes;
11709     // named sortTypes are supported, here we look them up
11710     if(typeof this.sortType == "string"){
11711         this.sortType = st[this.sortType];
11712     }
11713     
11714     // set default sortType for strings and dates
11715     if(!this.sortType){
11716         switch(this.type){
11717             case "string":
11718                 this.sortType = st.asUCString;
11719                 break;
11720             case "date":
11721                 this.sortType = st.asDate;
11722                 break;
11723             default:
11724                 this.sortType = st.none;
11725         }
11726     }
11727
11728     // define once
11729     var stripRe = /[\$,%]/g;
11730
11731     // prebuilt conversion function for this field, instead of
11732     // switching every time we're reading a value
11733     if(!this.convert){
11734         var cv, dateFormat = this.dateFormat;
11735         switch(this.type){
11736             case "":
11737             case "auto":
11738             case undefined:
11739                 cv = function(v){ return v; };
11740                 break;
11741             case "string":
11742                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11743                 break;
11744             case "int":
11745                 cv = function(v){
11746                     return v !== undefined && v !== null && v !== '' ?
11747                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11748                     };
11749                 break;
11750             case "float":
11751                 cv = function(v){
11752                     return v !== undefined && v !== null && v !== '' ?
11753                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11754                     };
11755                 break;
11756             case "bool":
11757             case "boolean":
11758                 cv = function(v){ return v === true || v === "true" || v == 1; };
11759                 break;
11760             case "date":
11761                 cv = function(v){
11762                     if(!v){
11763                         return '';
11764                     }
11765                     if(v instanceof Date){
11766                         return v;
11767                     }
11768                     if(dateFormat){
11769                         if(dateFormat == "timestamp"){
11770                             return new Date(v*1000);
11771                         }
11772                         return Date.parseDate(v, dateFormat);
11773                     }
11774                     var parsed = Date.parse(v);
11775                     return parsed ? new Date(parsed) : null;
11776                 };
11777              break;
11778             
11779         }
11780         this.convert = cv;
11781     }
11782 };
11783
11784 Roo.data.Field.prototype = {
11785     dateFormat: null,
11786     defaultValue: "",
11787     mapping: null,
11788     sortType : null,
11789     sortDir : "ASC"
11790 };/*
11791  * Based on:
11792  * Ext JS Library 1.1.1
11793  * Copyright(c) 2006-2007, Ext JS, LLC.
11794  *
11795  * Originally Released Under LGPL - original licence link has changed is not relivant.
11796  *
11797  * Fork - LGPL
11798  * <script type="text/javascript">
11799  */
11800  
11801 // Base class for reading structured data from a data source.  This class is intended to be
11802 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11803
11804 /**
11805  * @class Roo.data.DataReader
11806  * Base class for reading structured data from a data source.  This class is intended to be
11807  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11808  */
11809
11810 Roo.data.DataReader = function(meta, recordType){
11811     
11812     this.meta = meta;
11813     
11814     this.recordType = recordType instanceof Array ? 
11815         Roo.data.Record.create(recordType) : recordType;
11816 };
11817
11818 Roo.data.DataReader.prototype = {
11819      /**
11820      * Create an empty record
11821      * @param {Object} data (optional) - overlay some values
11822      * @return {Roo.data.Record} record created.
11823      */
11824     newRow :  function(d) {
11825         var da =  {};
11826         this.recordType.prototype.fields.each(function(c) {
11827             switch( c.type) {
11828                 case 'int' : da[c.name] = 0; break;
11829                 case 'date' : da[c.name] = new Date(); break;
11830                 case 'float' : da[c.name] = 0.0; break;
11831                 case 'boolean' : da[c.name] = false; break;
11832                 default : da[c.name] = ""; break;
11833             }
11834             
11835         });
11836         return new this.recordType(Roo.apply(da, d));
11837     }
11838     
11839 };/*
11840  * Based on:
11841  * Ext JS Library 1.1.1
11842  * Copyright(c) 2006-2007, Ext JS, LLC.
11843  *
11844  * Originally Released Under LGPL - original licence link has changed is not relivant.
11845  *
11846  * Fork - LGPL
11847  * <script type="text/javascript">
11848  */
11849
11850 /**
11851  * @class Roo.data.DataProxy
11852  * @extends Roo.data.Observable
11853  * This class is an abstract base class for implementations which provide retrieval of
11854  * unformatted data objects.<br>
11855  * <p>
11856  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11857  * (of the appropriate type which knows how to parse the data object) to provide a block of
11858  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11859  * <p>
11860  * Custom implementations must implement the load method as described in
11861  * {@link Roo.data.HttpProxy#load}.
11862  */
11863 Roo.data.DataProxy = function(){
11864     this.addEvents({
11865         /**
11866          * @event beforeload
11867          * Fires before a network request is made to retrieve a data object.
11868          * @param {Object} This DataProxy object.
11869          * @param {Object} params The params parameter to the load function.
11870          */
11871         beforeload : true,
11872         /**
11873          * @event load
11874          * Fires before the load method's callback is called.
11875          * @param {Object} This DataProxy object.
11876          * @param {Object} o The data object.
11877          * @param {Object} arg The callback argument object passed to the load function.
11878          */
11879         load : true,
11880         /**
11881          * @event loadexception
11882          * Fires if an Exception occurs during data retrieval.
11883          * @param {Object} This DataProxy object.
11884          * @param {Object} o The data object.
11885          * @param {Object} arg The callback argument object passed to the load function.
11886          * @param {Object} e The Exception.
11887          */
11888         loadexception : true
11889     });
11890     Roo.data.DataProxy.superclass.constructor.call(this);
11891 };
11892
11893 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11894
11895     /**
11896      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11897      */
11898 /*
11899  * Based on:
11900  * Ext JS Library 1.1.1
11901  * Copyright(c) 2006-2007, Ext JS, LLC.
11902  *
11903  * Originally Released Under LGPL - original licence link has changed is not relivant.
11904  *
11905  * Fork - LGPL
11906  * <script type="text/javascript">
11907  */
11908 /**
11909  * @class Roo.data.MemoryProxy
11910  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11911  * to the Reader when its load method is called.
11912  * @constructor
11913  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11914  */
11915 Roo.data.MemoryProxy = function(data){
11916     if (data.data) {
11917         data = data.data;
11918     }
11919     Roo.data.MemoryProxy.superclass.constructor.call(this);
11920     this.data = data;
11921 };
11922
11923 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11924     
11925     /**
11926      * Load data from the requested source (in this case an in-memory
11927      * data object passed to the constructor), read the data object into
11928      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11929      * process that block using the passed callback.
11930      * @param {Object} params This parameter is not used by the MemoryProxy class.
11931      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11932      * object into a block of Roo.data.Records.
11933      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11934      * The function must be passed <ul>
11935      * <li>The Record block object</li>
11936      * <li>The "arg" argument from the load function</li>
11937      * <li>A boolean success indicator</li>
11938      * </ul>
11939      * @param {Object} scope The scope in which to call the callback
11940      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11941      */
11942     load : function(params, reader, callback, scope, arg){
11943         params = params || {};
11944         var result;
11945         try {
11946             result = reader.readRecords(this.data);
11947         }catch(e){
11948             this.fireEvent("loadexception", this, arg, null, e);
11949             callback.call(scope, null, arg, false);
11950             return;
11951         }
11952         callback.call(scope, result, arg, true);
11953     },
11954     
11955     // private
11956     update : function(params, records){
11957         
11958     }
11959 });/*
11960  * Based on:
11961  * Ext JS Library 1.1.1
11962  * Copyright(c) 2006-2007, Ext JS, LLC.
11963  *
11964  * Originally Released Under LGPL - original licence link has changed is not relivant.
11965  *
11966  * Fork - LGPL
11967  * <script type="text/javascript">
11968  */
11969 /**
11970  * @class Roo.data.HttpProxy
11971  * @extends Roo.data.DataProxy
11972  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11973  * configured to reference a certain URL.<br><br>
11974  * <p>
11975  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11976  * from which the running page was served.<br><br>
11977  * <p>
11978  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11979  * <p>
11980  * Be aware that to enable the browser to parse an XML document, the server must set
11981  * the Content-Type header in the HTTP response to "text/xml".
11982  * @constructor
11983  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11984  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11985  * will be used to make the request.
11986  */
11987 Roo.data.HttpProxy = function(conn){
11988     Roo.data.HttpProxy.superclass.constructor.call(this);
11989     // is conn a conn config or a real conn?
11990     this.conn = conn;
11991     this.useAjax = !conn || !conn.events;
11992   
11993 };
11994
11995 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11996     // thse are take from connection...
11997     
11998     /**
11999      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12000      */
12001     /**
12002      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12003      * extra parameters to each request made by this object. (defaults to undefined)
12004      */
12005     /**
12006      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12007      *  to each request made by this object. (defaults to undefined)
12008      */
12009     /**
12010      * @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)
12011      */
12012     /**
12013      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12014      */
12015      /**
12016      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12017      * @type Boolean
12018      */
12019   
12020
12021     /**
12022      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12023      * @type Boolean
12024      */
12025     /**
12026      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12027      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12028      * a finer-grained basis than the DataProxy events.
12029      */
12030     getConnection : function(){
12031         return this.useAjax ? Roo.Ajax : this.conn;
12032     },
12033
12034     /**
12035      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12036      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12037      * process that block using the passed callback.
12038      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12039      * for the request to the remote server.
12040      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12041      * object into a block of Roo.data.Records.
12042      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12043      * The function must be passed <ul>
12044      * <li>The Record block object</li>
12045      * <li>The "arg" argument from the load function</li>
12046      * <li>A boolean success indicator</li>
12047      * </ul>
12048      * @param {Object} scope The scope in which to call the callback
12049      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12050      */
12051     load : function(params, reader, callback, scope, arg){
12052         if(this.fireEvent("beforeload", this, params) !== false){
12053             var  o = {
12054                 params : params || {},
12055                 request: {
12056                     callback : callback,
12057                     scope : scope,
12058                     arg : arg
12059                 },
12060                 reader: reader,
12061                 callback : this.loadResponse,
12062                 scope: this
12063             };
12064             if(this.useAjax){
12065                 Roo.applyIf(o, this.conn);
12066                 if(this.activeRequest){
12067                     Roo.Ajax.abort(this.activeRequest);
12068                 }
12069                 this.activeRequest = Roo.Ajax.request(o);
12070             }else{
12071                 this.conn.request(o);
12072             }
12073         }else{
12074             callback.call(scope||this, null, arg, false);
12075         }
12076     },
12077
12078     // private
12079     loadResponse : function(o, success, response){
12080         delete this.activeRequest;
12081         if(!success){
12082             this.fireEvent("loadexception", this, o, response);
12083             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12084             return;
12085         }
12086         var result;
12087         try {
12088             result = o.reader.read(response);
12089         }catch(e){
12090             this.fireEvent("loadexception", this, o, response, e);
12091             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12092             return;
12093         }
12094         
12095         this.fireEvent("load", this, o, o.request.arg);
12096         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12097     },
12098
12099     // private
12100     update : function(dataSet){
12101
12102     },
12103
12104     // private
12105     updateResponse : function(dataSet){
12106
12107     }
12108 });/*
12109  * Based on:
12110  * Ext JS Library 1.1.1
12111  * Copyright(c) 2006-2007, Ext JS, LLC.
12112  *
12113  * Originally Released Under LGPL - original licence link has changed is not relivant.
12114  *
12115  * Fork - LGPL
12116  * <script type="text/javascript">
12117  */
12118
12119 /**
12120  * @class Roo.data.ScriptTagProxy
12121  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12122  * other than the originating domain of the running page.<br><br>
12123  * <p>
12124  * <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
12125  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12126  * <p>
12127  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12128  * source code that is used as the source inside a &lt;script> tag.<br><br>
12129  * <p>
12130  * In order for the browser to process the returned data, the server must wrap the data object
12131  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12132  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12133  * depending on whether the callback name was passed:
12134  * <p>
12135  * <pre><code>
12136 boolean scriptTag = false;
12137 String cb = request.getParameter("callback");
12138 if (cb != null) {
12139     scriptTag = true;
12140     response.setContentType("text/javascript");
12141 } else {
12142     response.setContentType("application/x-json");
12143 }
12144 Writer out = response.getWriter();
12145 if (scriptTag) {
12146     out.write(cb + "(");
12147 }
12148 out.print(dataBlock.toJsonString());
12149 if (scriptTag) {
12150     out.write(");");
12151 }
12152 </pre></code>
12153  *
12154  * @constructor
12155  * @param {Object} config A configuration object.
12156  */
12157 Roo.data.ScriptTagProxy = function(config){
12158     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12159     Roo.apply(this, config);
12160     this.head = document.getElementsByTagName("head")[0];
12161 };
12162
12163 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12164
12165 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12166     /**
12167      * @cfg {String} url The URL from which to request the data object.
12168      */
12169     /**
12170      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12171      */
12172     timeout : 30000,
12173     /**
12174      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12175      * the server the name of the callback function set up by the load call to process the returned data object.
12176      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12177      * javascript output which calls this named function passing the data object as its only parameter.
12178      */
12179     callbackParam : "callback",
12180     /**
12181      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12182      * name to the request.
12183      */
12184     nocache : true,
12185
12186     /**
12187      * Load data from the configured URL, read the data object into
12188      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12189      * process that block using the passed callback.
12190      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12191      * for the request to the remote server.
12192      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12193      * object into a block of Roo.data.Records.
12194      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12195      * The function must be passed <ul>
12196      * <li>The Record block object</li>
12197      * <li>The "arg" argument from the load function</li>
12198      * <li>A boolean success indicator</li>
12199      * </ul>
12200      * @param {Object} scope The scope in which to call the callback
12201      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12202      */
12203     load : function(params, reader, callback, scope, arg){
12204         if(this.fireEvent("beforeload", this, params) !== false){
12205
12206             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12207
12208             var url = this.url;
12209             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12210             if(this.nocache){
12211                 url += "&_dc=" + (new Date().getTime());
12212             }
12213             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12214             var trans = {
12215                 id : transId,
12216                 cb : "stcCallback"+transId,
12217                 scriptId : "stcScript"+transId,
12218                 params : params,
12219                 arg : arg,
12220                 url : url,
12221                 callback : callback,
12222                 scope : scope,
12223                 reader : reader
12224             };
12225             var conn = this;
12226
12227             window[trans.cb] = function(o){
12228                 conn.handleResponse(o, trans);
12229             };
12230
12231             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12232
12233             if(this.autoAbort !== false){
12234                 this.abort();
12235             }
12236
12237             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12238
12239             var script = document.createElement("script");
12240             script.setAttribute("src", url);
12241             script.setAttribute("type", "text/javascript");
12242             script.setAttribute("id", trans.scriptId);
12243             this.head.appendChild(script);
12244
12245             this.trans = trans;
12246         }else{
12247             callback.call(scope||this, null, arg, false);
12248         }
12249     },
12250
12251     // private
12252     isLoading : function(){
12253         return this.trans ? true : false;
12254     },
12255
12256     /**
12257      * Abort the current server request.
12258      */
12259     abort : function(){
12260         if(this.isLoading()){
12261             this.destroyTrans(this.trans);
12262         }
12263     },
12264
12265     // private
12266     destroyTrans : function(trans, isLoaded){
12267         this.head.removeChild(document.getElementById(trans.scriptId));
12268         clearTimeout(trans.timeoutId);
12269         if(isLoaded){
12270             window[trans.cb] = undefined;
12271             try{
12272                 delete window[trans.cb];
12273             }catch(e){}
12274         }else{
12275             // if hasn't been loaded, wait for load to remove it to prevent script error
12276             window[trans.cb] = function(){
12277                 window[trans.cb] = undefined;
12278                 try{
12279                     delete window[trans.cb];
12280                 }catch(e){}
12281             };
12282         }
12283     },
12284
12285     // private
12286     handleResponse : function(o, trans){
12287         this.trans = false;
12288         this.destroyTrans(trans, true);
12289         var result;
12290         try {
12291             result = trans.reader.readRecords(o);
12292         }catch(e){
12293             this.fireEvent("loadexception", this, o, trans.arg, e);
12294             trans.callback.call(trans.scope||window, null, trans.arg, false);
12295             return;
12296         }
12297         this.fireEvent("load", this, o, trans.arg);
12298         trans.callback.call(trans.scope||window, result, trans.arg, true);
12299     },
12300
12301     // private
12302     handleFailure : function(trans){
12303         this.trans = false;
12304         this.destroyTrans(trans, false);
12305         this.fireEvent("loadexception", this, null, trans.arg);
12306         trans.callback.call(trans.scope||window, null, trans.arg, false);
12307     }
12308 });/*
12309  * Based on:
12310  * Ext JS Library 1.1.1
12311  * Copyright(c) 2006-2007, Ext JS, LLC.
12312  *
12313  * Originally Released Under LGPL - original licence link has changed is not relivant.
12314  *
12315  * Fork - LGPL
12316  * <script type="text/javascript">
12317  */
12318
12319 /**
12320  * @class Roo.data.JsonReader
12321  * @extends Roo.data.DataReader
12322  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12323  * based on mappings in a provided Roo.data.Record constructor.
12324  * 
12325  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12326  * in the reply previously. 
12327  * 
12328  * <p>
12329  * Example code:
12330  * <pre><code>
12331 var RecordDef = Roo.data.Record.create([
12332     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12333     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12334 ]);
12335 var myReader = new Roo.data.JsonReader({
12336     totalProperty: "results",    // The property which contains the total dataset size (optional)
12337     root: "rows",                // The property which contains an Array of row objects
12338     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12339 }, RecordDef);
12340 </code></pre>
12341  * <p>
12342  * This would consume a JSON file like this:
12343  * <pre><code>
12344 { 'results': 2, 'rows': [
12345     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12346     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12347 }
12348 </code></pre>
12349  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12350  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12351  * paged from the remote server.
12352  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12353  * @cfg {String} root name of the property which contains the Array of row objects.
12354  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12355  * @cfg {Array} fields Array of field definition objects
12356  * @constructor
12357  * Create a new JsonReader
12358  * @param {Object} meta Metadata configuration options
12359  * @param {Object} recordType Either an Array of field definition objects,
12360  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12361  */
12362 Roo.data.JsonReader = function(meta, recordType){
12363     
12364     meta = meta || {};
12365     // set some defaults:
12366     Roo.applyIf(meta, {
12367         totalProperty: 'total',
12368         successProperty : 'success',
12369         root : 'data',
12370         id : 'id'
12371     });
12372     
12373     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12374 };
12375 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12376     
12377     /**
12378      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12379      * Used by Store query builder to append _requestMeta to params.
12380      * 
12381      */
12382     metaFromRemote : false,
12383     /**
12384      * This method is only used by a DataProxy which has retrieved data from a remote server.
12385      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12386      * @return {Object} data A data block which is used by an Roo.data.Store object as
12387      * a cache of Roo.data.Records.
12388      */
12389     read : function(response){
12390         var json = response.responseText;
12391        
12392         var o = /* eval:var:o */ eval("("+json+")");
12393         if(!o) {
12394             throw {message: "JsonReader.read: Json object not found"};
12395         }
12396         
12397         if(o.metaData){
12398             
12399             delete this.ef;
12400             this.metaFromRemote = true;
12401             this.meta = o.metaData;
12402             this.recordType = Roo.data.Record.create(o.metaData.fields);
12403             this.onMetaChange(this.meta, this.recordType, o);
12404         }
12405         return this.readRecords(o);
12406     },
12407
12408     // private function a store will implement
12409     onMetaChange : function(meta, recordType, o){
12410
12411     },
12412
12413     /**
12414          * @ignore
12415          */
12416     simpleAccess: function(obj, subsc) {
12417         return obj[subsc];
12418     },
12419
12420         /**
12421          * @ignore
12422          */
12423     getJsonAccessor: function(){
12424         var re = /[\[\.]/;
12425         return function(expr) {
12426             try {
12427                 return(re.test(expr))
12428                     ? new Function("obj", "return obj." + expr)
12429                     : function(obj){
12430                         return obj[expr];
12431                     };
12432             } catch(e){}
12433             return Roo.emptyFn;
12434         };
12435     }(),
12436
12437     /**
12438      * Create a data block containing Roo.data.Records from an XML document.
12439      * @param {Object} o An object which contains an Array of row objects in the property specified
12440      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12441      * which contains the total size of the dataset.
12442      * @return {Object} data A data block which is used by an Roo.data.Store object as
12443      * a cache of Roo.data.Records.
12444      */
12445     readRecords : function(o){
12446         /**
12447          * After any data loads, the raw JSON data is available for further custom processing.
12448          * @type Object
12449          */
12450         this.o = o;
12451         var s = this.meta, Record = this.recordType,
12452             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12453
12454 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12455         if (!this.ef) {
12456             if(s.totalProperty) {
12457                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12458                 }
12459                 if(s.successProperty) {
12460                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12461                 }
12462                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12463                 if (s.id) {
12464                         var g = this.getJsonAccessor(s.id);
12465                         this.getId = function(rec) {
12466                                 var r = g(rec);  
12467                                 return (r === undefined || r === "") ? null : r;
12468                         };
12469                 } else {
12470                         this.getId = function(){return null;};
12471                 }
12472             this.ef = [];
12473             for(var jj = 0; jj < fl; jj++){
12474                 f = fi[jj];
12475                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12476                 this.ef[jj] = this.getJsonAccessor(map);
12477             }
12478         }
12479
12480         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12481         if(s.totalProperty){
12482             var vt = parseInt(this.getTotal(o), 10);
12483             if(!isNaN(vt)){
12484                 totalRecords = vt;
12485             }
12486         }
12487         if(s.successProperty){
12488             var vs = this.getSuccess(o);
12489             if(vs === false || vs === 'false'){
12490                 success = false;
12491             }
12492         }
12493         var records = [];
12494         for(var i = 0; i < c; i++){
12495                 var n = root[i];
12496             var values = {};
12497             var id = this.getId(n);
12498             for(var j = 0; j < fl; j++){
12499                 f = fi[j];
12500             var v = this.ef[j](n);
12501             if (!f.convert) {
12502                 Roo.log('missing convert for ' + f.name);
12503                 Roo.log(f);
12504                 continue;
12505             }
12506             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12507             }
12508             var record = new Record(values, id);
12509             record.json = n;
12510             records[i] = record;
12511         }
12512         return {
12513             raw : o,
12514             success : success,
12515             records : records,
12516             totalRecords : totalRecords
12517         };
12518     }
12519 });/*
12520  * Based on:
12521  * Ext JS Library 1.1.1
12522  * Copyright(c) 2006-2007, Ext JS, LLC.
12523  *
12524  * Originally Released Under LGPL - original licence link has changed is not relivant.
12525  *
12526  * Fork - LGPL
12527  * <script type="text/javascript">
12528  */
12529
12530 /**
12531  * @class Roo.data.ArrayReader
12532  * @extends Roo.data.DataReader
12533  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12534  * Each element of that Array represents a row of data fields. The
12535  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12536  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12537  * <p>
12538  * Example code:.
12539  * <pre><code>
12540 var RecordDef = Roo.data.Record.create([
12541     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12542     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12543 ]);
12544 var myReader = new Roo.data.ArrayReader({
12545     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12546 }, RecordDef);
12547 </code></pre>
12548  * <p>
12549  * This would consume an Array like this:
12550  * <pre><code>
12551 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12552   </code></pre>
12553  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12554  * @constructor
12555  * Create a new JsonReader
12556  * @param {Object} meta Metadata configuration options.
12557  * @param {Object} recordType Either an Array of field definition objects
12558  * as specified to {@link Roo.data.Record#create},
12559  * or an {@link Roo.data.Record} object
12560  * created using {@link Roo.data.Record#create}.
12561  */
12562 Roo.data.ArrayReader = function(meta, recordType){
12563     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12564 };
12565
12566 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12567     /**
12568      * Create a data block containing Roo.data.Records from an XML document.
12569      * @param {Object} o An Array of row objects which represents the dataset.
12570      * @return {Object} data A data block which is used by an Roo.data.Store object as
12571      * a cache of Roo.data.Records.
12572      */
12573     readRecords : function(o){
12574         var sid = this.meta ? this.meta.id : null;
12575         var recordType = this.recordType, fields = recordType.prototype.fields;
12576         var records = [];
12577         var root = o;
12578             for(var i = 0; i < root.length; i++){
12579                     var n = root[i];
12580                 var values = {};
12581                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12582                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12583                 var f = fields.items[j];
12584                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12585                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12586                 v = f.convert(v);
12587                 values[f.name] = v;
12588             }
12589                 var record = new recordType(values, id);
12590                 record.json = n;
12591                 records[records.length] = record;
12592             }
12593             return {
12594                 records : records,
12595                 totalRecords : records.length
12596             };
12597     }
12598 });/*
12599  * - LGPL
12600  * * 
12601  */
12602
12603 /**
12604  * @class Roo.bootstrap.ComboBox
12605  * @extends Roo.bootstrap.TriggerField
12606  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12607  * @cfg {Boolean} append (true|false) default false
12608  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12609  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12610  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12611  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12612  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12613  * @cfg {Boolean} animate default true
12614  * @cfg {Boolean} emptyResultText only for touch device
12615  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12616  * @cfg {String} emptyTitle default ''
12617  * @constructor
12618  * Create a new ComboBox.
12619  * @param {Object} config Configuration options
12620  */
12621 Roo.bootstrap.ComboBox = function(config){
12622     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12623     this.addEvents({
12624         /**
12625          * @event expand
12626          * Fires when the dropdown list is expanded
12627         * @param {Roo.bootstrap.ComboBox} combo This combo box
12628         */
12629         'expand' : true,
12630         /**
12631          * @event collapse
12632          * Fires when the dropdown list is collapsed
12633         * @param {Roo.bootstrap.ComboBox} combo This combo box
12634         */
12635         'collapse' : true,
12636         /**
12637          * @event beforeselect
12638          * Fires before a list item is selected. Return false to cancel the selection.
12639         * @param {Roo.bootstrap.ComboBox} combo This combo box
12640         * @param {Roo.data.Record} record The data record returned from the underlying store
12641         * @param {Number} index The index of the selected item in the dropdown list
12642         */
12643         'beforeselect' : true,
12644         /**
12645          * @event select
12646          * Fires when a list item is selected
12647         * @param {Roo.bootstrap.ComboBox} combo This combo box
12648         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12649         * @param {Number} index The index of the selected item in the dropdown list
12650         */
12651         'select' : true,
12652         /**
12653          * @event beforequery
12654          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12655          * The event object passed has these properties:
12656         * @param {Roo.bootstrap.ComboBox} combo This combo box
12657         * @param {String} query The query
12658         * @param {Boolean} forceAll true to force "all" query
12659         * @param {Boolean} cancel true to cancel the query
12660         * @param {Object} e The query event object
12661         */
12662         'beforequery': true,
12663          /**
12664          * @event add
12665          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12666         * @param {Roo.bootstrap.ComboBox} combo This combo box
12667         */
12668         'add' : true,
12669         /**
12670          * @event edit
12671          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12672         * @param {Roo.bootstrap.ComboBox} combo This combo box
12673         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12674         */
12675         'edit' : true,
12676         /**
12677          * @event remove
12678          * Fires when the remove value from the combobox array
12679         * @param {Roo.bootstrap.ComboBox} combo This combo box
12680         */
12681         'remove' : true,
12682         /**
12683          * @event afterremove
12684          * Fires when the remove value from the combobox array
12685         * @param {Roo.bootstrap.ComboBox} combo This combo box
12686         */
12687         'afterremove' : true,
12688         /**
12689          * @event specialfilter
12690          * Fires when specialfilter
12691             * @param {Roo.bootstrap.ComboBox} combo This combo box
12692             */
12693         'specialfilter' : true,
12694         /**
12695          * @event tick
12696          * Fires when tick the element
12697             * @param {Roo.bootstrap.ComboBox} combo This combo box
12698             */
12699         'tick' : true,
12700         /**
12701          * @event touchviewdisplay
12702          * Fires when touch view require special display (default is using displayField)
12703             * @param {Roo.bootstrap.ComboBox} combo This combo box
12704             * @param {Object} cfg set html .
12705             */
12706         'touchviewdisplay' : true
12707         
12708     });
12709     
12710     this.item = [];
12711     this.tickItems = [];
12712     
12713     this.selectedIndex = -1;
12714     if(this.mode == 'local'){
12715         if(config.queryDelay === undefined){
12716             this.queryDelay = 10;
12717         }
12718         if(config.minChars === undefined){
12719             this.minChars = 0;
12720         }
12721     }
12722 };
12723
12724 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12725      
12726     /**
12727      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12728      * rendering into an Roo.Editor, defaults to false)
12729      */
12730     /**
12731      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12732      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12733      */
12734     /**
12735      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12736      */
12737     /**
12738      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12739      * the dropdown list (defaults to undefined, with no header element)
12740      */
12741
12742      /**
12743      * @cfg {String/Roo.Template} tpl The template to use to render the output
12744      */
12745      
12746      /**
12747      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12748      */
12749     listWidth: undefined,
12750     /**
12751      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12752      * mode = 'remote' or 'text' if mode = 'local')
12753      */
12754     displayField: undefined,
12755     
12756     /**
12757      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12758      * mode = 'remote' or 'value' if mode = 'local'). 
12759      * Note: use of a valueField requires the user make a selection
12760      * in order for a value to be mapped.
12761      */
12762     valueField: undefined,
12763     /**
12764      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12765      */
12766     modalTitle : '',
12767     
12768     /**
12769      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12770      * field's data value (defaults to the underlying DOM element's name)
12771      */
12772     hiddenName: undefined,
12773     /**
12774      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12775      */
12776     listClass: '',
12777     /**
12778      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12779      */
12780     selectedClass: 'active',
12781     
12782     /**
12783      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12784      */
12785     shadow:'sides',
12786     /**
12787      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12788      * anchor positions (defaults to 'tl-bl')
12789      */
12790     listAlign: 'tl-bl?',
12791     /**
12792      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12793      */
12794     maxHeight: 300,
12795     /**
12796      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12797      * query specified by the allQuery config option (defaults to 'query')
12798      */
12799     triggerAction: 'query',
12800     /**
12801      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12802      * (defaults to 4, does not apply if editable = false)
12803      */
12804     minChars : 4,
12805     /**
12806      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12807      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12808      */
12809     typeAhead: false,
12810     /**
12811      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12812      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12813      */
12814     queryDelay: 500,
12815     /**
12816      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12817      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12818      */
12819     pageSize: 0,
12820     /**
12821      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12822      * when editable = true (defaults to false)
12823      */
12824     selectOnFocus:false,
12825     /**
12826      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12827      */
12828     queryParam: 'query',
12829     /**
12830      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12831      * when mode = 'remote' (defaults to 'Loading...')
12832      */
12833     loadingText: 'Loading...',
12834     /**
12835      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12836      */
12837     resizable: false,
12838     /**
12839      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12840      */
12841     handleHeight : 8,
12842     /**
12843      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12844      * traditional select (defaults to true)
12845      */
12846     editable: true,
12847     /**
12848      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12849      */
12850     allQuery: '',
12851     /**
12852      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12853      */
12854     mode: 'remote',
12855     /**
12856      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12857      * listWidth has a higher value)
12858      */
12859     minListWidth : 70,
12860     /**
12861      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12862      * allow the user to set arbitrary text into the field (defaults to false)
12863      */
12864     forceSelection:false,
12865     /**
12866      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12867      * if typeAhead = true (defaults to 250)
12868      */
12869     typeAheadDelay : 250,
12870     /**
12871      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12872      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12873      */
12874     valueNotFoundText : undefined,
12875     /**
12876      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12877      */
12878     blockFocus : false,
12879     
12880     /**
12881      * @cfg {Boolean} disableClear Disable showing of clear button.
12882      */
12883     disableClear : false,
12884     /**
12885      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12886      */
12887     alwaysQuery : false,
12888     
12889     /**
12890      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12891      */
12892     multiple : false,
12893     
12894     /**
12895      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12896      */
12897     invalidClass : "has-warning",
12898     
12899     /**
12900      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12901      */
12902     validClass : "has-success",
12903     
12904     /**
12905      * @cfg {Boolean} specialFilter (true|false) special filter default false
12906      */
12907     specialFilter : false,
12908     
12909     /**
12910      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12911      */
12912     mobileTouchView : true,
12913     
12914     /**
12915      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12916      */
12917     useNativeIOS : false,
12918     
12919     ios_options : false,
12920     
12921     //private
12922     addicon : false,
12923     editicon: false,
12924     
12925     page: 0,
12926     hasQuery: false,
12927     append: false,
12928     loadNext: false,
12929     autoFocus : true,
12930     tickable : false,
12931     btnPosition : 'right',
12932     triggerList : true,
12933     showToggleBtn : true,
12934     animate : true,
12935     emptyResultText: 'Empty',
12936     triggerText : 'Select',
12937     emptyTitle : '',
12938     
12939     // element that contains real text value.. (when hidden is used..)
12940     
12941     getAutoCreate : function()
12942     {   
12943         var cfg = false;
12944         //render
12945         /*
12946          * Render classic select for iso
12947          */
12948         
12949         if(Roo.isIOS && this.useNativeIOS){
12950             cfg = this.getAutoCreateNativeIOS();
12951             return cfg;
12952         }
12953         
12954         /*
12955          * Touch Devices
12956          */
12957         
12958         if(Roo.isTouch && this.mobileTouchView){
12959             cfg = this.getAutoCreateTouchView();
12960             return cfg;;
12961         }
12962         
12963         /*
12964          *  Normal ComboBox
12965          */
12966         if(!this.tickable){
12967             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12968             return cfg;
12969         }
12970         
12971         /*
12972          *  ComboBox with tickable selections
12973          */
12974              
12975         var align = this.labelAlign || this.parentLabelAlign();
12976         
12977         cfg = {
12978             cls : 'form-group roo-combobox-tickable' //input-group
12979         };
12980         
12981         var btn_text_select = '';
12982         var btn_text_done = '';
12983         var btn_text_cancel = '';
12984         
12985         if (this.btn_text_show) {
12986             btn_text_select = 'Select';
12987             btn_text_done = 'Done';
12988             btn_text_cancel = 'Cancel'; 
12989         }
12990         
12991         var buttons = {
12992             tag : 'div',
12993             cls : 'tickable-buttons',
12994             cn : [
12995                 {
12996                     tag : 'button',
12997                     type : 'button',
12998                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12999                     //html : this.triggerText
13000                     html: btn_text_select
13001                 },
13002                 {
13003                     tag : 'button',
13004                     type : 'button',
13005                     name : 'ok',
13006                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13007                     //html : 'Done'
13008                     html: btn_text_done
13009                 },
13010                 {
13011                     tag : 'button',
13012                     type : 'button',
13013                     name : 'cancel',
13014                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13015                     //html : 'Cancel'
13016                     html: btn_text_cancel
13017                 }
13018             ]
13019         };
13020         
13021         if(this.editable){
13022             buttons.cn.unshift({
13023                 tag: 'input',
13024                 cls: 'roo-select2-search-field-input'
13025             });
13026         }
13027         
13028         var _this = this;
13029         
13030         Roo.each(buttons.cn, function(c){
13031             if (_this.size) {
13032                 c.cls += ' btn-' + _this.size;
13033             }
13034
13035             if (_this.disabled) {
13036                 c.disabled = true;
13037             }
13038         });
13039         
13040         var box = {
13041             tag: 'div',
13042             cn: [
13043                 {
13044                     tag: 'input',
13045                     type : 'hidden',
13046                     cls: 'form-hidden-field'
13047                 },
13048                 {
13049                     tag: 'ul',
13050                     cls: 'roo-select2-choices',
13051                     cn:[
13052                         {
13053                             tag: 'li',
13054                             cls: 'roo-select2-search-field',
13055                             cn: [
13056                                 buttons
13057                             ]
13058                         }
13059                     ]
13060                 }
13061             ]
13062         };
13063         
13064         var combobox = {
13065             cls: 'roo-select2-container input-group roo-select2-container-multi',
13066             cn: [
13067                 box
13068 //                {
13069 //                    tag: 'ul',
13070 //                    cls: 'typeahead typeahead-long dropdown-menu',
13071 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13072 //                }
13073             ]
13074         };
13075         
13076         if(this.hasFeedback && !this.allowBlank){
13077             
13078             var feedback = {
13079                 tag: 'span',
13080                 cls: 'glyphicon form-control-feedback'
13081             };
13082
13083             combobox.cn.push(feedback);
13084         }
13085         
13086         
13087         if (align ==='left' && this.fieldLabel.length) {
13088             
13089             cfg.cls += ' roo-form-group-label-left';
13090             
13091             cfg.cn = [
13092                 {
13093                     tag : 'i',
13094                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13095                     tooltip : 'This field is required'
13096                 },
13097                 {
13098                     tag: 'label',
13099                     'for' :  id,
13100                     cls : 'control-label',
13101                     html : this.fieldLabel
13102
13103                 },
13104                 {
13105                     cls : "", 
13106                     cn: [
13107                         combobox
13108                     ]
13109                 }
13110
13111             ];
13112             
13113             var labelCfg = cfg.cn[1];
13114             var contentCfg = cfg.cn[2];
13115             
13116
13117             if(this.indicatorpos == 'right'){
13118                 
13119                 cfg.cn = [
13120                     {
13121                         tag: 'label',
13122                         'for' :  id,
13123                         cls : 'control-label',
13124                         cn : [
13125                             {
13126                                 tag : 'span',
13127                                 html : this.fieldLabel
13128                             },
13129                             {
13130                                 tag : 'i',
13131                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13132                                 tooltip : 'This field is required'
13133                             }
13134                         ]
13135                     },
13136                     {
13137                         cls : "",
13138                         cn: [
13139                             combobox
13140                         ]
13141                     }
13142
13143                 ];
13144                 
13145                 
13146                 
13147                 labelCfg = cfg.cn[0];
13148                 contentCfg = cfg.cn[1];
13149             
13150             }
13151             
13152             if(this.labelWidth > 12){
13153                 labelCfg.style = "width: " + this.labelWidth + 'px';
13154             }
13155             
13156             if(this.labelWidth < 13 && this.labelmd == 0){
13157                 this.labelmd = this.labelWidth;
13158             }
13159             
13160             if(this.labellg > 0){
13161                 labelCfg.cls += ' col-lg-' + this.labellg;
13162                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13163             }
13164             
13165             if(this.labelmd > 0){
13166                 labelCfg.cls += ' col-md-' + this.labelmd;
13167                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13168             }
13169             
13170             if(this.labelsm > 0){
13171                 labelCfg.cls += ' col-sm-' + this.labelsm;
13172                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13173             }
13174             
13175             if(this.labelxs > 0){
13176                 labelCfg.cls += ' col-xs-' + this.labelxs;
13177                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13178             }
13179                 
13180                 
13181         } else if ( this.fieldLabel.length) {
13182 //                Roo.log(" label");
13183                  cfg.cn = [
13184                     {
13185                         tag : 'i',
13186                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13187                         tooltip : 'This field is required'
13188                     },
13189                     {
13190                         tag: 'label',
13191                         //cls : 'input-group-addon',
13192                         html : this.fieldLabel
13193                     },
13194                     combobox
13195                 ];
13196                 
13197                 if(this.indicatorpos == 'right'){
13198                     cfg.cn = [
13199                         {
13200                             tag: 'label',
13201                             //cls : 'input-group-addon',
13202                             html : this.fieldLabel
13203                         },
13204                         {
13205                             tag : 'i',
13206                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13207                             tooltip : 'This field is required'
13208                         },
13209                         combobox
13210                     ];
13211                     
13212                 }
13213
13214         } else {
13215             
13216 //                Roo.log(" no label && no align");
13217                 cfg = combobox
13218                      
13219                 
13220         }
13221          
13222         var settings=this;
13223         ['xs','sm','md','lg'].map(function(size){
13224             if (settings[size]) {
13225                 cfg.cls += ' col-' + size + '-' + settings[size];
13226             }
13227         });
13228         
13229         return cfg;
13230         
13231     },
13232     
13233     _initEventsCalled : false,
13234     
13235     // private
13236     initEvents: function()
13237     {   
13238         if (this._initEventsCalled) { // as we call render... prevent looping...
13239             return;
13240         }
13241         this._initEventsCalled = true;
13242         
13243         if (!this.store) {
13244             throw "can not find store for combo";
13245         }
13246         
13247         this.indicator = this.indicatorEl();
13248         
13249         this.store = Roo.factory(this.store, Roo.data);
13250         this.store.parent = this;
13251         
13252         // if we are building from html. then this element is so complex, that we can not really
13253         // use the rendered HTML.
13254         // so we have to trash and replace the previous code.
13255         if (Roo.XComponent.build_from_html) {
13256             // remove this element....
13257             var e = this.el.dom, k=0;
13258             while (e ) { e = e.previousSibling;  ++k;}
13259
13260             this.el.remove();
13261             
13262             this.el=false;
13263             this.rendered = false;
13264             
13265             this.render(this.parent().getChildContainer(true), k);
13266         }
13267         
13268         if(Roo.isIOS && this.useNativeIOS){
13269             this.initIOSView();
13270             return;
13271         }
13272         
13273         /*
13274          * Touch Devices
13275          */
13276         
13277         if(Roo.isTouch && this.mobileTouchView){
13278             this.initTouchView();
13279             return;
13280         }
13281         
13282         if(this.tickable){
13283             this.initTickableEvents();
13284             return;
13285         }
13286         
13287         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13288         
13289         if(this.hiddenName){
13290             
13291             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13292             
13293             this.hiddenField.dom.value =
13294                 this.hiddenValue !== undefined ? this.hiddenValue :
13295                 this.value !== undefined ? this.value : '';
13296
13297             // prevent input submission
13298             this.el.dom.removeAttribute('name');
13299             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13300              
13301              
13302         }
13303         //if(Roo.isGecko){
13304         //    this.el.dom.setAttribute('autocomplete', 'off');
13305         //}
13306         
13307         var cls = 'x-combo-list';
13308         
13309         //this.list = new Roo.Layer({
13310         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13311         //});
13312         
13313         var _this = this;
13314         
13315         (function(){
13316             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13317             _this.list.setWidth(lw);
13318         }).defer(100);
13319         
13320         this.list.on('mouseover', this.onViewOver, this);
13321         this.list.on('mousemove', this.onViewMove, this);
13322         this.list.on('scroll', this.onViewScroll, this);
13323         
13324         /*
13325         this.list.swallowEvent('mousewheel');
13326         this.assetHeight = 0;
13327
13328         if(this.title){
13329             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13330             this.assetHeight += this.header.getHeight();
13331         }
13332
13333         this.innerList = this.list.createChild({cls:cls+'-inner'});
13334         this.innerList.on('mouseover', this.onViewOver, this);
13335         this.innerList.on('mousemove', this.onViewMove, this);
13336         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13337         
13338         if(this.allowBlank && !this.pageSize && !this.disableClear){
13339             this.footer = this.list.createChild({cls:cls+'-ft'});
13340             this.pageTb = new Roo.Toolbar(this.footer);
13341            
13342         }
13343         if(this.pageSize){
13344             this.footer = this.list.createChild({cls:cls+'-ft'});
13345             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13346                     {pageSize: this.pageSize});
13347             
13348         }
13349         
13350         if (this.pageTb && this.allowBlank && !this.disableClear) {
13351             var _this = this;
13352             this.pageTb.add(new Roo.Toolbar.Fill(), {
13353                 cls: 'x-btn-icon x-btn-clear',
13354                 text: '&#160;',
13355                 handler: function()
13356                 {
13357                     _this.collapse();
13358                     _this.clearValue();
13359                     _this.onSelect(false, -1);
13360                 }
13361             });
13362         }
13363         if (this.footer) {
13364             this.assetHeight += this.footer.getHeight();
13365         }
13366         */
13367             
13368         if(!this.tpl){
13369             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13370         }
13371
13372         this.view = new Roo.View(this.list, this.tpl, {
13373             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13374         });
13375         //this.view.wrapEl.setDisplayed(false);
13376         this.view.on('click', this.onViewClick, this);
13377         
13378         
13379         this.store.on('beforeload', this.onBeforeLoad, this);
13380         this.store.on('load', this.onLoad, this);
13381         this.store.on('loadexception', this.onLoadException, this);
13382         /*
13383         if(this.resizable){
13384             this.resizer = new Roo.Resizable(this.list,  {
13385                pinned:true, handles:'se'
13386             });
13387             this.resizer.on('resize', function(r, w, h){
13388                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13389                 this.listWidth = w;
13390                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13391                 this.restrictHeight();
13392             }, this);
13393             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13394         }
13395         */
13396         if(!this.editable){
13397             this.editable = true;
13398             this.setEditable(false);
13399         }
13400         
13401         /*
13402         
13403         if (typeof(this.events.add.listeners) != 'undefined') {
13404             
13405             this.addicon = this.wrap.createChild(
13406                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13407        
13408             this.addicon.on('click', function(e) {
13409                 this.fireEvent('add', this);
13410             }, this);
13411         }
13412         if (typeof(this.events.edit.listeners) != 'undefined') {
13413             
13414             this.editicon = this.wrap.createChild(
13415                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13416             if (this.addicon) {
13417                 this.editicon.setStyle('margin-left', '40px');
13418             }
13419             this.editicon.on('click', function(e) {
13420                 
13421                 // we fire even  if inothing is selected..
13422                 this.fireEvent('edit', this, this.lastData );
13423                 
13424             }, this);
13425         }
13426         */
13427         
13428         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13429             "up" : function(e){
13430                 this.inKeyMode = true;
13431                 this.selectPrev();
13432             },
13433
13434             "down" : function(e){
13435                 if(!this.isExpanded()){
13436                     this.onTriggerClick();
13437                 }else{
13438                     this.inKeyMode = true;
13439                     this.selectNext();
13440                 }
13441             },
13442
13443             "enter" : function(e){
13444 //                this.onViewClick();
13445                 //return true;
13446                 this.collapse();
13447                 
13448                 if(this.fireEvent("specialkey", this, e)){
13449                     this.onViewClick(false);
13450                 }
13451                 
13452                 return true;
13453             },
13454
13455             "esc" : function(e){
13456                 this.collapse();
13457             },
13458
13459             "tab" : function(e){
13460                 this.collapse();
13461                 
13462                 if(this.fireEvent("specialkey", this, e)){
13463                     this.onViewClick(false);
13464                 }
13465                 
13466                 return true;
13467             },
13468
13469             scope : this,
13470
13471             doRelay : function(foo, bar, hname){
13472                 if(hname == 'down' || this.scope.isExpanded()){
13473                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13474                 }
13475                 return true;
13476             },
13477
13478             forceKeyDown: true
13479         });
13480         
13481         
13482         this.queryDelay = Math.max(this.queryDelay || 10,
13483                 this.mode == 'local' ? 10 : 250);
13484         
13485         
13486         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13487         
13488         if(this.typeAhead){
13489             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13490         }
13491         if(this.editable !== false){
13492             this.inputEl().on("keyup", this.onKeyUp, this);
13493         }
13494         if(this.forceSelection){
13495             this.inputEl().on('blur', this.doForce, this);
13496         }
13497         
13498         if(this.multiple){
13499             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13500             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13501         }
13502     },
13503     
13504     initTickableEvents: function()
13505     {   
13506         this.createList();
13507         
13508         if(this.hiddenName){
13509             
13510             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13511             
13512             this.hiddenField.dom.value =
13513                 this.hiddenValue !== undefined ? this.hiddenValue :
13514                 this.value !== undefined ? this.value : '';
13515
13516             // prevent input submission
13517             this.el.dom.removeAttribute('name');
13518             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13519              
13520              
13521         }
13522         
13523 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13524         
13525         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13526         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13527         if(this.triggerList){
13528             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13529         }
13530          
13531         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13532         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13533         
13534         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13535         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13536         
13537         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13538         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13539         
13540         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13541         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13542         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13543         
13544         this.okBtn.hide();
13545         this.cancelBtn.hide();
13546         
13547         var _this = this;
13548         
13549         (function(){
13550             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13551             _this.list.setWidth(lw);
13552         }).defer(100);
13553         
13554         this.list.on('mouseover', this.onViewOver, this);
13555         this.list.on('mousemove', this.onViewMove, this);
13556         
13557         this.list.on('scroll', this.onViewScroll, this);
13558         
13559         if(!this.tpl){
13560             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>';
13561         }
13562
13563         this.view = new Roo.View(this.list, this.tpl, {
13564             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13565         });
13566         
13567         //this.view.wrapEl.setDisplayed(false);
13568         this.view.on('click', this.onViewClick, this);
13569         
13570         
13571         
13572         this.store.on('beforeload', this.onBeforeLoad, this);
13573         this.store.on('load', this.onLoad, this);
13574         this.store.on('loadexception', this.onLoadException, this);
13575         
13576         if(this.editable){
13577             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13578                 "up" : function(e){
13579                     this.inKeyMode = true;
13580                     this.selectPrev();
13581                 },
13582
13583                 "down" : function(e){
13584                     this.inKeyMode = true;
13585                     this.selectNext();
13586                 },
13587
13588                 "enter" : function(e){
13589                     if(this.fireEvent("specialkey", this, e)){
13590                         this.onViewClick(false);
13591                     }
13592                     
13593                     return true;
13594                 },
13595
13596                 "esc" : function(e){
13597                     this.onTickableFooterButtonClick(e, false, false);
13598                 },
13599
13600                 "tab" : function(e){
13601                     this.fireEvent("specialkey", this, e);
13602                     
13603                     this.onTickableFooterButtonClick(e, false, false);
13604                     
13605                     return true;
13606                 },
13607
13608                 scope : this,
13609
13610                 doRelay : function(e, fn, key){
13611                     if(this.scope.isExpanded()){
13612                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13613                     }
13614                     return true;
13615                 },
13616
13617                 forceKeyDown: true
13618             });
13619         }
13620         
13621         this.queryDelay = Math.max(this.queryDelay || 10,
13622                 this.mode == 'local' ? 10 : 250);
13623         
13624         
13625         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13626         
13627         if(this.typeAhead){
13628             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13629         }
13630         
13631         if(this.editable !== false){
13632             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13633         }
13634         
13635         this.indicator = this.indicatorEl();
13636         
13637         if(this.indicator){
13638             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13639             this.indicator.hide();
13640         }
13641         
13642     },
13643
13644     onDestroy : function(){
13645         if(this.view){
13646             this.view.setStore(null);
13647             this.view.el.removeAllListeners();
13648             this.view.el.remove();
13649             this.view.purgeListeners();
13650         }
13651         if(this.list){
13652             this.list.dom.innerHTML  = '';
13653         }
13654         
13655         if(this.store){
13656             this.store.un('beforeload', this.onBeforeLoad, this);
13657             this.store.un('load', this.onLoad, this);
13658             this.store.un('loadexception', this.onLoadException, this);
13659         }
13660         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13661     },
13662
13663     // private
13664     fireKey : function(e){
13665         if(e.isNavKeyPress() && !this.list.isVisible()){
13666             this.fireEvent("specialkey", this, e);
13667         }
13668     },
13669
13670     // private
13671     onResize: function(w, h){
13672 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13673 //        
13674 //        if(typeof w != 'number'){
13675 //            // we do not handle it!?!?
13676 //            return;
13677 //        }
13678 //        var tw = this.trigger.getWidth();
13679 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13680 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13681 //        var x = w - tw;
13682 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13683 //            
13684 //        //this.trigger.setStyle('left', x+'px');
13685 //        
13686 //        if(this.list && this.listWidth === undefined){
13687 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13688 //            this.list.setWidth(lw);
13689 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13690 //        }
13691         
13692     
13693         
13694     },
13695
13696     /**
13697      * Allow or prevent the user from directly editing the field text.  If false is passed,
13698      * the user will only be able to select from the items defined in the dropdown list.  This method
13699      * is the runtime equivalent of setting the 'editable' config option at config time.
13700      * @param {Boolean} value True to allow the user to directly edit the field text
13701      */
13702     setEditable : function(value){
13703         if(value == this.editable){
13704             return;
13705         }
13706         this.editable = value;
13707         if(!value){
13708             this.inputEl().dom.setAttribute('readOnly', true);
13709             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13710             this.inputEl().addClass('x-combo-noedit');
13711         }else{
13712             this.inputEl().dom.setAttribute('readOnly', false);
13713             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13714             this.inputEl().removeClass('x-combo-noedit');
13715         }
13716     },
13717
13718     // private
13719     
13720     onBeforeLoad : function(combo,opts){
13721         if(!this.hasFocus){
13722             return;
13723         }
13724          if (!opts.add) {
13725             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13726          }
13727         this.restrictHeight();
13728         this.selectedIndex = -1;
13729     },
13730
13731     // private
13732     onLoad : function(){
13733         
13734         this.hasQuery = false;
13735         
13736         if(!this.hasFocus){
13737             return;
13738         }
13739         
13740         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13741             this.loading.hide();
13742         }
13743         
13744         if(this.store.getCount() > 0){
13745             
13746             this.expand();
13747             this.restrictHeight();
13748             if(this.lastQuery == this.allQuery){
13749                 if(this.editable && !this.tickable){
13750                     this.inputEl().dom.select();
13751                 }
13752                 
13753                 if(
13754                     !this.selectByValue(this.value, true) &&
13755                     this.autoFocus && 
13756                     (
13757                         !this.store.lastOptions ||
13758                         typeof(this.store.lastOptions.add) == 'undefined' || 
13759                         this.store.lastOptions.add != true
13760                     )
13761                 ){
13762                     this.select(0, true);
13763                 }
13764             }else{
13765                 if(this.autoFocus){
13766                     this.selectNext();
13767                 }
13768                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13769                     this.taTask.delay(this.typeAheadDelay);
13770                 }
13771             }
13772         }else{
13773             this.onEmptyResults();
13774         }
13775         
13776         //this.el.focus();
13777     },
13778     // private
13779     onLoadException : function()
13780     {
13781         this.hasQuery = false;
13782         
13783         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13784             this.loading.hide();
13785         }
13786         
13787         if(this.tickable && this.editable){
13788             return;
13789         }
13790         
13791         this.collapse();
13792         // only causes errors at present
13793         //Roo.log(this.store.reader.jsonData);
13794         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13795             // fixme
13796             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13797         //}
13798         
13799         
13800     },
13801     // private
13802     onTypeAhead : function(){
13803         if(this.store.getCount() > 0){
13804             var r = this.store.getAt(0);
13805             var newValue = r.data[this.displayField];
13806             var len = newValue.length;
13807             var selStart = this.getRawValue().length;
13808             
13809             if(selStart != len){
13810                 this.setRawValue(newValue);
13811                 this.selectText(selStart, newValue.length);
13812             }
13813         }
13814     },
13815
13816     // private
13817     onSelect : function(record, index){
13818         
13819         if(this.fireEvent('beforeselect', this, record, index) !== false){
13820         
13821             this.setFromData(index > -1 ? record.data : false);
13822             
13823             this.collapse();
13824             this.fireEvent('select', this, record, index);
13825         }
13826     },
13827
13828     /**
13829      * Returns the currently selected field value or empty string if no value is set.
13830      * @return {String} value The selected value
13831      */
13832     getValue : function()
13833     {
13834         if(Roo.isIOS && this.useNativeIOS){
13835             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13836         }
13837         
13838         if(this.multiple){
13839             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13840         }
13841         
13842         if(this.valueField){
13843             return typeof this.value != 'undefined' ? this.value : '';
13844         }else{
13845             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13846         }
13847     },
13848     
13849     getRawValue : function()
13850     {
13851         if(Roo.isIOS && this.useNativeIOS){
13852             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13853         }
13854         
13855         var v = this.inputEl().getValue();
13856         
13857         return v;
13858     },
13859
13860     /**
13861      * Clears any text/value currently set in the field
13862      */
13863     clearValue : function(){
13864         
13865         if(this.hiddenField){
13866             this.hiddenField.dom.value = '';
13867         }
13868         this.value = '';
13869         this.setRawValue('');
13870         this.lastSelectionText = '';
13871         this.lastData = false;
13872         
13873         var close = this.closeTriggerEl();
13874         
13875         if(close){
13876             close.hide();
13877         }
13878         
13879         this.validate();
13880         
13881     },
13882
13883     /**
13884      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13885      * will be displayed in the field.  If the value does not match the data value of an existing item,
13886      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13887      * Otherwise the field will be blank (although the value will still be set).
13888      * @param {String} value The value to match
13889      */
13890     setValue : function(v)
13891     {
13892         if(Roo.isIOS && this.useNativeIOS){
13893             this.setIOSValue(v);
13894             return;
13895         }
13896         
13897         if(this.multiple){
13898             this.syncValue();
13899             return;
13900         }
13901         
13902         var text = v;
13903         if(this.valueField){
13904             var r = this.findRecord(this.valueField, v);
13905             if(r){
13906                 text = r.data[this.displayField];
13907             }else if(this.valueNotFoundText !== undefined){
13908                 text = this.valueNotFoundText;
13909             }
13910         }
13911         this.lastSelectionText = text;
13912         if(this.hiddenField){
13913             this.hiddenField.dom.value = v;
13914         }
13915         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13916         this.value = v;
13917         
13918         var close = this.closeTriggerEl();
13919         
13920         if(close){
13921             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13922         }
13923         
13924         this.validate();
13925     },
13926     /**
13927      * @property {Object} the last set data for the element
13928      */
13929     
13930     lastData : false,
13931     /**
13932      * Sets the value of the field based on a object which is related to the record format for the store.
13933      * @param {Object} value the value to set as. or false on reset?
13934      */
13935     setFromData : function(o){
13936         
13937         if(this.multiple){
13938             this.addItem(o);
13939             return;
13940         }
13941             
13942         var dv = ''; // display value
13943         var vv = ''; // value value..
13944         this.lastData = o;
13945         if (this.displayField) {
13946             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13947         } else {
13948             // this is an error condition!!!
13949             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13950         }
13951         
13952         if(this.valueField){
13953             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13954         }
13955         
13956         var close = this.closeTriggerEl();
13957         
13958         if(close){
13959             if(dv.length || vv * 1 > 0){
13960                 close.show() ;
13961                 this.blockFocus=true;
13962             } else {
13963                 close.hide();
13964             }             
13965         }
13966         
13967         if(this.hiddenField){
13968             this.hiddenField.dom.value = vv;
13969             
13970             this.lastSelectionText = dv;
13971             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13972             this.value = vv;
13973             return;
13974         }
13975         // no hidden field.. - we store the value in 'value', but still display
13976         // display field!!!!
13977         this.lastSelectionText = dv;
13978         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13979         this.value = vv;
13980         
13981         
13982         
13983     },
13984     // private
13985     reset : function(){
13986         // overridden so that last data is reset..
13987         
13988         if(this.multiple){
13989             this.clearItem();
13990             return;
13991         }
13992         
13993         this.setValue(this.originalValue);
13994         //this.clearInvalid();
13995         this.lastData = false;
13996         if (this.view) {
13997             this.view.clearSelections();
13998         }
13999         
14000         this.validate();
14001     },
14002     // private
14003     findRecord : function(prop, value){
14004         var record;
14005         if(this.store.getCount() > 0){
14006             this.store.each(function(r){
14007                 if(r.data[prop] == value){
14008                     record = r;
14009                     return false;
14010                 }
14011                 return true;
14012             });
14013         }
14014         return record;
14015     },
14016     
14017     getName: function()
14018     {
14019         // returns hidden if it's set..
14020         if (!this.rendered) {return ''};
14021         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14022         
14023     },
14024     // private
14025     onViewMove : function(e, t){
14026         this.inKeyMode = false;
14027     },
14028
14029     // private
14030     onViewOver : function(e, t){
14031         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14032             return;
14033         }
14034         var item = this.view.findItemFromChild(t);
14035         
14036         if(item){
14037             var index = this.view.indexOf(item);
14038             this.select(index, false);
14039         }
14040     },
14041
14042     // private
14043     onViewClick : function(view, doFocus, el, e)
14044     {
14045         var index = this.view.getSelectedIndexes()[0];
14046         
14047         var r = this.store.getAt(index);
14048         
14049         if(this.tickable){
14050             
14051             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14052                 return;
14053             }
14054             
14055             var rm = false;
14056             var _this = this;
14057             
14058             Roo.each(this.tickItems, function(v,k){
14059                 
14060                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14061                     Roo.log(v);
14062                     _this.tickItems.splice(k, 1);
14063                     
14064                     if(typeof(e) == 'undefined' && view == false){
14065                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14066                     }
14067                     
14068                     rm = true;
14069                     return;
14070                 }
14071             });
14072             
14073             if(rm){
14074                 return;
14075             }
14076             
14077             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14078                 this.tickItems.push(r.data);
14079             }
14080             
14081             if(typeof(e) == 'undefined' && view == false){
14082                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14083             }
14084                     
14085             return;
14086         }
14087         
14088         if(r){
14089             this.onSelect(r, index);
14090         }
14091         if(doFocus !== false && !this.blockFocus){
14092             this.inputEl().focus();
14093         }
14094     },
14095
14096     // private
14097     restrictHeight : function(){
14098         //this.innerList.dom.style.height = '';
14099         //var inner = this.innerList.dom;
14100         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14101         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14102         //this.list.beginUpdate();
14103         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14104         this.list.alignTo(this.inputEl(), this.listAlign);
14105         this.list.alignTo(this.inputEl(), this.listAlign);
14106         //this.list.endUpdate();
14107     },
14108
14109     // private
14110     onEmptyResults : function(){
14111         
14112         if(this.tickable && this.editable){
14113             this.hasFocus = false;
14114             this.restrictHeight();
14115             return;
14116         }
14117         
14118         this.collapse();
14119     },
14120
14121     /**
14122      * Returns true if the dropdown list is expanded, else false.
14123      */
14124     isExpanded : function(){
14125         return this.list.isVisible();
14126     },
14127
14128     /**
14129      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14130      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14131      * @param {String} value The data value of the item to select
14132      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14133      * selected item if it is not currently in view (defaults to true)
14134      * @return {Boolean} True if the value matched an item in the list, else false
14135      */
14136     selectByValue : function(v, scrollIntoView){
14137         if(v !== undefined && v !== null){
14138             var r = this.findRecord(this.valueField || this.displayField, v);
14139             if(r){
14140                 this.select(this.store.indexOf(r), scrollIntoView);
14141                 return true;
14142             }
14143         }
14144         return false;
14145     },
14146
14147     /**
14148      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14149      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14150      * @param {Number} index The zero-based index of the list item to select
14151      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14152      * selected item if it is not currently in view (defaults to true)
14153      */
14154     select : function(index, scrollIntoView){
14155         this.selectedIndex = index;
14156         this.view.select(index);
14157         if(scrollIntoView !== false){
14158             var el = this.view.getNode(index);
14159             /*
14160              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14161              */
14162             if(el){
14163                 this.list.scrollChildIntoView(el, false);
14164             }
14165         }
14166     },
14167
14168     // private
14169     selectNext : function(){
14170         var ct = this.store.getCount();
14171         if(ct > 0){
14172             if(this.selectedIndex == -1){
14173                 this.select(0);
14174             }else if(this.selectedIndex < ct-1){
14175                 this.select(this.selectedIndex+1);
14176             }
14177         }
14178     },
14179
14180     // private
14181     selectPrev : function(){
14182         var ct = this.store.getCount();
14183         if(ct > 0){
14184             if(this.selectedIndex == -1){
14185                 this.select(0);
14186             }else if(this.selectedIndex != 0){
14187                 this.select(this.selectedIndex-1);
14188             }
14189         }
14190     },
14191
14192     // private
14193     onKeyUp : function(e){
14194         if(this.editable !== false && !e.isSpecialKey()){
14195             this.lastKey = e.getKey();
14196             this.dqTask.delay(this.queryDelay);
14197         }
14198     },
14199
14200     // private
14201     validateBlur : function(){
14202         return !this.list || !this.list.isVisible();   
14203     },
14204
14205     // private
14206     initQuery : function(){
14207         
14208         var v = this.getRawValue();
14209         
14210         if(this.tickable && this.editable){
14211             v = this.tickableInputEl().getValue();
14212         }
14213         
14214         this.doQuery(v);
14215     },
14216
14217     // private
14218     doForce : function(){
14219         if(this.inputEl().dom.value.length > 0){
14220             this.inputEl().dom.value =
14221                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14222              
14223         }
14224     },
14225
14226     /**
14227      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14228      * query allowing the query action to be canceled if needed.
14229      * @param {String} query The SQL query to execute
14230      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14231      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14232      * saved in the current store (defaults to false)
14233      */
14234     doQuery : function(q, forceAll){
14235         
14236         if(q === undefined || q === null){
14237             q = '';
14238         }
14239         var qe = {
14240             query: q,
14241             forceAll: forceAll,
14242             combo: this,
14243             cancel:false
14244         };
14245         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14246             return false;
14247         }
14248         q = qe.query;
14249         
14250         forceAll = qe.forceAll;
14251         if(forceAll === true || (q.length >= this.minChars)){
14252             
14253             this.hasQuery = true;
14254             
14255             if(this.lastQuery != q || this.alwaysQuery){
14256                 this.lastQuery = q;
14257                 if(this.mode == 'local'){
14258                     this.selectedIndex = -1;
14259                     if(forceAll){
14260                         this.store.clearFilter();
14261                     }else{
14262                         
14263                         if(this.specialFilter){
14264                             this.fireEvent('specialfilter', this);
14265                             this.onLoad();
14266                             return;
14267                         }
14268                         
14269                         this.store.filter(this.displayField, q);
14270                     }
14271                     
14272                     this.store.fireEvent("datachanged", this.store);
14273                     
14274                     this.onLoad();
14275                     
14276                     
14277                 }else{
14278                     
14279                     this.store.baseParams[this.queryParam] = q;
14280                     
14281                     var options = {params : this.getParams(q)};
14282                     
14283                     if(this.loadNext){
14284                         options.add = true;
14285                         options.params.start = this.page * this.pageSize;
14286                     }
14287                     
14288                     this.store.load(options);
14289                     
14290                     /*
14291                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14292                      *  we should expand the list on onLoad
14293                      *  so command out it
14294                      */
14295 //                    this.expand();
14296                 }
14297             }else{
14298                 this.selectedIndex = -1;
14299                 this.onLoad();   
14300             }
14301         }
14302         
14303         this.loadNext = false;
14304     },
14305     
14306     // private
14307     getParams : function(q){
14308         var p = {};
14309         //p[this.queryParam] = q;
14310         
14311         if(this.pageSize){
14312             p.start = 0;
14313             p.limit = this.pageSize;
14314         }
14315         return p;
14316     },
14317
14318     /**
14319      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14320      */
14321     collapse : function(){
14322         if(!this.isExpanded()){
14323             return;
14324         }
14325         
14326         this.list.hide();
14327         
14328         this.hasFocus = false;
14329         
14330         if(this.tickable){
14331             this.okBtn.hide();
14332             this.cancelBtn.hide();
14333             this.trigger.show();
14334             
14335             if(this.editable){
14336                 this.tickableInputEl().dom.value = '';
14337                 this.tickableInputEl().blur();
14338             }
14339             
14340         }
14341         
14342         Roo.get(document).un('mousedown', this.collapseIf, this);
14343         Roo.get(document).un('mousewheel', this.collapseIf, this);
14344         if (!this.editable) {
14345             Roo.get(document).un('keydown', this.listKeyPress, this);
14346         }
14347         this.fireEvent('collapse', this);
14348         
14349         this.validate();
14350     },
14351
14352     // private
14353     collapseIf : function(e){
14354         var in_combo  = e.within(this.el);
14355         var in_list =  e.within(this.list);
14356         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14357         
14358         if (in_combo || in_list || is_list) {
14359             //e.stopPropagation();
14360             return;
14361         }
14362         
14363         if(this.tickable){
14364             this.onTickableFooterButtonClick(e, false, false);
14365         }
14366
14367         this.collapse();
14368         
14369     },
14370
14371     /**
14372      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14373      */
14374     expand : function(){
14375        
14376         if(this.isExpanded() || !this.hasFocus){
14377             return;
14378         }
14379         
14380         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14381         this.list.setWidth(lw);
14382         
14383         Roo.log('expand');
14384         
14385         this.list.show();
14386         
14387         this.restrictHeight();
14388         
14389         if(this.tickable){
14390             
14391             this.tickItems = Roo.apply([], this.item);
14392             
14393             this.okBtn.show();
14394             this.cancelBtn.show();
14395             this.trigger.hide();
14396             
14397             if(this.editable){
14398                 this.tickableInputEl().focus();
14399             }
14400             
14401         }
14402         
14403         Roo.get(document).on('mousedown', this.collapseIf, this);
14404         Roo.get(document).on('mousewheel', this.collapseIf, this);
14405         if (!this.editable) {
14406             Roo.get(document).on('keydown', this.listKeyPress, this);
14407         }
14408         
14409         this.fireEvent('expand', this);
14410     },
14411
14412     // private
14413     // Implements the default empty TriggerField.onTriggerClick function
14414     onTriggerClick : function(e)
14415     {
14416         Roo.log('trigger click');
14417         
14418         if(this.disabled || !this.triggerList){
14419             return;
14420         }
14421         
14422         this.page = 0;
14423         this.loadNext = false;
14424         
14425         if(this.isExpanded()){
14426             this.collapse();
14427             if (!this.blockFocus) {
14428                 this.inputEl().focus();
14429             }
14430             
14431         }else {
14432             this.hasFocus = true;
14433             if(this.triggerAction == 'all') {
14434                 this.doQuery(this.allQuery, true);
14435             } else {
14436                 this.doQuery(this.getRawValue());
14437             }
14438             if (!this.blockFocus) {
14439                 this.inputEl().focus();
14440             }
14441         }
14442     },
14443     
14444     onTickableTriggerClick : function(e)
14445     {
14446         if(this.disabled){
14447             return;
14448         }
14449         
14450         this.page = 0;
14451         this.loadNext = false;
14452         this.hasFocus = true;
14453         
14454         if(this.triggerAction == 'all') {
14455             this.doQuery(this.allQuery, true);
14456         } else {
14457             this.doQuery(this.getRawValue());
14458         }
14459     },
14460     
14461     onSearchFieldClick : function(e)
14462     {
14463         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14464             this.onTickableFooterButtonClick(e, false, false);
14465             return;
14466         }
14467         
14468         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14469             return;
14470         }
14471         
14472         this.page = 0;
14473         this.loadNext = false;
14474         this.hasFocus = true;
14475         
14476         if(this.triggerAction == 'all') {
14477             this.doQuery(this.allQuery, true);
14478         } else {
14479             this.doQuery(this.getRawValue());
14480         }
14481     },
14482     
14483     listKeyPress : function(e)
14484     {
14485         //Roo.log('listkeypress');
14486         // scroll to first matching element based on key pres..
14487         if (e.isSpecialKey()) {
14488             return false;
14489         }
14490         var k = String.fromCharCode(e.getKey()).toUpperCase();
14491         //Roo.log(k);
14492         var match  = false;
14493         var csel = this.view.getSelectedNodes();
14494         var cselitem = false;
14495         if (csel.length) {
14496             var ix = this.view.indexOf(csel[0]);
14497             cselitem  = this.store.getAt(ix);
14498             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14499                 cselitem = false;
14500             }
14501             
14502         }
14503         
14504         this.store.each(function(v) { 
14505             if (cselitem) {
14506                 // start at existing selection.
14507                 if (cselitem.id == v.id) {
14508                     cselitem = false;
14509                 }
14510                 return true;
14511             }
14512                 
14513             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14514                 match = this.store.indexOf(v);
14515                 return false;
14516             }
14517             return true;
14518         }, this);
14519         
14520         if (match === false) {
14521             return true; // no more action?
14522         }
14523         // scroll to?
14524         this.view.select(match);
14525         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14526         sn.scrollIntoView(sn.dom.parentNode, false);
14527     },
14528     
14529     onViewScroll : function(e, t){
14530         
14531         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){
14532             return;
14533         }
14534         
14535         this.hasQuery = true;
14536         
14537         this.loading = this.list.select('.loading', true).first();
14538         
14539         if(this.loading === null){
14540             this.list.createChild({
14541                 tag: 'div',
14542                 cls: 'loading roo-select2-more-results roo-select2-active',
14543                 html: 'Loading more results...'
14544             });
14545             
14546             this.loading = this.list.select('.loading', true).first();
14547             
14548             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14549             
14550             this.loading.hide();
14551         }
14552         
14553         this.loading.show();
14554         
14555         var _combo = this;
14556         
14557         this.page++;
14558         this.loadNext = true;
14559         
14560         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14561         
14562         return;
14563     },
14564     
14565     addItem : function(o)
14566     {   
14567         var dv = ''; // display value
14568         
14569         if (this.displayField) {
14570             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14571         } else {
14572             // this is an error condition!!!
14573             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14574         }
14575         
14576         if(!dv.length){
14577             return;
14578         }
14579         
14580         var choice = this.choices.createChild({
14581             tag: 'li',
14582             cls: 'roo-select2-search-choice',
14583             cn: [
14584                 {
14585                     tag: 'div',
14586                     html: dv
14587                 },
14588                 {
14589                     tag: 'a',
14590                     href: '#',
14591                     cls: 'roo-select2-search-choice-close fa fa-times',
14592                     tabindex: '-1'
14593                 }
14594             ]
14595             
14596         }, this.searchField);
14597         
14598         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14599         
14600         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14601         
14602         this.item.push(o);
14603         
14604         this.lastData = o;
14605         
14606         this.syncValue();
14607         
14608         this.inputEl().dom.value = '';
14609         
14610         this.validate();
14611     },
14612     
14613     onRemoveItem : function(e, _self, o)
14614     {
14615         e.preventDefault();
14616         
14617         this.lastItem = Roo.apply([], this.item);
14618         
14619         var index = this.item.indexOf(o.data) * 1;
14620         
14621         if( index < 0){
14622             Roo.log('not this item?!');
14623             return;
14624         }
14625         
14626         this.item.splice(index, 1);
14627         o.item.remove();
14628         
14629         this.syncValue();
14630         
14631         this.fireEvent('remove', this, e);
14632         
14633         this.validate();
14634         
14635     },
14636     
14637     syncValue : function()
14638     {
14639         if(!this.item.length){
14640             this.clearValue();
14641             return;
14642         }
14643             
14644         var value = [];
14645         var _this = this;
14646         Roo.each(this.item, function(i){
14647             if(_this.valueField){
14648                 value.push(i[_this.valueField]);
14649                 return;
14650             }
14651
14652             value.push(i);
14653         });
14654
14655         this.value = value.join(',');
14656
14657         if(this.hiddenField){
14658             this.hiddenField.dom.value = this.value;
14659         }
14660         
14661         this.store.fireEvent("datachanged", this.store);
14662         
14663         this.validate();
14664     },
14665     
14666     clearItem : function()
14667     {
14668         if(!this.multiple){
14669             return;
14670         }
14671         
14672         this.item = [];
14673         
14674         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14675            c.remove();
14676         });
14677         
14678         this.syncValue();
14679         
14680         this.validate();
14681         
14682         if(this.tickable && !Roo.isTouch){
14683             this.view.refresh();
14684         }
14685     },
14686     
14687     inputEl: function ()
14688     {
14689         if(Roo.isIOS && this.useNativeIOS){
14690             return this.el.select('select.roo-ios-select', true).first();
14691         }
14692         
14693         if(Roo.isTouch && this.mobileTouchView){
14694             return this.el.select('input.form-control',true).first();
14695         }
14696         
14697         if(this.tickable){
14698             return this.searchField;
14699         }
14700         
14701         return this.el.select('input.form-control',true).first();
14702     },
14703     
14704     onTickableFooterButtonClick : function(e, btn, el)
14705     {
14706         e.preventDefault();
14707         
14708         this.lastItem = Roo.apply([], this.item);
14709         
14710         if(btn && btn.name == 'cancel'){
14711             this.tickItems = Roo.apply([], this.item);
14712             this.collapse();
14713             return;
14714         }
14715         
14716         this.clearItem();
14717         
14718         var _this = this;
14719         
14720         Roo.each(this.tickItems, function(o){
14721             _this.addItem(o);
14722         });
14723         
14724         this.collapse();
14725         
14726     },
14727     
14728     validate : function()
14729     {
14730         if(this.getVisibilityEl().hasClass('hidden')){
14731             return true;
14732         }
14733         
14734         var v = this.getRawValue();
14735         
14736         if(this.multiple){
14737             v = this.getValue();
14738         }
14739         
14740         if(this.disabled || this.allowBlank || v.length){
14741             this.markValid();
14742             return true;
14743         }
14744         
14745         this.markInvalid();
14746         return false;
14747     },
14748     
14749     tickableInputEl : function()
14750     {
14751         if(!this.tickable || !this.editable){
14752             return this.inputEl();
14753         }
14754         
14755         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14756     },
14757     
14758     
14759     getAutoCreateTouchView : function()
14760     {
14761         var id = Roo.id();
14762         
14763         var cfg = {
14764             cls: 'form-group' //input-group
14765         };
14766         
14767         var input =  {
14768             tag: 'input',
14769             id : id,
14770             type : this.inputType,
14771             cls : 'form-control x-combo-noedit',
14772             autocomplete: 'new-password',
14773             placeholder : this.placeholder || '',
14774             readonly : true
14775         };
14776         
14777         if (this.name) {
14778             input.name = this.name;
14779         }
14780         
14781         if (this.size) {
14782             input.cls += ' input-' + this.size;
14783         }
14784         
14785         if (this.disabled) {
14786             input.disabled = true;
14787         }
14788         
14789         var inputblock = {
14790             cls : '',
14791             cn : [
14792                 input
14793             ]
14794         };
14795         
14796         if(this.before){
14797             inputblock.cls += ' input-group';
14798             
14799             inputblock.cn.unshift({
14800                 tag :'span',
14801                 cls : 'input-group-addon',
14802                 html : this.before
14803             });
14804         }
14805         
14806         if(this.removable && !this.multiple){
14807             inputblock.cls += ' roo-removable';
14808             
14809             inputblock.cn.push({
14810                 tag: 'button',
14811                 html : 'x',
14812                 cls : 'roo-combo-removable-btn close'
14813             });
14814         }
14815
14816         if(this.hasFeedback && !this.allowBlank){
14817             
14818             inputblock.cls += ' has-feedback';
14819             
14820             inputblock.cn.push({
14821                 tag: 'span',
14822                 cls: 'glyphicon form-control-feedback'
14823             });
14824             
14825         }
14826         
14827         if (this.after) {
14828             
14829             inputblock.cls += (this.before) ? '' : ' input-group';
14830             
14831             inputblock.cn.push({
14832                 tag :'span',
14833                 cls : 'input-group-addon',
14834                 html : this.after
14835             });
14836         }
14837
14838         var box = {
14839             tag: 'div',
14840             cn: [
14841                 {
14842                     tag: 'input',
14843                     type : 'hidden',
14844                     cls: 'form-hidden-field'
14845                 },
14846                 inputblock
14847             ]
14848             
14849         };
14850         
14851         if(this.multiple){
14852             box = {
14853                 tag: 'div',
14854                 cn: [
14855                     {
14856                         tag: 'input',
14857                         type : 'hidden',
14858                         cls: 'form-hidden-field'
14859                     },
14860                     {
14861                         tag: 'ul',
14862                         cls: 'roo-select2-choices',
14863                         cn:[
14864                             {
14865                                 tag: 'li',
14866                                 cls: 'roo-select2-search-field',
14867                                 cn: [
14868
14869                                     inputblock
14870                                 ]
14871                             }
14872                         ]
14873                     }
14874                 ]
14875             }
14876         };
14877         
14878         var combobox = {
14879             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14880             cn: [
14881                 box
14882             ]
14883         };
14884         
14885         if(!this.multiple && this.showToggleBtn){
14886             
14887             var caret = {
14888                         tag: 'span',
14889                         cls: 'caret'
14890             };
14891             
14892             if (this.caret != false) {
14893                 caret = {
14894                      tag: 'i',
14895                      cls: 'fa fa-' + this.caret
14896                 };
14897                 
14898             }
14899             
14900             combobox.cn.push({
14901                 tag :'span',
14902                 cls : 'input-group-addon btn dropdown-toggle',
14903                 cn : [
14904                     caret,
14905                     {
14906                         tag: 'span',
14907                         cls: 'combobox-clear',
14908                         cn  : [
14909                             {
14910                                 tag : 'i',
14911                                 cls: 'icon-remove'
14912                             }
14913                         ]
14914                     }
14915                 ]
14916
14917             })
14918         }
14919         
14920         if(this.multiple){
14921             combobox.cls += ' roo-select2-container-multi';
14922         }
14923         
14924         var align = this.labelAlign || this.parentLabelAlign();
14925         
14926         if (align ==='left' && this.fieldLabel.length) {
14927
14928             cfg.cn = [
14929                 {
14930                    tag : 'i',
14931                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14932                    tooltip : 'This field is required'
14933                 },
14934                 {
14935                     tag: 'label',
14936                     cls : 'control-label',
14937                     html : this.fieldLabel
14938
14939                 },
14940                 {
14941                     cls : '', 
14942                     cn: [
14943                         combobox
14944                     ]
14945                 }
14946             ];
14947             
14948             var labelCfg = cfg.cn[1];
14949             var contentCfg = cfg.cn[2];
14950             
14951
14952             if(this.indicatorpos == 'right'){
14953                 cfg.cn = [
14954                     {
14955                         tag: 'label',
14956                         'for' :  id,
14957                         cls : 'control-label',
14958                         cn : [
14959                             {
14960                                 tag : 'span',
14961                                 html : this.fieldLabel
14962                             },
14963                             {
14964                                 tag : 'i',
14965                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14966                                 tooltip : 'This field is required'
14967                             }
14968                         ]
14969                     },
14970                     {
14971                         cls : "",
14972                         cn: [
14973                             combobox
14974                         ]
14975                     }
14976
14977                 ];
14978                 
14979                 labelCfg = cfg.cn[0];
14980                 contentCfg = cfg.cn[1];
14981             }
14982             
14983            
14984             
14985             if(this.labelWidth > 12){
14986                 labelCfg.style = "width: " + this.labelWidth + 'px';
14987             }
14988             
14989             if(this.labelWidth < 13 && this.labelmd == 0){
14990                 this.labelmd = this.labelWidth;
14991             }
14992             
14993             if(this.labellg > 0){
14994                 labelCfg.cls += ' col-lg-' + this.labellg;
14995                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14996             }
14997             
14998             if(this.labelmd > 0){
14999                 labelCfg.cls += ' col-md-' + this.labelmd;
15000                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15001             }
15002             
15003             if(this.labelsm > 0){
15004                 labelCfg.cls += ' col-sm-' + this.labelsm;
15005                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15006             }
15007             
15008             if(this.labelxs > 0){
15009                 labelCfg.cls += ' col-xs-' + this.labelxs;
15010                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15011             }
15012                 
15013                 
15014         } else if ( this.fieldLabel.length) {
15015             cfg.cn = [
15016                 {
15017                    tag : 'i',
15018                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15019                    tooltip : 'This field is required'
15020                 },
15021                 {
15022                     tag: 'label',
15023                     cls : 'control-label',
15024                     html : this.fieldLabel
15025
15026                 },
15027                 {
15028                     cls : '', 
15029                     cn: [
15030                         combobox
15031                     ]
15032                 }
15033             ];
15034             
15035             if(this.indicatorpos == 'right'){
15036                 cfg.cn = [
15037                     {
15038                         tag: 'label',
15039                         cls : 'control-label',
15040                         html : this.fieldLabel,
15041                         cn : [
15042                             {
15043                                tag : 'i',
15044                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15045                                tooltip : 'This field is required'
15046                             }
15047                         ]
15048                     },
15049                     {
15050                         cls : '', 
15051                         cn: [
15052                             combobox
15053                         ]
15054                     }
15055                 ];
15056             }
15057         } else {
15058             cfg.cn = combobox;    
15059         }
15060         
15061         
15062         var settings = this;
15063         
15064         ['xs','sm','md','lg'].map(function(size){
15065             if (settings[size]) {
15066                 cfg.cls += ' col-' + size + '-' + settings[size];
15067             }
15068         });
15069         
15070         return cfg;
15071     },
15072     
15073     initTouchView : function()
15074     {
15075         this.renderTouchView();
15076         
15077         this.touchViewEl.on('scroll', function(){
15078             this.el.dom.scrollTop = 0;
15079         }, this);
15080         
15081         this.originalValue = this.getValue();
15082         
15083         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15084         
15085         this.inputEl().on("click", this.showTouchView, this);
15086         if (this.triggerEl) {
15087             this.triggerEl.on("click", this.showTouchView, this);
15088         }
15089         
15090         
15091         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15092         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15093         
15094         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15095         
15096         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15097         this.store.on('load', this.onTouchViewLoad, this);
15098         this.store.on('loadexception', this.onTouchViewLoadException, this);
15099         
15100         if(this.hiddenName){
15101             
15102             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15103             
15104             this.hiddenField.dom.value =
15105                 this.hiddenValue !== undefined ? this.hiddenValue :
15106                 this.value !== undefined ? this.value : '';
15107         
15108             this.el.dom.removeAttribute('name');
15109             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15110         }
15111         
15112         if(this.multiple){
15113             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15114             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15115         }
15116         
15117         if(this.removable && !this.multiple){
15118             var close = this.closeTriggerEl();
15119             if(close){
15120                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15121                 close.on('click', this.removeBtnClick, this, close);
15122             }
15123         }
15124         /*
15125          * fix the bug in Safari iOS8
15126          */
15127         this.inputEl().on("focus", function(e){
15128             document.activeElement.blur();
15129         }, this);
15130         
15131         return;
15132         
15133         
15134     },
15135     
15136     renderTouchView : function()
15137     {
15138         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15139         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15140         
15141         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15142         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15143         
15144         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15145         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15146         this.touchViewBodyEl.setStyle('overflow', 'auto');
15147         
15148         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15149         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15150         
15151         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15152         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15153         
15154     },
15155     
15156     showTouchView : function()
15157     {
15158         if(this.disabled){
15159             return;
15160         }
15161         
15162         this.touchViewHeaderEl.hide();
15163
15164         if(this.modalTitle.length){
15165             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15166             this.touchViewHeaderEl.show();
15167         }
15168
15169         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15170         this.touchViewEl.show();
15171
15172         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15173         
15174         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15175         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15176
15177         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15178
15179         if(this.modalTitle.length){
15180             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15181         }
15182         
15183         this.touchViewBodyEl.setHeight(bodyHeight);
15184
15185         if(this.animate){
15186             var _this = this;
15187             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15188         }else{
15189             this.touchViewEl.addClass('in');
15190         }
15191
15192         this.doTouchViewQuery();
15193         
15194     },
15195     
15196     hideTouchView : function()
15197     {
15198         this.touchViewEl.removeClass('in');
15199
15200         if(this.animate){
15201             var _this = this;
15202             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15203         }else{
15204             this.touchViewEl.setStyle('display', 'none');
15205         }
15206         
15207     },
15208     
15209     setTouchViewValue : function()
15210     {
15211         if(this.multiple){
15212             this.clearItem();
15213         
15214             var _this = this;
15215
15216             Roo.each(this.tickItems, function(o){
15217                 this.addItem(o);
15218             }, this);
15219         }
15220         
15221         this.hideTouchView();
15222     },
15223     
15224     doTouchViewQuery : function()
15225     {
15226         var qe = {
15227             query: '',
15228             forceAll: true,
15229             combo: this,
15230             cancel:false
15231         };
15232         
15233         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15234             return false;
15235         }
15236         
15237         if(!this.alwaysQuery || this.mode == 'local'){
15238             this.onTouchViewLoad();
15239             return;
15240         }
15241         
15242         this.store.load();
15243     },
15244     
15245     onTouchViewBeforeLoad : function(combo,opts)
15246     {
15247         return;
15248     },
15249
15250     // private
15251     onTouchViewLoad : function()
15252     {
15253         if(this.store.getCount() < 1){
15254             this.onTouchViewEmptyResults();
15255             return;
15256         }
15257         
15258         this.clearTouchView();
15259         
15260         var rawValue = this.getRawValue();
15261         
15262         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15263         
15264         this.tickItems = [];
15265         
15266         this.store.data.each(function(d, rowIndex){
15267             var row = this.touchViewListGroup.createChild(template);
15268             
15269             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15270                 row.addClass(d.data.cls);
15271             }
15272             
15273             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15274                 var cfg = {
15275                     data : d.data,
15276                     html : d.data[this.displayField]
15277                 };
15278                 
15279                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15280                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15281                 }
15282             }
15283             row.removeClass('selected');
15284             if(!this.multiple && this.valueField &&
15285                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15286             {
15287                 // radio buttons..
15288                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15289                 row.addClass('selected');
15290             }
15291             
15292             if(this.multiple && this.valueField &&
15293                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15294             {
15295                 
15296                 // checkboxes...
15297                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15298                 this.tickItems.push(d.data);
15299             }
15300             
15301             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15302             
15303         }, this);
15304         
15305         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15306         
15307         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15308
15309         if(this.modalTitle.length){
15310             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15311         }
15312
15313         var listHeight = this.touchViewListGroup.getHeight();
15314         
15315         var _this = this;
15316         
15317         if(firstChecked && listHeight > bodyHeight){
15318             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15319         }
15320         
15321     },
15322     
15323     onTouchViewLoadException : function()
15324     {
15325         this.hideTouchView();
15326     },
15327     
15328     onTouchViewEmptyResults : function()
15329     {
15330         this.clearTouchView();
15331         
15332         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15333         
15334         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15335         
15336     },
15337     
15338     clearTouchView : function()
15339     {
15340         this.touchViewListGroup.dom.innerHTML = '';
15341     },
15342     
15343     onTouchViewClick : function(e, el, o)
15344     {
15345         e.preventDefault();
15346         
15347         var row = o.row;
15348         var rowIndex = o.rowIndex;
15349         
15350         var r = this.store.getAt(rowIndex);
15351         
15352         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15353             
15354             if(!this.multiple){
15355                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15356                     c.dom.removeAttribute('checked');
15357                 }, this);
15358
15359                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15360
15361                 this.setFromData(r.data);
15362
15363                 var close = this.closeTriggerEl();
15364
15365                 if(close){
15366                     close.show();
15367                 }
15368
15369                 this.hideTouchView();
15370
15371                 this.fireEvent('select', this, r, rowIndex);
15372
15373                 return;
15374             }
15375
15376             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15377                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15378                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15379                 return;
15380             }
15381
15382             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15383             this.addItem(r.data);
15384             this.tickItems.push(r.data);
15385         }
15386     },
15387     
15388     getAutoCreateNativeIOS : function()
15389     {
15390         var cfg = {
15391             cls: 'form-group' //input-group,
15392         };
15393         
15394         var combobox =  {
15395             tag: 'select',
15396             cls : 'roo-ios-select'
15397         };
15398         
15399         if (this.name) {
15400             combobox.name = this.name;
15401         }
15402         
15403         if (this.disabled) {
15404             combobox.disabled = true;
15405         }
15406         
15407         var settings = this;
15408         
15409         ['xs','sm','md','lg'].map(function(size){
15410             if (settings[size]) {
15411                 cfg.cls += ' col-' + size + '-' + settings[size];
15412             }
15413         });
15414         
15415         cfg.cn = combobox;
15416         
15417         return cfg;
15418         
15419     },
15420     
15421     initIOSView : function()
15422     {
15423         this.store.on('load', this.onIOSViewLoad, this);
15424         
15425         return;
15426     },
15427     
15428     onIOSViewLoad : function()
15429     {
15430         if(this.store.getCount() < 1){
15431             return;
15432         }
15433         
15434         this.clearIOSView();
15435         
15436         if(this.allowBlank) {
15437             
15438             var default_text = '-- SELECT --';
15439             
15440             if(this.placeholder.length){
15441                 default_text = this.placeholder;
15442             }
15443             
15444             if(this.emptyTitle.length){
15445                 default_text += ' - ' + this.emptyTitle + ' -';
15446             }
15447             
15448             var opt = this.inputEl().createChild({
15449                 tag: 'option',
15450                 value : 0,
15451                 html : default_text
15452             });
15453             
15454             var o = {};
15455             o[this.valueField] = 0;
15456             o[this.displayField] = default_text;
15457             
15458             this.ios_options.push({
15459                 data : o,
15460                 el : opt
15461             });
15462             
15463         }
15464         
15465         this.store.data.each(function(d, rowIndex){
15466             
15467             var html = '';
15468             
15469             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15470                 html = d.data[this.displayField];
15471             }
15472             
15473             var value = '';
15474             
15475             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15476                 value = d.data[this.valueField];
15477             }
15478             
15479             var option = {
15480                 tag: 'option',
15481                 value : value,
15482                 html : html
15483             };
15484             
15485             if(this.value == d.data[this.valueField]){
15486                 option['selected'] = true;
15487             }
15488             
15489             var opt = this.inputEl().createChild(option);
15490             
15491             this.ios_options.push({
15492                 data : d.data,
15493                 el : opt
15494             });
15495             
15496         }, this);
15497         
15498         this.inputEl().on('change', function(){
15499            this.fireEvent('select', this);
15500         }, this);
15501         
15502     },
15503     
15504     clearIOSView: function()
15505     {
15506         this.inputEl().dom.innerHTML = '';
15507         
15508         this.ios_options = [];
15509     },
15510     
15511     setIOSValue: function(v)
15512     {
15513         this.value = v;
15514         
15515         if(!this.ios_options){
15516             return;
15517         }
15518         
15519         Roo.each(this.ios_options, function(opts){
15520            
15521            opts.el.dom.removeAttribute('selected');
15522            
15523            if(opts.data[this.valueField] != v){
15524                return;
15525            }
15526            
15527            opts.el.dom.setAttribute('selected', true);
15528            
15529         }, this);
15530     }
15531
15532     /** 
15533     * @cfg {Boolean} grow 
15534     * @hide 
15535     */
15536     /** 
15537     * @cfg {Number} growMin 
15538     * @hide 
15539     */
15540     /** 
15541     * @cfg {Number} growMax 
15542     * @hide 
15543     */
15544     /**
15545      * @hide
15546      * @method autoSize
15547      */
15548 });
15549
15550 Roo.apply(Roo.bootstrap.ComboBox,  {
15551     
15552     header : {
15553         tag: 'div',
15554         cls: 'modal-header',
15555         cn: [
15556             {
15557                 tag: 'h4',
15558                 cls: 'modal-title'
15559             }
15560         ]
15561     },
15562     
15563     body : {
15564         tag: 'div',
15565         cls: 'modal-body',
15566         cn: [
15567             {
15568                 tag: 'ul',
15569                 cls: 'list-group'
15570             }
15571         ]
15572     },
15573     
15574     listItemRadio : {
15575         tag: 'li',
15576         cls: 'list-group-item',
15577         cn: [
15578             {
15579                 tag: 'span',
15580                 cls: 'roo-combobox-list-group-item-value'
15581             },
15582             {
15583                 tag: 'div',
15584                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15585                 cn: [
15586                     {
15587                         tag: 'input',
15588                         type: 'radio'
15589                     },
15590                     {
15591                         tag: 'label'
15592                     }
15593                 ]
15594             }
15595         ]
15596     },
15597     
15598     listItemCheckbox : {
15599         tag: 'li',
15600         cls: 'list-group-item',
15601         cn: [
15602             {
15603                 tag: 'span',
15604                 cls: 'roo-combobox-list-group-item-value'
15605             },
15606             {
15607                 tag: 'div',
15608                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15609                 cn: [
15610                     {
15611                         tag: 'input',
15612                         type: 'checkbox'
15613                     },
15614                     {
15615                         tag: 'label'
15616                     }
15617                 ]
15618             }
15619         ]
15620     },
15621     
15622     emptyResult : {
15623         tag: 'div',
15624         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15625     },
15626     
15627     footer : {
15628         tag: 'div',
15629         cls: 'modal-footer',
15630         cn: [
15631             {
15632                 tag: 'div',
15633                 cls: 'row',
15634                 cn: [
15635                     {
15636                         tag: 'div',
15637                         cls: 'col-xs-6 text-left',
15638                         cn: {
15639                             tag: 'button',
15640                             cls: 'btn btn-danger roo-touch-view-cancel',
15641                             html: 'Cancel'
15642                         }
15643                     },
15644                     {
15645                         tag: 'div',
15646                         cls: 'col-xs-6 text-right',
15647                         cn: {
15648                             tag: 'button',
15649                             cls: 'btn btn-success roo-touch-view-ok',
15650                             html: 'OK'
15651                         }
15652                     }
15653                 ]
15654             }
15655         ]
15656         
15657     }
15658 });
15659
15660 Roo.apply(Roo.bootstrap.ComboBox,  {
15661     
15662     touchViewTemplate : {
15663         tag: 'div',
15664         cls: 'modal fade roo-combobox-touch-view',
15665         cn: [
15666             {
15667                 tag: 'div',
15668                 cls: 'modal-dialog',
15669                 style : 'position:fixed', // we have to fix position....
15670                 cn: [
15671                     {
15672                         tag: 'div',
15673                         cls: 'modal-content',
15674                         cn: [
15675                             Roo.bootstrap.ComboBox.header,
15676                             Roo.bootstrap.ComboBox.body,
15677                             Roo.bootstrap.ComboBox.footer
15678                         ]
15679                     }
15680                 ]
15681             }
15682         ]
15683     }
15684 });/*
15685  * Based on:
15686  * Ext JS Library 1.1.1
15687  * Copyright(c) 2006-2007, Ext JS, LLC.
15688  *
15689  * Originally Released Under LGPL - original licence link has changed is not relivant.
15690  *
15691  * Fork - LGPL
15692  * <script type="text/javascript">
15693  */
15694
15695 /**
15696  * @class Roo.View
15697  * @extends Roo.util.Observable
15698  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15699  * This class also supports single and multi selection modes. <br>
15700  * Create a data model bound view:
15701  <pre><code>
15702  var store = new Roo.data.Store(...);
15703
15704  var view = new Roo.View({
15705     el : "my-element",
15706     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15707  
15708     singleSelect: true,
15709     selectedClass: "ydataview-selected",
15710     store: store
15711  });
15712
15713  // listen for node click?
15714  view.on("click", function(vw, index, node, e){
15715  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15716  });
15717
15718  // load XML data
15719  dataModel.load("foobar.xml");
15720  </code></pre>
15721  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15722  * <br><br>
15723  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15724  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15725  * 
15726  * Note: old style constructor is still suported (container, template, config)
15727  * 
15728  * @constructor
15729  * Create a new View
15730  * @param {Object} config The config object
15731  * 
15732  */
15733 Roo.View = function(config, depreciated_tpl, depreciated_config){
15734     
15735     this.parent = false;
15736     
15737     if (typeof(depreciated_tpl) == 'undefined') {
15738         // new way.. - universal constructor.
15739         Roo.apply(this, config);
15740         this.el  = Roo.get(this.el);
15741     } else {
15742         // old format..
15743         this.el  = Roo.get(config);
15744         this.tpl = depreciated_tpl;
15745         Roo.apply(this, depreciated_config);
15746     }
15747     this.wrapEl  = this.el.wrap().wrap();
15748     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15749     
15750     
15751     if(typeof(this.tpl) == "string"){
15752         this.tpl = new Roo.Template(this.tpl);
15753     } else {
15754         // support xtype ctors..
15755         this.tpl = new Roo.factory(this.tpl, Roo);
15756     }
15757     
15758     
15759     this.tpl.compile();
15760     
15761     /** @private */
15762     this.addEvents({
15763         /**
15764          * @event beforeclick
15765          * Fires before a click is processed. Returns false to cancel the default action.
15766          * @param {Roo.View} this
15767          * @param {Number} index The index of the target node
15768          * @param {HTMLElement} node The target node
15769          * @param {Roo.EventObject} e The raw event object
15770          */
15771             "beforeclick" : true,
15772         /**
15773          * @event click
15774          * Fires when a template node is clicked.
15775          * @param {Roo.View} this
15776          * @param {Number} index The index of the target node
15777          * @param {HTMLElement} node The target node
15778          * @param {Roo.EventObject} e The raw event object
15779          */
15780             "click" : true,
15781         /**
15782          * @event dblclick
15783          * Fires when a template node is double clicked.
15784          * @param {Roo.View} this
15785          * @param {Number} index The index of the target node
15786          * @param {HTMLElement} node The target node
15787          * @param {Roo.EventObject} e The raw event object
15788          */
15789             "dblclick" : true,
15790         /**
15791          * @event contextmenu
15792          * Fires when a template node is right clicked.
15793          * @param {Roo.View} this
15794          * @param {Number} index The index of the target node
15795          * @param {HTMLElement} node The target node
15796          * @param {Roo.EventObject} e The raw event object
15797          */
15798             "contextmenu" : true,
15799         /**
15800          * @event selectionchange
15801          * Fires when the selected nodes change.
15802          * @param {Roo.View} this
15803          * @param {Array} selections Array of the selected nodes
15804          */
15805             "selectionchange" : true,
15806     
15807         /**
15808          * @event beforeselect
15809          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15810          * @param {Roo.View} this
15811          * @param {HTMLElement} node The node to be selected
15812          * @param {Array} selections Array of currently selected nodes
15813          */
15814             "beforeselect" : true,
15815         /**
15816          * @event preparedata
15817          * Fires on every row to render, to allow you to change the data.
15818          * @param {Roo.View} this
15819          * @param {Object} data to be rendered (change this)
15820          */
15821           "preparedata" : true
15822           
15823           
15824         });
15825
15826
15827
15828     this.el.on({
15829         "click": this.onClick,
15830         "dblclick": this.onDblClick,
15831         "contextmenu": this.onContextMenu,
15832         scope:this
15833     });
15834
15835     this.selections = [];
15836     this.nodes = [];
15837     this.cmp = new Roo.CompositeElementLite([]);
15838     if(this.store){
15839         this.store = Roo.factory(this.store, Roo.data);
15840         this.setStore(this.store, true);
15841     }
15842     
15843     if ( this.footer && this.footer.xtype) {
15844            
15845          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15846         
15847         this.footer.dataSource = this.store;
15848         this.footer.container = fctr;
15849         this.footer = Roo.factory(this.footer, Roo);
15850         fctr.insertFirst(this.el);
15851         
15852         // this is a bit insane - as the paging toolbar seems to detach the el..
15853 //        dom.parentNode.parentNode.parentNode
15854          // they get detached?
15855     }
15856     
15857     
15858     Roo.View.superclass.constructor.call(this);
15859     
15860     
15861 };
15862
15863 Roo.extend(Roo.View, Roo.util.Observable, {
15864     
15865      /**
15866      * @cfg {Roo.data.Store} store Data store to load data from.
15867      */
15868     store : false,
15869     
15870     /**
15871      * @cfg {String|Roo.Element} el The container element.
15872      */
15873     el : '',
15874     
15875     /**
15876      * @cfg {String|Roo.Template} tpl The template used by this View 
15877      */
15878     tpl : false,
15879     /**
15880      * @cfg {String} dataName the named area of the template to use as the data area
15881      *                          Works with domtemplates roo-name="name"
15882      */
15883     dataName: false,
15884     /**
15885      * @cfg {String} selectedClass The css class to add to selected nodes
15886      */
15887     selectedClass : "x-view-selected",
15888      /**
15889      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15890      */
15891     emptyText : "",
15892     
15893     /**
15894      * @cfg {String} text to display on mask (default Loading)
15895      */
15896     mask : false,
15897     /**
15898      * @cfg {Boolean} multiSelect Allow multiple selection
15899      */
15900     multiSelect : false,
15901     /**
15902      * @cfg {Boolean} singleSelect Allow single selection
15903      */
15904     singleSelect:  false,
15905     
15906     /**
15907      * @cfg {Boolean} toggleSelect - selecting 
15908      */
15909     toggleSelect : false,
15910     
15911     /**
15912      * @cfg {Boolean} tickable - selecting 
15913      */
15914     tickable : false,
15915     
15916     /**
15917      * Returns the element this view is bound to.
15918      * @return {Roo.Element}
15919      */
15920     getEl : function(){
15921         return this.wrapEl;
15922     },
15923     
15924     
15925
15926     /**
15927      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15928      */
15929     refresh : function(){
15930         //Roo.log('refresh');
15931         var t = this.tpl;
15932         
15933         // if we are using something like 'domtemplate', then
15934         // the what gets used is:
15935         // t.applySubtemplate(NAME, data, wrapping data..)
15936         // the outer template then get' applied with
15937         //     the store 'extra data'
15938         // and the body get's added to the
15939         //      roo-name="data" node?
15940         //      <span class='roo-tpl-{name}'></span> ?????
15941         
15942         
15943         
15944         this.clearSelections();
15945         this.el.update("");
15946         var html = [];
15947         var records = this.store.getRange();
15948         if(records.length < 1) {
15949             
15950             // is this valid??  = should it render a template??
15951             
15952             this.el.update(this.emptyText);
15953             return;
15954         }
15955         var el = this.el;
15956         if (this.dataName) {
15957             this.el.update(t.apply(this.store.meta)); //????
15958             el = this.el.child('.roo-tpl-' + this.dataName);
15959         }
15960         
15961         for(var i = 0, len = records.length; i < len; i++){
15962             var data = this.prepareData(records[i].data, i, records[i]);
15963             this.fireEvent("preparedata", this, data, i, records[i]);
15964             
15965             var d = Roo.apply({}, data);
15966             
15967             if(this.tickable){
15968                 Roo.apply(d, {'roo-id' : Roo.id()});
15969                 
15970                 var _this = this;
15971             
15972                 Roo.each(this.parent.item, function(item){
15973                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15974                         return;
15975                     }
15976                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15977                 });
15978             }
15979             
15980             html[html.length] = Roo.util.Format.trim(
15981                 this.dataName ?
15982                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15983                     t.apply(d)
15984             );
15985         }
15986         
15987         
15988         
15989         el.update(html.join(""));
15990         this.nodes = el.dom.childNodes;
15991         this.updateIndexes(0);
15992     },
15993     
15994
15995     /**
15996      * Function to override to reformat the data that is sent to
15997      * the template for each node.
15998      * DEPRICATED - use the preparedata event handler.
15999      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16000      * a JSON object for an UpdateManager bound view).
16001      */
16002     prepareData : function(data, index, record)
16003     {
16004         this.fireEvent("preparedata", this, data, index, record);
16005         return data;
16006     },
16007
16008     onUpdate : function(ds, record){
16009         // Roo.log('on update');   
16010         this.clearSelections();
16011         var index = this.store.indexOf(record);
16012         var n = this.nodes[index];
16013         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16014         n.parentNode.removeChild(n);
16015         this.updateIndexes(index, index);
16016     },
16017
16018     
16019     
16020 // --------- FIXME     
16021     onAdd : function(ds, records, index)
16022     {
16023         //Roo.log(['on Add', ds, records, index] );        
16024         this.clearSelections();
16025         if(this.nodes.length == 0){
16026             this.refresh();
16027             return;
16028         }
16029         var n = this.nodes[index];
16030         for(var i = 0, len = records.length; i < len; i++){
16031             var d = this.prepareData(records[i].data, i, records[i]);
16032             if(n){
16033                 this.tpl.insertBefore(n, d);
16034             }else{
16035                 
16036                 this.tpl.append(this.el, d);
16037             }
16038         }
16039         this.updateIndexes(index);
16040     },
16041
16042     onRemove : function(ds, record, index){
16043        // Roo.log('onRemove');
16044         this.clearSelections();
16045         var el = this.dataName  ?
16046             this.el.child('.roo-tpl-' + this.dataName) :
16047             this.el; 
16048         
16049         el.dom.removeChild(this.nodes[index]);
16050         this.updateIndexes(index);
16051     },
16052
16053     /**
16054      * Refresh an individual node.
16055      * @param {Number} index
16056      */
16057     refreshNode : function(index){
16058         this.onUpdate(this.store, this.store.getAt(index));
16059     },
16060
16061     updateIndexes : function(startIndex, endIndex){
16062         var ns = this.nodes;
16063         startIndex = startIndex || 0;
16064         endIndex = endIndex || ns.length - 1;
16065         for(var i = startIndex; i <= endIndex; i++){
16066             ns[i].nodeIndex = i;
16067         }
16068     },
16069
16070     /**
16071      * Changes the data store this view uses and refresh the view.
16072      * @param {Store} store
16073      */
16074     setStore : function(store, initial){
16075         if(!initial && this.store){
16076             this.store.un("datachanged", this.refresh);
16077             this.store.un("add", this.onAdd);
16078             this.store.un("remove", this.onRemove);
16079             this.store.un("update", this.onUpdate);
16080             this.store.un("clear", this.refresh);
16081             this.store.un("beforeload", this.onBeforeLoad);
16082             this.store.un("load", this.onLoad);
16083             this.store.un("loadexception", this.onLoad);
16084         }
16085         if(store){
16086           
16087             store.on("datachanged", this.refresh, this);
16088             store.on("add", this.onAdd, this);
16089             store.on("remove", this.onRemove, this);
16090             store.on("update", this.onUpdate, this);
16091             store.on("clear", this.refresh, this);
16092             store.on("beforeload", this.onBeforeLoad, this);
16093             store.on("load", this.onLoad, this);
16094             store.on("loadexception", this.onLoad, this);
16095         }
16096         
16097         if(store){
16098             this.refresh();
16099         }
16100     },
16101     /**
16102      * onbeforeLoad - masks the loading area.
16103      *
16104      */
16105     onBeforeLoad : function(store,opts)
16106     {
16107          //Roo.log('onBeforeLoad');   
16108         if (!opts.add) {
16109             this.el.update("");
16110         }
16111         this.el.mask(this.mask ? this.mask : "Loading" ); 
16112     },
16113     onLoad : function ()
16114     {
16115         this.el.unmask();
16116     },
16117     
16118
16119     /**
16120      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16121      * @param {HTMLElement} node
16122      * @return {HTMLElement} The template node
16123      */
16124     findItemFromChild : function(node){
16125         var el = this.dataName  ?
16126             this.el.child('.roo-tpl-' + this.dataName,true) :
16127             this.el.dom; 
16128         
16129         if(!node || node.parentNode == el){
16130                     return node;
16131             }
16132             var p = node.parentNode;
16133             while(p && p != el){
16134             if(p.parentNode == el){
16135                 return p;
16136             }
16137             p = p.parentNode;
16138         }
16139             return null;
16140     },
16141
16142     /** @ignore */
16143     onClick : function(e){
16144         var item = this.findItemFromChild(e.getTarget());
16145         if(item){
16146             var index = this.indexOf(item);
16147             if(this.onItemClick(item, index, e) !== false){
16148                 this.fireEvent("click", this, index, item, e);
16149             }
16150         }else{
16151             this.clearSelections();
16152         }
16153     },
16154
16155     /** @ignore */
16156     onContextMenu : function(e){
16157         var item = this.findItemFromChild(e.getTarget());
16158         if(item){
16159             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16160         }
16161     },
16162
16163     /** @ignore */
16164     onDblClick : function(e){
16165         var item = this.findItemFromChild(e.getTarget());
16166         if(item){
16167             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16168         }
16169     },
16170
16171     onItemClick : function(item, index, e)
16172     {
16173         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16174             return false;
16175         }
16176         if (this.toggleSelect) {
16177             var m = this.isSelected(item) ? 'unselect' : 'select';
16178             //Roo.log(m);
16179             var _t = this;
16180             _t[m](item, true, false);
16181             return true;
16182         }
16183         if(this.multiSelect || this.singleSelect){
16184             if(this.multiSelect && e.shiftKey && this.lastSelection){
16185                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16186             }else{
16187                 this.select(item, this.multiSelect && e.ctrlKey);
16188                 this.lastSelection = item;
16189             }
16190             
16191             if(!this.tickable){
16192                 e.preventDefault();
16193             }
16194             
16195         }
16196         return true;
16197     },
16198
16199     /**
16200      * Get the number of selected nodes.
16201      * @return {Number}
16202      */
16203     getSelectionCount : function(){
16204         return this.selections.length;
16205     },
16206
16207     /**
16208      * Get the currently selected nodes.
16209      * @return {Array} An array of HTMLElements
16210      */
16211     getSelectedNodes : function(){
16212         return this.selections;
16213     },
16214
16215     /**
16216      * Get the indexes of the selected nodes.
16217      * @return {Array}
16218      */
16219     getSelectedIndexes : function(){
16220         var indexes = [], s = this.selections;
16221         for(var i = 0, len = s.length; i < len; i++){
16222             indexes.push(s[i].nodeIndex);
16223         }
16224         return indexes;
16225     },
16226
16227     /**
16228      * Clear all selections
16229      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16230      */
16231     clearSelections : function(suppressEvent){
16232         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16233             this.cmp.elements = this.selections;
16234             this.cmp.removeClass(this.selectedClass);
16235             this.selections = [];
16236             if(!suppressEvent){
16237                 this.fireEvent("selectionchange", this, this.selections);
16238             }
16239         }
16240     },
16241
16242     /**
16243      * Returns true if the passed node is selected
16244      * @param {HTMLElement/Number} node The node or node index
16245      * @return {Boolean}
16246      */
16247     isSelected : function(node){
16248         var s = this.selections;
16249         if(s.length < 1){
16250             return false;
16251         }
16252         node = this.getNode(node);
16253         return s.indexOf(node) !== -1;
16254     },
16255
16256     /**
16257      * Selects nodes.
16258      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
16259      * @param {Boolean} keepExisting (optional) true to keep existing selections
16260      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16261      */
16262     select : function(nodeInfo, keepExisting, suppressEvent){
16263         if(nodeInfo instanceof Array){
16264             if(!keepExisting){
16265                 this.clearSelections(true);
16266             }
16267             for(var i = 0, len = nodeInfo.length; i < len; i++){
16268                 this.select(nodeInfo[i], true, true);
16269             }
16270             return;
16271         } 
16272         var node = this.getNode(nodeInfo);
16273         if(!node || this.isSelected(node)){
16274             return; // already selected.
16275         }
16276         if(!keepExisting){
16277             this.clearSelections(true);
16278         }
16279         
16280         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16281             Roo.fly(node).addClass(this.selectedClass);
16282             this.selections.push(node);
16283             if(!suppressEvent){
16284                 this.fireEvent("selectionchange", this, this.selections);
16285             }
16286         }
16287         
16288         
16289     },
16290       /**
16291      * Unselects nodes.
16292      * @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
16293      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16294      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16295      */
16296     unselect : function(nodeInfo, keepExisting, suppressEvent)
16297     {
16298         if(nodeInfo instanceof Array){
16299             Roo.each(this.selections, function(s) {
16300                 this.unselect(s, nodeInfo);
16301             }, this);
16302             return;
16303         }
16304         var node = this.getNode(nodeInfo);
16305         if(!node || !this.isSelected(node)){
16306             //Roo.log("not selected");
16307             return; // not selected.
16308         }
16309         // fireevent???
16310         var ns = [];
16311         Roo.each(this.selections, function(s) {
16312             if (s == node ) {
16313                 Roo.fly(node).removeClass(this.selectedClass);
16314
16315                 return;
16316             }
16317             ns.push(s);
16318         },this);
16319         
16320         this.selections= ns;
16321         this.fireEvent("selectionchange", this, this.selections);
16322     },
16323
16324     /**
16325      * Gets a template node.
16326      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16327      * @return {HTMLElement} The node or null if it wasn't found
16328      */
16329     getNode : function(nodeInfo){
16330         if(typeof nodeInfo == "string"){
16331             return document.getElementById(nodeInfo);
16332         }else if(typeof nodeInfo == "number"){
16333             return this.nodes[nodeInfo];
16334         }
16335         return nodeInfo;
16336     },
16337
16338     /**
16339      * Gets a range template nodes.
16340      * @param {Number} startIndex
16341      * @param {Number} endIndex
16342      * @return {Array} An array of nodes
16343      */
16344     getNodes : function(start, end){
16345         var ns = this.nodes;
16346         start = start || 0;
16347         end = typeof end == "undefined" ? ns.length - 1 : end;
16348         var nodes = [];
16349         if(start <= end){
16350             for(var i = start; i <= end; i++){
16351                 nodes.push(ns[i]);
16352             }
16353         } else{
16354             for(var i = start; i >= end; i--){
16355                 nodes.push(ns[i]);
16356             }
16357         }
16358         return nodes;
16359     },
16360
16361     /**
16362      * Finds the index of the passed node
16363      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16364      * @return {Number} The index of the node or -1
16365      */
16366     indexOf : function(node){
16367         node = this.getNode(node);
16368         if(typeof node.nodeIndex == "number"){
16369             return node.nodeIndex;
16370         }
16371         var ns = this.nodes;
16372         for(var i = 0, len = ns.length; i < len; i++){
16373             if(ns[i] == node){
16374                 return i;
16375             }
16376         }
16377         return -1;
16378     }
16379 });
16380 /*
16381  * - LGPL
16382  *
16383  * based on jquery fullcalendar
16384  * 
16385  */
16386
16387 Roo.bootstrap = Roo.bootstrap || {};
16388 /**
16389  * @class Roo.bootstrap.Calendar
16390  * @extends Roo.bootstrap.Component
16391  * Bootstrap Calendar class
16392  * @cfg {Boolean} loadMask (true|false) default false
16393  * @cfg {Object} header generate the user specific header of the calendar, default false
16394
16395  * @constructor
16396  * Create a new Container
16397  * @param {Object} config The config object
16398  */
16399
16400
16401
16402 Roo.bootstrap.Calendar = function(config){
16403     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16404      this.addEvents({
16405         /**
16406              * @event select
16407              * Fires when a date is selected
16408              * @param {DatePicker} this
16409              * @param {Date} date The selected date
16410              */
16411         'select': true,
16412         /**
16413              * @event monthchange
16414              * Fires when the displayed month changes 
16415              * @param {DatePicker} this
16416              * @param {Date} date The selected month
16417              */
16418         'monthchange': true,
16419         /**
16420              * @event evententer
16421              * Fires when mouse over an event
16422              * @param {Calendar} this
16423              * @param {event} Event
16424              */
16425         'evententer': true,
16426         /**
16427              * @event eventleave
16428              * Fires when the mouse leaves an
16429              * @param {Calendar} this
16430              * @param {event}
16431              */
16432         'eventleave': true,
16433         /**
16434              * @event eventclick
16435              * Fires when the mouse click an
16436              * @param {Calendar} this
16437              * @param {event}
16438              */
16439         'eventclick': true
16440         
16441     });
16442
16443 };
16444
16445 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16446     
16447      /**
16448      * @cfg {Number} startDay
16449      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16450      */
16451     startDay : 0,
16452     
16453     loadMask : false,
16454     
16455     header : false,
16456       
16457     getAutoCreate : function(){
16458         
16459         
16460         var fc_button = function(name, corner, style, content ) {
16461             return Roo.apply({},{
16462                 tag : 'span',
16463                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16464                          (corner.length ?
16465                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16466                             ''
16467                         ),
16468                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16469                 unselectable: 'on'
16470             });
16471         };
16472         
16473         var header = {};
16474         
16475         if(!this.header){
16476             header = {
16477                 tag : 'table',
16478                 cls : 'fc-header',
16479                 style : 'width:100%',
16480                 cn : [
16481                     {
16482                         tag: 'tr',
16483                         cn : [
16484                             {
16485                                 tag : 'td',
16486                                 cls : 'fc-header-left',
16487                                 cn : [
16488                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16489                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16490                                     { tag: 'span', cls: 'fc-header-space' },
16491                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16492
16493
16494                                 ]
16495                             },
16496
16497                             {
16498                                 tag : 'td',
16499                                 cls : 'fc-header-center',
16500                                 cn : [
16501                                     {
16502                                         tag: 'span',
16503                                         cls: 'fc-header-title',
16504                                         cn : {
16505                                             tag: 'H2',
16506                                             html : 'month / year'
16507                                         }
16508                                     }
16509
16510                                 ]
16511                             },
16512                             {
16513                                 tag : 'td',
16514                                 cls : 'fc-header-right',
16515                                 cn : [
16516                               /*      fc_button('month', 'left', '', 'month' ),
16517                                     fc_button('week', '', '', 'week' ),
16518                                     fc_button('day', 'right', '', 'day' )
16519                                 */    
16520
16521                                 ]
16522                             }
16523
16524                         ]
16525                     }
16526                 ]
16527             };
16528         }
16529         
16530         header = this.header;
16531         
16532        
16533         var cal_heads = function() {
16534             var ret = [];
16535             // fixme - handle this.
16536             
16537             for (var i =0; i < Date.dayNames.length; i++) {
16538                 var d = Date.dayNames[i];
16539                 ret.push({
16540                     tag: 'th',
16541                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16542                     html : d.substring(0,3)
16543                 });
16544                 
16545             }
16546             ret[0].cls += ' fc-first';
16547             ret[6].cls += ' fc-last';
16548             return ret;
16549         };
16550         var cal_cell = function(n) {
16551             return  {
16552                 tag: 'td',
16553                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16554                 cn : [
16555                     {
16556                         cn : [
16557                             {
16558                                 cls: 'fc-day-number',
16559                                 html: 'D'
16560                             },
16561                             {
16562                                 cls: 'fc-day-content',
16563                              
16564                                 cn : [
16565                                      {
16566                                         style: 'position: relative;' // height: 17px;
16567                                     }
16568                                 ]
16569                             }
16570                             
16571                             
16572                         ]
16573                     }
16574                 ]
16575                 
16576             }
16577         };
16578         var cal_rows = function() {
16579             
16580             var ret = [];
16581             for (var r = 0; r < 6; r++) {
16582                 var row= {
16583                     tag : 'tr',
16584                     cls : 'fc-week',
16585                     cn : []
16586                 };
16587                 
16588                 for (var i =0; i < Date.dayNames.length; i++) {
16589                     var d = Date.dayNames[i];
16590                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16591
16592                 }
16593                 row.cn[0].cls+=' fc-first';
16594                 row.cn[0].cn[0].style = 'min-height:90px';
16595                 row.cn[6].cls+=' fc-last';
16596                 ret.push(row);
16597                 
16598             }
16599             ret[0].cls += ' fc-first';
16600             ret[4].cls += ' fc-prev-last';
16601             ret[5].cls += ' fc-last';
16602             return ret;
16603             
16604         };
16605         
16606         var cal_table = {
16607             tag: 'table',
16608             cls: 'fc-border-separate',
16609             style : 'width:100%',
16610             cellspacing  : 0,
16611             cn : [
16612                 { 
16613                     tag: 'thead',
16614                     cn : [
16615                         { 
16616                             tag: 'tr',
16617                             cls : 'fc-first fc-last',
16618                             cn : cal_heads()
16619                         }
16620                     ]
16621                 },
16622                 { 
16623                     tag: 'tbody',
16624                     cn : cal_rows()
16625                 }
16626                   
16627             ]
16628         };
16629          
16630          var cfg = {
16631             cls : 'fc fc-ltr',
16632             cn : [
16633                 header,
16634                 {
16635                     cls : 'fc-content',
16636                     style : "position: relative;",
16637                     cn : [
16638                         {
16639                             cls : 'fc-view fc-view-month fc-grid',
16640                             style : 'position: relative',
16641                             unselectable : 'on',
16642                             cn : [
16643                                 {
16644                                     cls : 'fc-event-container',
16645                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16646                                 },
16647                                 cal_table
16648                             ]
16649                         }
16650                     ]
16651     
16652                 }
16653            ] 
16654             
16655         };
16656         
16657          
16658         
16659         return cfg;
16660     },
16661     
16662     
16663     initEvents : function()
16664     {
16665         if(!this.store){
16666             throw "can not find store for calendar";
16667         }
16668         
16669         var mark = {
16670             tag: "div",
16671             cls:"x-dlg-mask",
16672             style: "text-align:center",
16673             cn: [
16674                 {
16675                     tag: "div",
16676                     style: "background-color:white;width:50%;margin:250 auto",
16677                     cn: [
16678                         {
16679                             tag: "img",
16680                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16681                         },
16682                         {
16683                             tag: "span",
16684                             html: "Loading"
16685                         }
16686                         
16687                     ]
16688                 }
16689             ]
16690         };
16691         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16692         
16693         var size = this.el.select('.fc-content', true).first().getSize();
16694         this.maskEl.setSize(size.width, size.height);
16695         this.maskEl.enableDisplayMode("block");
16696         if(!this.loadMask){
16697             this.maskEl.hide();
16698         }
16699         
16700         this.store = Roo.factory(this.store, Roo.data);
16701         this.store.on('load', this.onLoad, this);
16702         this.store.on('beforeload', this.onBeforeLoad, this);
16703         
16704         this.resize();
16705         
16706         this.cells = this.el.select('.fc-day',true);
16707         //Roo.log(this.cells);
16708         this.textNodes = this.el.query('.fc-day-number');
16709         this.cells.addClassOnOver('fc-state-hover');
16710         
16711         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16712         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16713         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16714         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16715         
16716         this.on('monthchange', this.onMonthChange, this);
16717         
16718         this.update(new Date().clearTime());
16719     },
16720     
16721     resize : function() {
16722         var sz  = this.el.getSize();
16723         
16724         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16725         this.el.select('.fc-day-content div',true).setHeight(34);
16726     },
16727     
16728     
16729     // private
16730     showPrevMonth : function(e){
16731         this.update(this.activeDate.add("mo", -1));
16732     },
16733     showToday : function(e){
16734         this.update(new Date().clearTime());
16735     },
16736     // private
16737     showNextMonth : function(e){
16738         this.update(this.activeDate.add("mo", 1));
16739     },
16740
16741     // private
16742     showPrevYear : function(){
16743         this.update(this.activeDate.add("y", -1));
16744     },
16745
16746     // private
16747     showNextYear : function(){
16748         this.update(this.activeDate.add("y", 1));
16749     },
16750
16751     
16752    // private
16753     update : function(date)
16754     {
16755         var vd = this.activeDate;
16756         this.activeDate = date;
16757 //        if(vd && this.el){
16758 //            var t = date.getTime();
16759 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16760 //                Roo.log('using add remove');
16761 //                
16762 //                this.fireEvent('monthchange', this, date);
16763 //                
16764 //                this.cells.removeClass("fc-state-highlight");
16765 //                this.cells.each(function(c){
16766 //                   if(c.dateValue == t){
16767 //                       c.addClass("fc-state-highlight");
16768 //                       setTimeout(function(){
16769 //                            try{c.dom.firstChild.focus();}catch(e){}
16770 //                       }, 50);
16771 //                       return false;
16772 //                   }
16773 //                   return true;
16774 //                });
16775 //                return;
16776 //            }
16777 //        }
16778         
16779         var days = date.getDaysInMonth();
16780         
16781         var firstOfMonth = date.getFirstDateOfMonth();
16782         var startingPos = firstOfMonth.getDay()-this.startDay;
16783         
16784         if(startingPos < this.startDay){
16785             startingPos += 7;
16786         }
16787         
16788         var pm = date.add(Date.MONTH, -1);
16789         var prevStart = pm.getDaysInMonth()-startingPos;
16790 //        
16791         this.cells = this.el.select('.fc-day',true);
16792         this.textNodes = this.el.query('.fc-day-number');
16793         this.cells.addClassOnOver('fc-state-hover');
16794         
16795         var cells = this.cells.elements;
16796         var textEls = this.textNodes;
16797         
16798         Roo.each(cells, function(cell){
16799             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16800         });
16801         
16802         days += startingPos;
16803
16804         // convert everything to numbers so it's fast
16805         var day = 86400000;
16806         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16807         //Roo.log(d);
16808         //Roo.log(pm);
16809         //Roo.log(prevStart);
16810         
16811         var today = new Date().clearTime().getTime();
16812         var sel = date.clearTime().getTime();
16813         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16814         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16815         var ddMatch = this.disabledDatesRE;
16816         var ddText = this.disabledDatesText;
16817         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16818         var ddaysText = this.disabledDaysText;
16819         var format = this.format;
16820         
16821         var setCellClass = function(cal, cell){
16822             cell.row = 0;
16823             cell.events = [];
16824             cell.more = [];
16825             //Roo.log('set Cell Class');
16826             cell.title = "";
16827             var t = d.getTime();
16828             
16829             //Roo.log(d);
16830             
16831             cell.dateValue = t;
16832             if(t == today){
16833                 cell.className += " fc-today";
16834                 cell.className += " fc-state-highlight";
16835                 cell.title = cal.todayText;
16836             }
16837             if(t == sel){
16838                 // disable highlight in other month..
16839                 //cell.className += " fc-state-highlight";
16840                 
16841             }
16842             // disabling
16843             if(t < min) {
16844                 cell.className = " fc-state-disabled";
16845                 cell.title = cal.minText;
16846                 return;
16847             }
16848             if(t > max) {
16849                 cell.className = " fc-state-disabled";
16850                 cell.title = cal.maxText;
16851                 return;
16852             }
16853             if(ddays){
16854                 if(ddays.indexOf(d.getDay()) != -1){
16855                     cell.title = ddaysText;
16856                     cell.className = " fc-state-disabled";
16857                 }
16858             }
16859             if(ddMatch && format){
16860                 var fvalue = d.dateFormat(format);
16861                 if(ddMatch.test(fvalue)){
16862                     cell.title = ddText.replace("%0", fvalue);
16863                     cell.className = " fc-state-disabled";
16864                 }
16865             }
16866             
16867             if (!cell.initialClassName) {
16868                 cell.initialClassName = cell.dom.className;
16869             }
16870             
16871             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16872         };
16873
16874         var i = 0;
16875         
16876         for(; i < startingPos; i++) {
16877             textEls[i].innerHTML = (++prevStart);
16878             d.setDate(d.getDate()+1);
16879             
16880             cells[i].className = "fc-past fc-other-month";
16881             setCellClass(this, cells[i]);
16882         }
16883         
16884         var intDay = 0;
16885         
16886         for(; i < days; i++){
16887             intDay = i - startingPos + 1;
16888             textEls[i].innerHTML = (intDay);
16889             d.setDate(d.getDate()+1);
16890             
16891             cells[i].className = ''; // "x-date-active";
16892             setCellClass(this, cells[i]);
16893         }
16894         var extraDays = 0;
16895         
16896         for(; i < 42; i++) {
16897             textEls[i].innerHTML = (++extraDays);
16898             d.setDate(d.getDate()+1);
16899             
16900             cells[i].className = "fc-future fc-other-month";
16901             setCellClass(this, cells[i]);
16902         }
16903         
16904         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16905         
16906         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16907         
16908         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16909         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16910         
16911         if(totalRows != 6){
16912             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16913             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16914         }
16915         
16916         this.fireEvent('monthchange', this, date);
16917         
16918         
16919         /*
16920         if(!this.internalRender){
16921             var main = this.el.dom.firstChild;
16922             var w = main.offsetWidth;
16923             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16924             Roo.fly(main).setWidth(w);
16925             this.internalRender = true;
16926             // opera does not respect the auto grow header center column
16927             // then, after it gets a width opera refuses to recalculate
16928             // without a second pass
16929             if(Roo.isOpera && !this.secondPass){
16930                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16931                 this.secondPass = true;
16932                 this.update.defer(10, this, [date]);
16933             }
16934         }
16935         */
16936         
16937     },
16938     
16939     findCell : function(dt) {
16940         dt = dt.clearTime().getTime();
16941         var ret = false;
16942         this.cells.each(function(c){
16943             //Roo.log("check " +c.dateValue + '?=' + dt);
16944             if(c.dateValue == dt){
16945                 ret = c;
16946                 return false;
16947             }
16948             return true;
16949         });
16950         
16951         return ret;
16952     },
16953     
16954     findCells : function(ev) {
16955         var s = ev.start.clone().clearTime().getTime();
16956        // Roo.log(s);
16957         var e= ev.end.clone().clearTime().getTime();
16958        // Roo.log(e);
16959         var ret = [];
16960         this.cells.each(function(c){
16961              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16962             
16963             if(c.dateValue > e){
16964                 return ;
16965             }
16966             if(c.dateValue < s){
16967                 return ;
16968             }
16969             ret.push(c);
16970         });
16971         
16972         return ret;    
16973     },
16974     
16975 //    findBestRow: function(cells)
16976 //    {
16977 //        var ret = 0;
16978 //        
16979 //        for (var i =0 ; i < cells.length;i++) {
16980 //            ret  = Math.max(cells[i].rows || 0,ret);
16981 //        }
16982 //        return ret;
16983 //        
16984 //    },
16985     
16986     
16987     addItem : function(ev)
16988     {
16989         // look for vertical location slot in
16990         var cells = this.findCells(ev);
16991         
16992 //        ev.row = this.findBestRow(cells);
16993         
16994         // work out the location.
16995         
16996         var crow = false;
16997         var rows = [];
16998         for(var i =0; i < cells.length; i++) {
16999             
17000             cells[i].row = cells[0].row;
17001             
17002             if(i == 0){
17003                 cells[i].row = cells[i].row + 1;
17004             }
17005             
17006             if (!crow) {
17007                 crow = {
17008                     start : cells[i],
17009                     end :  cells[i]
17010                 };
17011                 continue;
17012             }
17013             if (crow.start.getY() == cells[i].getY()) {
17014                 // on same row.
17015                 crow.end = cells[i];
17016                 continue;
17017             }
17018             // different row.
17019             rows.push(crow);
17020             crow = {
17021                 start: cells[i],
17022                 end : cells[i]
17023             };
17024             
17025         }
17026         
17027         rows.push(crow);
17028         ev.els = [];
17029         ev.rows = rows;
17030         ev.cells = cells;
17031         
17032         cells[0].events.push(ev);
17033         
17034         this.calevents.push(ev);
17035     },
17036     
17037     clearEvents: function() {
17038         
17039         if(!this.calevents){
17040             return;
17041         }
17042         
17043         Roo.each(this.cells.elements, function(c){
17044             c.row = 0;
17045             c.events = [];
17046             c.more = [];
17047         });
17048         
17049         Roo.each(this.calevents, function(e) {
17050             Roo.each(e.els, function(el) {
17051                 el.un('mouseenter' ,this.onEventEnter, this);
17052                 el.un('mouseleave' ,this.onEventLeave, this);
17053                 el.remove();
17054             },this);
17055         },this);
17056         
17057         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17058             e.remove();
17059         });
17060         
17061     },
17062     
17063     renderEvents: function()
17064     {   
17065         var _this = this;
17066         
17067         this.cells.each(function(c) {
17068             
17069             if(c.row < 5){
17070                 return;
17071             }
17072             
17073             var ev = c.events;
17074             
17075             var r = 4;
17076             if(c.row != c.events.length){
17077                 r = 4 - (4 - (c.row - c.events.length));
17078             }
17079             
17080             c.events = ev.slice(0, r);
17081             c.more = ev.slice(r);
17082             
17083             if(c.more.length && c.more.length == 1){
17084                 c.events.push(c.more.pop());
17085             }
17086             
17087             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17088             
17089         });
17090             
17091         this.cells.each(function(c) {
17092             
17093             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17094             
17095             
17096             for (var e = 0; e < c.events.length; e++){
17097                 var ev = c.events[e];
17098                 var rows = ev.rows;
17099                 
17100                 for(var i = 0; i < rows.length; i++) {
17101                 
17102                     // how many rows should it span..
17103
17104                     var  cfg = {
17105                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17106                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17107
17108                         unselectable : "on",
17109                         cn : [
17110                             {
17111                                 cls: 'fc-event-inner',
17112                                 cn : [
17113     //                                {
17114     //                                  tag:'span',
17115     //                                  cls: 'fc-event-time',
17116     //                                  html : cells.length > 1 ? '' : ev.time
17117     //                                },
17118                                     {
17119                                       tag:'span',
17120                                       cls: 'fc-event-title',
17121                                       html : String.format('{0}', ev.title)
17122                                     }
17123
17124
17125                                 ]
17126                             },
17127                             {
17128                                 cls: 'ui-resizable-handle ui-resizable-e',
17129                                 html : '&nbsp;&nbsp;&nbsp'
17130                             }
17131
17132                         ]
17133                     };
17134
17135                     if (i == 0) {
17136                         cfg.cls += ' fc-event-start';
17137                     }
17138                     if ((i+1) == rows.length) {
17139                         cfg.cls += ' fc-event-end';
17140                     }
17141
17142                     var ctr = _this.el.select('.fc-event-container',true).first();
17143                     var cg = ctr.createChild(cfg);
17144
17145                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17146                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17147
17148                     var r = (c.more.length) ? 1 : 0;
17149                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17150                     cg.setWidth(ebox.right - sbox.x -2);
17151
17152                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17153                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17154                     cg.on('click', _this.onEventClick, _this, ev);
17155
17156                     ev.els.push(cg);
17157                     
17158                 }
17159                 
17160             }
17161             
17162             
17163             if(c.more.length){
17164                 var  cfg = {
17165                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17166                     style : 'position: absolute',
17167                     unselectable : "on",
17168                     cn : [
17169                         {
17170                             cls: 'fc-event-inner',
17171                             cn : [
17172                                 {
17173                                   tag:'span',
17174                                   cls: 'fc-event-title',
17175                                   html : 'More'
17176                                 }
17177
17178
17179                             ]
17180                         },
17181                         {
17182                             cls: 'ui-resizable-handle ui-resizable-e',
17183                             html : '&nbsp;&nbsp;&nbsp'
17184                         }
17185
17186                     ]
17187                 };
17188
17189                 var ctr = _this.el.select('.fc-event-container',true).first();
17190                 var cg = ctr.createChild(cfg);
17191
17192                 var sbox = c.select('.fc-day-content',true).first().getBox();
17193                 var ebox = c.select('.fc-day-content',true).first().getBox();
17194                 //Roo.log(cg);
17195                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17196                 cg.setWidth(ebox.right - sbox.x -2);
17197
17198                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17199                 
17200             }
17201             
17202         });
17203         
17204         
17205         
17206     },
17207     
17208     onEventEnter: function (e, el,event,d) {
17209         this.fireEvent('evententer', this, el, event);
17210     },
17211     
17212     onEventLeave: function (e, el,event,d) {
17213         this.fireEvent('eventleave', this, el, event);
17214     },
17215     
17216     onEventClick: function (e, el,event,d) {
17217         this.fireEvent('eventclick', this, el, event);
17218     },
17219     
17220     onMonthChange: function () {
17221         this.store.load();
17222     },
17223     
17224     onMoreEventClick: function(e, el, more)
17225     {
17226         var _this = this;
17227         
17228         this.calpopover.placement = 'right';
17229         this.calpopover.setTitle('More');
17230         
17231         this.calpopover.setContent('');
17232         
17233         var ctr = this.calpopover.el.select('.popover-content', true).first();
17234         
17235         Roo.each(more, function(m){
17236             var cfg = {
17237                 cls : 'fc-event-hori fc-event-draggable',
17238                 html : m.title
17239             };
17240             var cg = ctr.createChild(cfg);
17241             
17242             cg.on('click', _this.onEventClick, _this, m);
17243         });
17244         
17245         this.calpopover.show(el);
17246         
17247         
17248     },
17249     
17250     onLoad: function () 
17251     {   
17252         this.calevents = [];
17253         var cal = this;
17254         
17255         if(this.store.getCount() > 0){
17256             this.store.data.each(function(d){
17257                cal.addItem({
17258                     id : d.data.id,
17259                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17260                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17261                     time : d.data.start_time,
17262                     title : d.data.title,
17263                     description : d.data.description,
17264                     venue : d.data.venue
17265                 });
17266             });
17267         }
17268         
17269         this.renderEvents();
17270         
17271         if(this.calevents.length && this.loadMask){
17272             this.maskEl.hide();
17273         }
17274     },
17275     
17276     onBeforeLoad: function()
17277     {
17278         this.clearEvents();
17279         if(this.loadMask){
17280             this.maskEl.show();
17281         }
17282     }
17283 });
17284
17285  
17286  /*
17287  * - LGPL
17288  *
17289  * element
17290  * 
17291  */
17292
17293 /**
17294  * @class Roo.bootstrap.Popover
17295  * @extends Roo.bootstrap.Component
17296  * Bootstrap Popover class
17297  * @cfg {String} html contents of the popover   (or false to use children..)
17298  * @cfg {String} title of popover (or false to hide)
17299  * @cfg {String} placement how it is placed
17300  * @cfg {String} trigger click || hover (or false to trigger manually)
17301  * @cfg {String} over what (parent or false to trigger manually.)
17302  * @cfg {Number} delay - delay before showing
17303  
17304  * @constructor
17305  * Create a new Popover
17306  * @param {Object} config The config object
17307  */
17308
17309 Roo.bootstrap.Popover = function(config){
17310     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17311     
17312     this.addEvents({
17313         // raw events
17314          /**
17315          * @event show
17316          * After the popover show
17317          * 
17318          * @param {Roo.bootstrap.Popover} this
17319          */
17320         "show" : true,
17321         /**
17322          * @event hide
17323          * After the popover hide
17324          * 
17325          * @param {Roo.bootstrap.Popover} this
17326          */
17327         "hide" : true
17328     });
17329 };
17330
17331 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17332     
17333     title: 'Fill in a title',
17334     html: false,
17335     
17336     placement : 'right',
17337     trigger : 'hover', // hover
17338     
17339     delay : 0,
17340     
17341     over: 'parent',
17342     
17343     can_build_overlaid : false,
17344     
17345     getChildContainer : function()
17346     {
17347         return this.el.select('.popover-content',true).first();
17348     },
17349     
17350     getAutoCreate : function(){
17351          
17352         var cfg = {
17353            cls : 'popover roo-dynamic',
17354            style: 'display:block',
17355            cn : [
17356                 {
17357                     cls : 'arrow'
17358                 },
17359                 {
17360                     cls : 'popover-inner',
17361                     cn : [
17362                         {
17363                             tag: 'h3',
17364                             cls: 'popover-title',
17365                             html : this.title
17366                         },
17367                         {
17368                             cls : 'popover-content',
17369                             html : this.html
17370                         }
17371                     ]
17372                     
17373                 }
17374            ]
17375         };
17376         
17377         return cfg;
17378     },
17379     setTitle: function(str)
17380     {
17381         this.title = str;
17382         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17383     },
17384     setContent: function(str)
17385     {
17386         this.html = str;
17387         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17388     },
17389     // as it get's added to the bottom of the page.
17390     onRender : function(ct, position)
17391     {
17392         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17393         if(!this.el){
17394             var cfg = Roo.apply({},  this.getAutoCreate());
17395             cfg.id = Roo.id();
17396             
17397             if (this.cls) {
17398                 cfg.cls += ' ' + this.cls;
17399             }
17400             if (this.style) {
17401                 cfg.style = this.style;
17402             }
17403             //Roo.log("adding to ");
17404             this.el = Roo.get(document.body).createChild(cfg, position);
17405 //            Roo.log(this.el);
17406         }
17407         this.initEvents();
17408     },
17409     
17410     initEvents : function()
17411     {
17412         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17413         this.el.enableDisplayMode('block');
17414         this.el.hide();
17415         if (this.over === false) {
17416             return; 
17417         }
17418         if (this.triggers === false) {
17419             return;
17420         }
17421         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17422         var triggers = this.trigger ? this.trigger.split(' ') : [];
17423         Roo.each(triggers, function(trigger) {
17424         
17425             if (trigger == 'click') {
17426                 on_el.on('click', this.toggle, this);
17427             } else if (trigger != 'manual') {
17428                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17429                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17430       
17431                 on_el.on(eventIn  ,this.enter, this);
17432                 on_el.on(eventOut, this.leave, this);
17433             }
17434         }, this);
17435         
17436     },
17437     
17438     
17439     // private
17440     timeout : null,
17441     hoverState : null,
17442     
17443     toggle : function () {
17444         this.hoverState == 'in' ? this.leave() : this.enter();
17445     },
17446     
17447     enter : function () {
17448         
17449         clearTimeout(this.timeout);
17450     
17451         this.hoverState = 'in';
17452     
17453         if (!this.delay || !this.delay.show) {
17454             this.show();
17455             return;
17456         }
17457         var _t = this;
17458         this.timeout = setTimeout(function () {
17459             if (_t.hoverState == 'in') {
17460                 _t.show();
17461             }
17462         }, this.delay.show)
17463     },
17464     
17465     leave : function() {
17466         clearTimeout(this.timeout);
17467     
17468         this.hoverState = 'out';
17469     
17470         if (!this.delay || !this.delay.hide) {
17471             this.hide();
17472             return;
17473         }
17474         var _t = this;
17475         this.timeout = setTimeout(function () {
17476             if (_t.hoverState == 'out') {
17477                 _t.hide();
17478             }
17479         }, this.delay.hide)
17480     },
17481     
17482     show : function (on_el)
17483     {
17484         if (!on_el) {
17485             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17486         }
17487         
17488         // set content.
17489         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17490         if (this.html !== false) {
17491             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17492         }
17493         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17494         if (!this.title.length) {
17495             this.el.select('.popover-title',true).hide();
17496         }
17497         
17498         var placement = typeof this.placement == 'function' ?
17499             this.placement.call(this, this.el, on_el) :
17500             this.placement;
17501             
17502         var autoToken = /\s?auto?\s?/i;
17503         var autoPlace = autoToken.test(placement);
17504         if (autoPlace) {
17505             placement = placement.replace(autoToken, '') || 'top';
17506         }
17507         
17508         //this.el.detach()
17509         //this.el.setXY([0,0]);
17510         this.el.show();
17511         this.el.dom.style.display='block';
17512         this.el.addClass(placement);
17513         
17514         //this.el.appendTo(on_el);
17515         
17516         var p = this.getPosition();
17517         var box = this.el.getBox();
17518         
17519         if (autoPlace) {
17520             // fixme..
17521         }
17522         var align = Roo.bootstrap.Popover.alignment[placement];
17523         
17524 //        Roo.log(align);
17525         this.el.alignTo(on_el, align[0],align[1]);
17526         //var arrow = this.el.select('.arrow',true).first();
17527         //arrow.set(align[2], 
17528         
17529         this.el.addClass('in');
17530         
17531         
17532         if (this.el.hasClass('fade')) {
17533             // fade it?
17534         }
17535         
17536         this.hoverState = 'in';
17537         
17538         this.fireEvent('show', this);
17539         
17540     },
17541     hide : function()
17542     {
17543         this.el.setXY([0,0]);
17544         this.el.removeClass('in');
17545         this.el.hide();
17546         this.hoverState = null;
17547         
17548         this.fireEvent('hide', this);
17549     }
17550     
17551 });
17552
17553 Roo.bootstrap.Popover.alignment = {
17554     'left' : ['r-l', [-10,0], 'right'],
17555     'right' : ['l-r', [10,0], 'left'],
17556     'bottom' : ['t-b', [0,10], 'top'],
17557     'top' : [ 'b-t', [0,-10], 'bottom']
17558 };
17559
17560  /*
17561  * - LGPL
17562  *
17563  * Progress
17564  * 
17565  */
17566
17567 /**
17568  * @class Roo.bootstrap.Progress
17569  * @extends Roo.bootstrap.Component
17570  * Bootstrap Progress class
17571  * @cfg {Boolean} striped striped of the progress bar
17572  * @cfg {Boolean} active animated of the progress bar
17573  * 
17574  * 
17575  * @constructor
17576  * Create a new Progress
17577  * @param {Object} config The config object
17578  */
17579
17580 Roo.bootstrap.Progress = function(config){
17581     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17582 };
17583
17584 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17585     
17586     striped : false,
17587     active: false,
17588     
17589     getAutoCreate : function(){
17590         var cfg = {
17591             tag: 'div',
17592             cls: 'progress'
17593         };
17594         
17595         
17596         if(this.striped){
17597             cfg.cls += ' progress-striped';
17598         }
17599       
17600         if(this.active){
17601             cfg.cls += ' active';
17602         }
17603         
17604         
17605         return cfg;
17606     }
17607    
17608 });
17609
17610  
17611
17612  /*
17613  * - LGPL
17614  *
17615  * ProgressBar
17616  * 
17617  */
17618
17619 /**
17620  * @class Roo.bootstrap.ProgressBar
17621  * @extends Roo.bootstrap.Component
17622  * Bootstrap ProgressBar class
17623  * @cfg {Number} aria_valuenow aria-value now
17624  * @cfg {Number} aria_valuemin aria-value min
17625  * @cfg {Number} aria_valuemax aria-value max
17626  * @cfg {String} label label for the progress bar
17627  * @cfg {String} panel (success | info | warning | danger )
17628  * @cfg {String} role role of the progress bar
17629  * @cfg {String} sr_only text
17630  * 
17631  * 
17632  * @constructor
17633  * Create a new ProgressBar
17634  * @param {Object} config The config object
17635  */
17636
17637 Roo.bootstrap.ProgressBar = function(config){
17638     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17639 };
17640
17641 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17642     
17643     aria_valuenow : 0,
17644     aria_valuemin : 0,
17645     aria_valuemax : 100,
17646     label : false,
17647     panel : false,
17648     role : false,
17649     sr_only: false,
17650     
17651     getAutoCreate : function()
17652     {
17653         
17654         var cfg = {
17655             tag: 'div',
17656             cls: 'progress-bar',
17657             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17658         };
17659         
17660         if(this.sr_only){
17661             cfg.cn = {
17662                 tag: 'span',
17663                 cls: 'sr-only',
17664                 html: this.sr_only
17665             }
17666         }
17667         
17668         if(this.role){
17669             cfg.role = this.role;
17670         }
17671         
17672         if(this.aria_valuenow){
17673             cfg['aria-valuenow'] = this.aria_valuenow;
17674         }
17675         
17676         if(this.aria_valuemin){
17677             cfg['aria-valuemin'] = this.aria_valuemin;
17678         }
17679         
17680         if(this.aria_valuemax){
17681             cfg['aria-valuemax'] = this.aria_valuemax;
17682         }
17683         
17684         if(this.label && !this.sr_only){
17685             cfg.html = this.label;
17686         }
17687         
17688         if(this.panel){
17689             cfg.cls += ' progress-bar-' + this.panel;
17690         }
17691         
17692         return cfg;
17693     },
17694     
17695     update : function(aria_valuenow)
17696     {
17697         this.aria_valuenow = aria_valuenow;
17698         
17699         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17700     }
17701    
17702 });
17703
17704  
17705
17706  /*
17707  * - LGPL
17708  *
17709  * column
17710  * 
17711  */
17712
17713 /**
17714  * @class Roo.bootstrap.TabGroup
17715  * @extends Roo.bootstrap.Column
17716  * Bootstrap Column class
17717  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17718  * @cfg {Boolean} carousel true to make the group behave like a carousel
17719  * @cfg {Boolean} bullets show bullets for the panels
17720  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17721  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17722  * @cfg {Boolean} showarrow (true|false) show arrow default true
17723  * 
17724  * @constructor
17725  * Create a new TabGroup
17726  * @param {Object} config The config object
17727  */
17728
17729 Roo.bootstrap.TabGroup = function(config){
17730     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17731     if (!this.navId) {
17732         this.navId = Roo.id();
17733     }
17734     this.tabs = [];
17735     Roo.bootstrap.TabGroup.register(this);
17736     
17737 };
17738
17739 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17740     
17741     carousel : false,
17742     transition : false,
17743     bullets : 0,
17744     timer : 0,
17745     autoslide : false,
17746     slideFn : false,
17747     slideOnTouch : false,
17748     showarrow : true,
17749     
17750     getAutoCreate : function()
17751     {
17752         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17753         
17754         cfg.cls += ' tab-content';
17755         
17756         if (this.carousel) {
17757             cfg.cls += ' carousel slide';
17758             
17759             cfg.cn = [{
17760                cls : 'carousel-inner',
17761                cn : []
17762             }];
17763         
17764             if(this.bullets  && !Roo.isTouch){
17765                 
17766                 var bullets = {
17767                     cls : 'carousel-bullets',
17768                     cn : []
17769                 };
17770                
17771                 if(this.bullets_cls){
17772                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17773                 }
17774                 
17775                 bullets.cn.push({
17776                     cls : 'clear'
17777                 });
17778                 
17779                 cfg.cn[0].cn.push(bullets);
17780             }
17781             
17782             if(this.showarrow){
17783                 cfg.cn[0].cn.push({
17784                     tag : 'div',
17785                     class : 'carousel-arrow',
17786                     cn : [
17787                         {
17788                             tag : 'div',
17789                             class : 'carousel-prev',
17790                             cn : [
17791                                 {
17792                                     tag : 'i',
17793                                     class : 'fa fa-chevron-left'
17794                                 }
17795                             ]
17796                         },
17797                         {
17798                             tag : 'div',
17799                             class : 'carousel-next',
17800                             cn : [
17801                                 {
17802                                     tag : 'i',
17803                                     class : 'fa fa-chevron-right'
17804                                 }
17805                             ]
17806                         }
17807                     ]
17808                 });
17809             }
17810             
17811         }
17812         
17813         return cfg;
17814     },
17815     
17816     initEvents:  function()
17817     {
17818 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17819 //            this.el.on("touchstart", this.onTouchStart, this);
17820 //        }
17821         
17822         if(this.autoslide){
17823             var _this = this;
17824             
17825             this.slideFn = window.setInterval(function() {
17826                 _this.showPanelNext();
17827             }, this.timer);
17828         }
17829         
17830         if(this.showarrow){
17831             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17832             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17833         }
17834         
17835         
17836     },
17837     
17838 //    onTouchStart : function(e, el, o)
17839 //    {
17840 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17841 //            return;
17842 //        }
17843 //        
17844 //        this.showPanelNext();
17845 //    },
17846     
17847     
17848     getChildContainer : function()
17849     {
17850         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17851     },
17852     
17853     /**
17854     * register a Navigation item
17855     * @param {Roo.bootstrap.NavItem} the navitem to add
17856     */
17857     register : function(item)
17858     {
17859         this.tabs.push( item);
17860         item.navId = this.navId; // not really needed..
17861         this.addBullet();
17862     
17863     },
17864     
17865     getActivePanel : function()
17866     {
17867         var r = false;
17868         Roo.each(this.tabs, function(t) {
17869             if (t.active) {
17870                 r = t;
17871                 return false;
17872             }
17873             return null;
17874         });
17875         return r;
17876         
17877     },
17878     getPanelByName : function(n)
17879     {
17880         var r = false;
17881         Roo.each(this.tabs, function(t) {
17882             if (t.tabId == n) {
17883                 r = t;
17884                 return false;
17885             }
17886             return null;
17887         });
17888         return r;
17889     },
17890     indexOfPanel : function(p)
17891     {
17892         var r = false;
17893         Roo.each(this.tabs, function(t,i) {
17894             if (t.tabId == p.tabId) {
17895                 r = i;
17896                 return false;
17897             }
17898             return null;
17899         });
17900         return r;
17901     },
17902     /**
17903      * show a specific panel
17904      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17905      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17906      */
17907     showPanel : function (pan)
17908     {
17909         if(this.transition || typeof(pan) == 'undefined'){
17910             Roo.log("waiting for the transitionend");
17911             return;
17912         }
17913         
17914         if (typeof(pan) == 'number') {
17915             pan = this.tabs[pan];
17916         }
17917         
17918         if (typeof(pan) == 'string') {
17919             pan = this.getPanelByName(pan);
17920         }
17921         
17922         var cur = this.getActivePanel();
17923         
17924         if(!pan || !cur){
17925             Roo.log('pan or acitve pan is undefined');
17926             return false;
17927         }
17928         
17929         if (pan.tabId == this.getActivePanel().tabId) {
17930             return true;
17931         }
17932         
17933         if (false === cur.fireEvent('beforedeactivate')) {
17934             return false;
17935         }
17936         
17937         if(this.bullets > 0 && !Roo.isTouch){
17938             this.setActiveBullet(this.indexOfPanel(pan));
17939         }
17940         
17941         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17942             
17943             this.transition = true;
17944             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17945             var lr = dir == 'next' ? 'left' : 'right';
17946             pan.el.addClass(dir); // or prev
17947             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17948             cur.el.addClass(lr); // or right
17949             pan.el.addClass(lr);
17950             
17951             var _this = this;
17952             cur.el.on('transitionend', function() {
17953                 Roo.log("trans end?");
17954                 
17955                 pan.el.removeClass([lr,dir]);
17956                 pan.setActive(true);
17957                 
17958                 cur.el.removeClass([lr]);
17959                 cur.setActive(false);
17960                 
17961                 _this.transition = false;
17962                 
17963             }, this, { single:  true } );
17964             
17965             return true;
17966         }
17967         
17968         cur.setActive(false);
17969         pan.setActive(true);
17970         
17971         return true;
17972         
17973     },
17974     showPanelNext : function()
17975     {
17976         var i = this.indexOfPanel(this.getActivePanel());
17977         
17978         if (i >= this.tabs.length - 1 && !this.autoslide) {
17979             return;
17980         }
17981         
17982         if (i >= this.tabs.length - 1 && this.autoslide) {
17983             i = -1;
17984         }
17985         
17986         this.showPanel(this.tabs[i+1]);
17987     },
17988     
17989     showPanelPrev : function()
17990     {
17991         var i = this.indexOfPanel(this.getActivePanel());
17992         
17993         if (i  < 1 && !this.autoslide) {
17994             return;
17995         }
17996         
17997         if (i < 1 && this.autoslide) {
17998             i = this.tabs.length;
17999         }
18000         
18001         this.showPanel(this.tabs[i-1]);
18002     },
18003     
18004     
18005     addBullet: function()
18006     {
18007         if(!this.bullets || Roo.isTouch){
18008             return;
18009         }
18010         var ctr = this.el.select('.carousel-bullets',true).first();
18011         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18012         var bullet = ctr.createChild({
18013             cls : 'bullet bullet-' + i
18014         },ctr.dom.lastChild);
18015         
18016         
18017         var _this = this;
18018         
18019         bullet.on('click', (function(e, el, o, ii, t){
18020
18021             e.preventDefault();
18022
18023             this.showPanel(ii);
18024
18025             if(this.autoslide && this.slideFn){
18026                 clearInterval(this.slideFn);
18027                 this.slideFn = window.setInterval(function() {
18028                     _this.showPanelNext();
18029                 }, this.timer);
18030             }
18031
18032         }).createDelegate(this, [i, bullet], true));
18033                 
18034         
18035     },
18036      
18037     setActiveBullet : function(i)
18038     {
18039         if(Roo.isTouch){
18040             return;
18041         }
18042         
18043         Roo.each(this.el.select('.bullet', true).elements, function(el){
18044             el.removeClass('selected');
18045         });
18046
18047         var bullet = this.el.select('.bullet-' + i, true).first();
18048         
18049         if(!bullet){
18050             return;
18051         }
18052         
18053         bullet.addClass('selected');
18054     }
18055     
18056     
18057   
18058 });
18059
18060  
18061
18062  
18063  
18064 Roo.apply(Roo.bootstrap.TabGroup, {
18065     
18066     groups: {},
18067      /**
18068     * register a Navigation Group
18069     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18070     */
18071     register : function(navgrp)
18072     {
18073         this.groups[navgrp.navId] = navgrp;
18074         
18075     },
18076     /**
18077     * fetch a Navigation Group based on the navigation ID
18078     * if one does not exist , it will get created.
18079     * @param {string} the navgroup to add
18080     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18081     */
18082     get: function(navId) {
18083         if (typeof(this.groups[navId]) == 'undefined') {
18084             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18085         }
18086         return this.groups[navId] ;
18087     }
18088     
18089     
18090     
18091 });
18092
18093  /*
18094  * - LGPL
18095  *
18096  * TabPanel
18097  * 
18098  */
18099
18100 /**
18101  * @class Roo.bootstrap.TabPanel
18102  * @extends Roo.bootstrap.Component
18103  * Bootstrap TabPanel class
18104  * @cfg {Boolean} active panel active
18105  * @cfg {String} html panel content
18106  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18107  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18108  * @cfg {String} href click to link..
18109  * 
18110  * 
18111  * @constructor
18112  * Create a new TabPanel
18113  * @param {Object} config The config object
18114  */
18115
18116 Roo.bootstrap.TabPanel = function(config){
18117     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18118     this.addEvents({
18119         /**
18120              * @event changed
18121              * Fires when the active status changes
18122              * @param {Roo.bootstrap.TabPanel} this
18123              * @param {Boolean} state the new state
18124             
18125          */
18126         'changed': true,
18127         /**
18128              * @event beforedeactivate
18129              * Fires before a tab is de-activated - can be used to do validation on a form.
18130              * @param {Roo.bootstrap.TabPanel} this
18131              * @return {Boolean} false if there is an error
18132             
18133          */
18134         'beforedeactivate': true
18135      });
18136     
18137     this.tabId = this.tabId || Roo.id();
18138   
18139 };
18140
18141 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18142     
18143     active: false,
18144     html: false,
18145     tabId: false,
18146     navId : false,
18147     href : '',
18148     
18149     getAutoCreate : function(){
18150         var cfg = {
18151             tag: 'div',
18152             // item is needed for carousel - not sure if it has any effect otherwise
18153             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18154             html: this.html || ''
18155         };
18156         
18157         if(this.active){
18158             cfg.cls += ' active';
18159         }
18160         
18161         if(this.tabId){
18162             cfg.tabId = this.tabId;
18163         }
18164         
18165         
18166         return cfg;
18167     },
18168     
18169     initEvents:  function()
18170     {
18171         var p = this.parent();
18172         
18173         this.navId = this.navId || p.navId;
18174         
18175         if (typeof(this.navId) != 'undefined') {
18176             // not really needed.. but just in case.. parent should be a NavGroup.
18177             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18178             
18179             tg.register(this);
18180             
18181             var i = tg.tabs.length - 1;
18182             
18183             if(this.active && tg.bullets > 0 && i < tg.bullets){
18184                 tg.setActiveBullet(i);
18185             }
18186         }
18187         
18188         this.el.on('click', this.onClick, this);
18189         
18190         if(Roo.isTouch){
18191             this.el.on("touchstart", this.onTouchStart, this);
18192             this.el.on("touchmove", this.onTouchMove, this);
18193             this.el.on("touchend", this.onTouchEnd, this);
18194         }
18195         
18196     },
18197     
18198     onRender : function(ct, position)
18199     {
18200         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18201     },
18202     
18203     setActive : function(state)
18204     {
18205         Roo.log("panel - set active " + this.tabId + "=" + state);
18206         
18207         this.active = state;
18208         if (!state) {
18209             this.el.removeClass('active');
18210             
18211         } else  if (!this.el.hasClass('active')) {
18212             this.el.addClass('active');
18213         }
18214         
18215         this.fireEvent('changed', this, state);
18216     },
18217     
18218     onClick : function(e)
18219     {
18220         e.preventDefault();
18221         
18222         if(!this.href.length){
18223             return;
18224         }
18225         
18226         window.location.href = this.href;
18227     },
18228     
18229     startX : 0,
18230     startY : 0,
18231     endX : 0,
18232     endY : 0,
18233     swiping : false,
18234     
18235     onTouchStart : function(e)
18236     {
18237         this.swiping = false;
18238         
18239         this.startX = e.browserEvent.touches[0].clientX;
18240         this.startY = e.browserEvent.touches[0].clientY;
18241     },
18242     
18243     onTouchMove : function(e)
18244     {
18245         this.swiping = true;
18246         
18247         this.endX = e.browserEvent.touches[0].clientX;
18248         this.endY = e.browserEvent.touches[0].clientY;
18249     },
18250     
18251     onTouchEnd : function(e)
18252     {
18253         if(!this.swiping){
18254             this.onClick(e);
18255             return;
18256         }
18257         
18258         var tabGroup = this.parent();
18259         
18260         if(this.endX > this.startX){ // swiping right
18261             tabGroup.showPanelPrev();
18262             return;
18263         }
18264         
18265         if(this.startX > this.endX){ // swiping left
18266             tabGroup.showPanelNext();
18267             return;
18268         }
18269     }
18270     
18271     
18272 });
18273  
18274
18275  
18276
18277  /*
18278  * - LGPL
18279  *
18280  * DateField
18281  * 
18282  */
18283
18284 /**
18285  * @class Roo.bootstrap.DateField
18286  * @extends Roo.bootstrap.Input
18287  * Bootstrap DateField class
18288  * @cfg {Number} weekStart default 0
18289  * @cfg {String} viewMode default empty, (months|years)
18290  * @cfg {String} minViewMode default empty, (months|years)
18291  * @cfg {Number} startDate default -Infinity
18292  * @cfg {Number} endDate default Infinity
18293  * @cfg {Boolean} todayHighlight default false
18294  * @cfg {Boolean} todayBtn default false
18295  * @cfg {Boolean} calendarWeeks default false
18296  * @cfg {Object} daysOfWeekDisabled default empty
18297  * @cfg {Boolean} singleMode default false (true | false)
18298  * 
18299  * @cfg {Boolean} keyboardNavigation default true
18300  * @cfg {String} language default en
18301  * 
18302  * @constructor
18303  * Create a new DateField
18304  * @param {Object} config The config object
18305  */
18306
18307 Roo.bootstrap.DateField = function(config){
18308     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18309      this.addEvents({
18310             /**
18311              * @event show
18312              * Fires when this field show.
18313              * @param {Roo.bootstrap.DateField} this
18314              * @param {Mixed} date The date value
18315              */
18316             show : true,
18317             /**
18318              * @event show
18319              * Fires when this field hide.
18320              * @param {Roo.bootstrap.DateField} this
18321              * @param {Mixed} date The date value
18322              */
18323             hide : true,
18324             /**
18325              * @event select
18326              * Fires when select a date.
18327              * @param {Roo.bootstrap.DateField} this
18328              * @param {Mixed} date The date value
18329              */
18330             select : true,
18331             /**
18332              * @event beforeselect
18333              * Fires when before select a date.
18334              * @param {Roo.bootstrap.DateField} this
18335              * @param {Mixed} date The date value
18336              */
18337             beforeselect : true
18338         });
18339 };
18340
18341 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18342     
18343     /**
18344      * @cfg {String} format
18345      * The default date format string which can be overriden for localization support.  The format must be
18346      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18347      */
18348     format : "m/d/y",
18349     /**
18350      * @cfg {String} altFormats
18351      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18352      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18353      */
18354     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18355     
18356     weekStart : 0,
18357     
18358     viewMode : '',
18359     
18360     minViewMode : '',
18361     
18362     todayHighlight : false,
18363     
18364     todayBtn: false,
18365     
18366     language: 'en',
18367     
18368     keyboardNavigation: true,
18369     
18370     calendarWeeks: false,
18371     
18372     startDate: -Infinity,
18373     
18374     endDate: Infinity,
18375     
18376     daysOfWeekDisabled: [],
18377     
18378     _events: [],
18379     
18380     singleMode : false,
18381     
18382     UTCDate: function()
18383     {
18384         return new Date(Date.UTC.apply(Date, arguments));
18385     },
18386     
18387     UTCToday: function()
18388     {
18389         var today = new Date();
18390         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18391     },
18392     
18393     getDate: function() {
18394             var d = this.getUTCDate();
18395             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18396     },
18397     
18398     getUTCDate: function() {
18399             return this.date;
18400     },
18401     
18402     setDate: function(d) {
18403             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18404     },
18405     
18406     setUTCDate: function(d) {
18407             this.date = d;
18408             this.setValue(this.formatDate(this.date));
18409     },
18410         
18411     onRender: function(ct, position)
18412     {
18413         
18414         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18415         
18416         this.language = this.language || 'en';
18417         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18418         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18419         
18420         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18421         this.format = this.format || 'm/d/y';
18422         this.isInline = false;
18423         this.isInput = true;
18424         this.component = this.el.select('.add-on', true).first() || false;
18425         this.component = (this.component && this.component.length === 0) ? false : this.component;
18426         this.hasInput = this.component && this.inputEl().length;
18427         
18428         if (typeof(this.minViewMode === 'string')) {
18429             switch (this.minViewMode) {
18430                 case 'months':
18431                     this.minViewMode = 1;
18432                     break;
18433                 case 'years':
18434                     this.minViewMode = 2;
18435                     break;
18436                 default:
18437                     this.minViewMode = 0;
18438                     break;
18439             }
18440         }
18441         
18442         if (typeof(this.viewMode === 'string')) {
18443             switch (this.viewMode) {
18444                 case 'months':
18445                     this.viewMode = 1;
18446                     break;
18447                 case 'years':
18448                     this.viewMode = 2;
18449                     break;
18450                 default:
18451                     this.viewMode = 0;
18452                     break;
18453             }
18454         }
18455                 
18456         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18457         
18458 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18459         
18460         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18461         
18462         this.picker().on('mousedown', this.onMousedown, this);
18463         this.picker().on('click', this.onClick, this);
18464         
18465         this.picker().addClass('datepicker-dropdown');
18466         
18467         this.startViewMode = this.viewMode;
18468         
18469         if(this.singleMode){
18470             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18471                 v.setVisibilityMode(Roo.Element.DISPLAY);
18472                 v.hide();
18473             });
18474             
18475             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18476                 v.setStyle('width', '189px');
18477             });
18478         }
18479         
18480         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18481             if(!this.calendarWeeks){
18482                 v.remove();
18483                 return;
18484             }
18485             
18486             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18487             v.attr('colspan', function(i, val){
18488                 return parseInt(val) + 1;
18489             });
18490         });
18491                         
18492         
18493         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18494         
18495         this.setStartDate(this.startDate);
18496         this.setEndDate(this.endDate);
18497         
18498         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18499         
18500         this.fillDow();
18501         this.fillMonths();
18502         this.update();
18503         this.showMode();
18504         
18505         if(this.isInline) {
18506             this.show();
18507         }
18508     },
18509     
18510     picker : function()
18511     {
18512         return this.pickerEl;
18513 //        return this.el.select('.datepicker', true).first();
18514     },
18515     
18516     fillDow: function()
18517     {
18518         var dowCnt = this.weekStart;
18519         
18520         var dow = {
18521             tag: 'tr',
18522             cn: [
18523                 
18524             ]
18525         };
18526         
18527         if(this.calendarWeeks){
18528             dow.cn.push({
18529                 tag: 'th',
18530                 cls: 'cw',
18531                 html: '&nbsp;'
18532             })
18533         }
18534         
18535         while (dowCnt < this.weekStart + 7) {
18536             dow.cn.push({
18537                 tag: 'th',
18538                 cls: 'dow',
18539                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18540             });
18541         }
18542         
18543         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18544     },
18545     
18546     fillMonths: function()
18547     {    
18548         var i = 0;
18549         var months = this.picker().select('>.datepicker-months td', true).first();
18550         
18551         months.dom.innerHTML = '';
18552         
18553         while (i < 12) {
18554             var month = {
18555                 tag: 'span',
18556                 cls: 'month',
18557                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18558             };
18559             
18560             months.createChild(month);
18561         }
18562         
18563     },
18564     
18565     update: function()
18566     {
18567         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;
18568         
18569         if (this.date < this.startDate) {
18570             this.viewDate = new Date(this.startDate);
18571         } else if (this.date > this.endDate) {
18572             this.viewDate = new Date(this.endDate);
18573         } else {
18574             this.viewDate = new Date(this.date);
18575         }
18576         
18577         this.fill();
18578     },
18579     
18580     fill: function() 
18581     {
18582         var d = new Date(this.viewDate),
18583                 year = d.getUTCFullYear(),
18584                 month = d.getUTCMonth(),
18585                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18586                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18587                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18588                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18589                 currentDate = this.date && this.date.valueOf(),
18590                 today = this.UTCToday();
18591         
18592         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18593         
18594 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18595         
18596 //        this.picker.select('>tfoot th.today').
18597 //                                              .text(dates[this.language].today)
18598 //                                              .toggle(this.todayBtn !== false);
18599     
18600         this.updateNavArrows();
18601         this.fillMonths();
18602                                                 
18603         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18604         
18605         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18606          
18607         prevMonth.setUTCDate(day);
18608         
18609         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18610         
18611         var nextMonth = new Date(prevMonth);
18612         
18613         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18614         
18615         nextMonth = nextMonth.valueOf();
18616         
18617         var fillMonths = false;
18618         
18619         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18620         
18621         while(prevMonth.valueOf() < nextMonth) {
18622             var clsName = '';
18623             
18624             if (prevMonth.getUTCDay() === this.weekStart) {
18625                 if(fillMonths){
18626                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18627                 }
18628                     
18629                 fillMonths = {
18630                     tag: 'tr',
18631                     cn: []
18632                 };
18633                 
18634                 if(this.calendarWeeks){
18635                     // ISO 8601: First week contains first thursday.
18636                     // ISO also states week starts on Monday, but we can be more abstract here.
18637                     var
18638                     // Start of current week: based on weekstart/current date
18639                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18640                     // Thursday of this week
18641                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18642                     // First Thursday of year, year from thursday
18643                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18644                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18645                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18646                     
18647                     fillMonths.cn.push({
18648                         tag: 'td',
18649                         cls: 'cw',
18650                         html: calWeek
18651                     });
18652                 }
18653             }
18654             
18655             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18656                 clsName += ' old';
18657             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18658                 clsName += ' new';
18659             }
18660             if (this.todayHighlight &&
18661                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18662                 prevMonth.getUTCMonth() == today.getMonth() &&
18663                 prevMonth.getUTCDate() == today.getDate()) {
18664                 clsName += ' today';
18665             }
18666             
18667             if (currentDate && prevMonth.valueOf() === currentDate) {
18668                 clsName += ' active';
18669             }
18670             
18671             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18672                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18673                     clsName += ' disabled';
18674             }
18675             
18676             fillMonths.cn.push({
18677                 tag: 'td',
18678                 cls: 'day ' + clsName,
18679                 html: prevMonth.getDate()
18680             });
18681             
18682             prevMonth.setDate(prevMonth.getDate()+1);
18683         }
18684           
18685         var currentYear = this.date && this.date.getUTCFullYear();
18686         var currentMonth = this.date && this.date.getUTCMonth();
18687         
18688         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18689         
18690         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18691             v.removeClass('active');
18692             
18693             if(currentYear === year && k === currentMonth){
18694                 v.addClass('active');
18695             }
18696             
18697             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18698                 v.addClass('disabled');
18699             }
18700             
18701         });
18702         
18703         
18704         year = parseInt(year/10, 10) * 10;
18705         
18706         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18707         
18708         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18709         
18710         year -= 1;
18711         for (var i = -1; i < 11; i++) {
18712             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18713                 tag: 'span',
18714                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18715                 html: year
18716             });
18717             
18718             year += 1;
18719         }
18720     },
18721     
18722     showMode: function(dir) 
18723     {
18724         if (dir) {
18725             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18726         }
18727         
18728         Roo.each(this.picker().select('>div',true).elements, function(v){
18729             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18730             v.hide();
18731         });
18732         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18733     },
18734     
18735     place: function()
18736     {
18737         if(this.isInline) {
18738             return;
18739         }
18740         
18741         this.picker().removeClass(['bottom', 'top']);
18742         
18743         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18744             /*
18745              * place to the top of element!
18746              *
18747              */
18748             
18749             this.picker().addClass('top');
18750             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18751             
18752             return;
18753         }
18754         
18755         this.picker().addClass('bottom');
18756         
18757         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18758     },
18759     
18760     parseDate : function(value)
18761     {
18762         if(!value || value instanceof Date){
18763             return value;
18764         }
18765         var v = Date.parseDate(value, this.format);
18766         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18767             v = Date.parseDate(value, 'Y-m-d');
18768         }
18769         if(!v && this.altFormats){
18770             if(!this.altFormatsArray){
18771                 this.altFormatsArray = this.altFormats.split("|");
18772             }
18773             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18774                 v = Date.parseDate(value, this.altFormatsArray[i]);
18775             }
18776         }
18777         return v;
18778     },
18779     
18780     formatDate : function(date, fmt)
18781     {   
18782         return (!date || !(date instanceof Date)) ?
18783         date : date.dateFormat(fmt || this.format);
18784     },
18785     
18786     onFocus : function()
18787     {
18788         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18789         this.show();
18790     },
18791     
18792     onBlur : function()
18793     {
18794         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18795         
18796         var d = this.inputEl().getValue();
18797         
18798         this.setValue(d);
18799                 
18800         this.hide();
18801     },
18802     
18803     show : function()
18804     {
18805         this.picker().show();
18806         this.update();
18807         this.place();
18808         
18809         this.fireEvent('show', this, this.date);
18810     },
18811     
18812     hide : function()
18813     {
18814         if(this.isInline) {
18815             return;
18816         }
18817         this.picker().hide();
18818         this.viewMode = this.startViewMode;
18819         this.showMode();
18820         
18821         this.fireEvent('hide', this, this.date);
18822         
18823     },
18824     
18825     onMousedown: function(e)
18826     {
18827         e.stopPropagation();
18828         e.preventDefault();
18829     },
18830     
18831     keyup: function(e)
18832     {
18833         Roo.bootstrap.DateField.superclass.keyup.call(this);
18834         this.update();
18835     },
18836
18837     setValue: function(v)
18838     {
18839         if(this.fireEvent('beforeselect', this, v) !== false){
18840             var d = new Date(this.parseDate(v) ).clearTime();
18841         
18842             if(isNaN(d.getTime())){
18843                 this.date = this.viewDate = '';
18844                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18845                 return;
18846             }
18847
18848             v = this.formatDate(d);
18849
18850             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18851
18852             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18853
18854             this.update();
18855
18856             this.fireEvent('select', this, this.date);
18857         }
18858     },
18859     
18860     getValue: function()
18861     {
18862         return this.formatDate(this.date);
18863     },
18864     
18865     fireKey: function(e)
18866     {
18867         if (!this.picker().isVisible()){
18868             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18869                 this.show();
18870             }
18871             return;
18872         }
18873         
18874         var dateChanged = false,
18875         dir, day, month,
18876         newDate, newViewDate;
18877         
18878         switch(e.keyCode){
18879             case 27: // escape
18880                 this.hide();
18881                 e.preventDefault();
18882                 break;
18883             case 37: // left
18884             case 39: // right
18885                 if (!this.keyboardNavigation) {
18886                     break;
18887                 }
18888                 dir = e.keyCode == 37 ? -1 : 1;
18889                 
18890                 if (e.ctrlKey){
18891                     newDate = this.moveYear(this.date, dir);
18892                     newViewDate = this.moveYear(this.viewDate, dir);
18893                 } else if (e.shiftKey){
18894                     newDate = this.moveMonth(this.date, dir);
18895                     newViewDate = this.moveMonth(this.viewDate, dir);
18896                 } else {
18897                     newDate = new Date(this.date);
18898                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18899                     newViewDate = new Date(this.viewDate);
18900                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18901                 }
18902                 if (this.dateWithinRange(newDate)){
18903                     this.date = newDate;
18904                     this.viewDate = newViewDate;
18905                     this.setValue(this.formatDate(this.date));
18906 //                    this.update();
18907                     e.preventDefault();
18908                     dateChanged = true;
18909                 }
18910                 break;
18911             case 38: // up
18912             case 40: // down
18913                 if (!this.keyboardNavigation) {
18914                     break;
18915                 }
18916                 dir = e.keyCode == 38 ? -1 : 1;
18917                 if (e.ctrlKey){
18918                     newDate = this.moveYear(this.date, dir);
18919                     newViewDate = this.moveYear(this.viewDate, dir);
18920                 } else if (e.shiftKey){
18921                     newDate = this.moveMonth(this.date, dir);
18922                     newViewDate = this.moveMonth(this.viewDate, dir);
18923                 } else {
18924                     newDate = new Date(this.date);
18925                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18926                     newViewDate = new Date(this.viewDate);
18927                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18928                 }
18929                 if (this.dateWithinRange(newDate)){
18930                     this.date = newDate;
18931                     this.viewDate = newViewDate;
18932                     this.setValue(this.formatDate(this.date));
18933 //                    this.update();
18934                     e.preventDefault();
18935                     dateChanged = true;
18936                 }
18937                 break;
18938             case 13: // enter
18939                 this.setValue(this.formatDate(this.date));
18940                 this.hide();
18941                 e.preventDefault();
18942                 break;
18943             case 9: // tab
18944                 this.setValue(this.formatDate(this.date));
18945                 this.hide();
18946                 break;
18947             case 16: // shift
18948             case 17: // ctrl
18949             case 18: // alt
18950                 break;
18951             default :
18952                 this.hide();
18953                 
18954         }
18955     },
18956     
18957     
18958     onClick: function(e) 
18959     {
18960         e.stopPropagation();
18961         e.preventDefault();
18962         
18963         var target = e.getTarget();
18964         
18965         if(target.nodeName.toLowerCase() === 'i'){
18966             target = Roo.get(target).dom.parentNode;
18967         }
18968         
18969         var nodeName = target.nodeName;
18970         var className = target.className;
18971         var html = target.innerHTML;
18972         //Roo.log(nodeName);
18973         
18974         switch(nodeName.toLowerCase()) {
18975             case 'th':
18976                 switch(className) {
18977                     case 'switch':
18978                         this.showMode(1);
18979                         break;
18980                     case 'prev':
18981                     case 'next':
18982                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18983                         switch(this.viewMode){
18984                                 case 0:
18985                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18986                                         break;
18987                                 case 1:
18988                                 case 2:
18989                                         this.viewDate = this.moveYear(this.viewDate, dir);
18990                                         break;
18991                         }
18992                         this.fill();
18993                         break;
18994                     case 'today':
18995                         var date = new Date();
18996                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18997 //                        this.fill()
18998                         this.setValue(this.formatDate(this.date));
18999                         
19000                         this.hide();
19001                         break;
19002                 }
19003                 break;
19004             case 'span':
19005                 if (className.indexOf('disabled') < 0) {
19006                     this.viewDate.setUTCDate(1);
19007                     if (className.indexOf('month') > -1) {
19008                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19009                     } else {
19010                         var year = parseInt(html, 10) || 0;
19011                         this.viewDate.setUTCFullYear(year);
19012                         
19013                     }
19014                     
19015                     if(this.singleMode){
19016                         this.setValue(this.formatDate(this.viewDate));
19017                         this.hide();
19018                         return;
19019                     }
19020                     
19021                     this.showMode(-1);
19022                     this.fill();
19023                 }
19024                 break;
19025                 
19026             case 'td':
19027                 //Roo.log(className);
19028                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19029                     var day = parseInt(html, 10) || 1;
19030                     var year = this.viewDate.getUTCFullYear(),
19031                         month = this.viewDate.getUTCMonth();
19032
19033                     if (className.indexOf('old') > -1) {
19034                         if(month === 0 ){
19035                             month = 11;
19036                             year -= 1;
19037                         }else{
19038                             month -= 1;
19039                         }
19040                     } else if (className.indexOf('new') > -1) {
19041                         if (month == 11) {
19042                             month = 0;
19043                             year += 1;
19044                         } else {
19045                             month += 1;
19046                         }
19047                     }
19048                     //Roo.log([year,month,day]);
19049                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19050                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19051 //                    this.fill();
19052                     //Roo.log(this.formatDate(this.date));
19053                     this.setValue(this.formatDate(this.date));
19054                     this.hide();
19055                 }
19056                 break;
19057         }
19058     },
19059     
19060     setStartDate: function(startDate)
19061     {
19062         this.startDate = startDate || -Infinity;
19063         if (this.startDate !== -Infinity) {
19064             this.startDate = this.parseDate(this.startDate);
19065         }
19066         this.update();
19067         this.updateNavArrows();
19068     },
19069
19070     setEndDate: function(endDate)
19071     {
19072         this.endDate = endDate || Infinity;
19073         if (this.endDate !== Infinity) {
19074             this.endDate = this.parseDate(this.endDate);
19075         }
19076         this.update();
19077         this.updateNavArrows();
19078     },
19079     
19080     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19081     {
19082         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19083         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19084             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19085         }
19086         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19087             return parseInt(d, 10);
19088         });
19089         this.update();
19090         this.updateNavArrows();
19091     },
19092     
19093     updateNavArrows: function() 
19094     {
19095         if(this.singleMode){
19096             return;
19097         }
19098         
19099         var d = new Date(this.viewDate),
19100         year = d.getUTCFullYear(),
19101         month = d.getUTCMonth();
19102         
19103         Roo.each(this.picker().select('.prev', true).elements, function(v){
19104             v.show();
19105             switch (this.viewMode) {
19106                 case 0:
19107
19108                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19109                         v.hide();
19110                     }
19111                     break;
19112                 case 1:
19113                 case 2:
19114                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19115                         v.hide();
19116                     }
19117                     break;
19118             }
19119         });
19120         
19121         Roo.each(this.picker().select('.next', true).elements, function(v){
19122             v.show();
19123             switch (this.viewMode) {
19124                 case 0:
19125
19126                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19127                         v.hide();
19128                     }
19129                     break;
19130                 case 1:
19131                 case 2:
19132                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19133                         v.hide();
19134                     }
19135                     break;
19136             }
19137         })
19138     },
19139     
19140     moveMonth: function(date, dir)
19141     {
19142         if (!dir) {
19143             return date;
19144         }
19145         var new_date = new Date(date.valueOf()),
19146         day = new_date.getUTCDate(),
19147         month = new_date.getUTCMonth(),
19148         mag = Math.abs(dir),
19149         new_month, test;
19150         dir = dir > 0 ? 1 : -1;
19151         if (mag == 1){
19152             test = dir == -1
19153             // If going back one month, make sure month is not current month
19154             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19155             ? function(){
19156                 return new_date.getUTCMonth() == month;
19157             }
19158             // If going forward one month, make sure month is as expected
19159             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19160             : function(){
19161                 return new_date.getUTCMonth() != new_month;
19162             };
19163             new_month = month + dir;
19164             new_date.setUTCMonth(new_month);
19165             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19166             if (new_month < 0 || new_month > 11) {
19167                 new_month = (new_month + 12) % 12;
19168             }
19169         } else {
19170             // For magnitudes >1, move one month at a time...
19171             for (var i=0; i<mag; i++) {
19172                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19173                 new_date = this.moveMonth(new_date, dir);
19174             }
19175             // ...then reset the day, keeping it in the new month
19176             new_month = new_date.getUTCMonth();
19177             new_date.setUTCDate(day);
19178             test = function(){
19179                 return new_month != new_date.getUTCMonth();
19180             };
19181         }
19182         // Common date-resetting loop -- if date is beyond end of month, make it
19183         // end of month
19184         while (test()){
19185             new_date.setUTCDate(--day);
19186             new_date.setUTCMonth(new_month);
19187         }
19188         return new_date;
19189     },
19190
19191     moveYear: function(date, dir)
19192     {
19193         return this.moveMonth(date, dir*12);
19194     },
19195
19196     dateWithinRange: function(date)
19197     {
19198         return date >= this.startDate && date <= this.endDate;
19199     },
19200
19201     
19202     remove: function() 
19203     {
19204         this.picker().remove();
19205     },
19206     
19207     validateValue : function(value)
19208     {
19209         if(this.getVisibilityEl().hasClass('hidden')){
19210             return true;
19211         }
19212         
19213         if(value.length < 1)  {
19214             if(this.allowBlank){
19215                 return true;
19216             }
19217             return false;
19218         }
19219         
19220         if(value.length < this.minLength){
19221             return false;
19222         }
19223         if(value.length > this.maxLength){
19224             return false;
19225         }
19226         if(this.vtype){
19227             var vt = Roo.form.VTypes;
19228             if(!vt[this.vtype](value, this)){
19229                 return false;
19230             }
19231         }
19232         if(typeof this.validator == "function"){
19233             var msg = this.validator(value);
19234             if(msg !== true){
19235                 return false;
19236             }
19237         }
19238         
19239         if(this.regex && !this.regex.test(value)){
19240             return false;
19241         }
19242         
19243         if(typeof(this.parseDate(value)) == 'undefined'){
19244             return false;
19245         }
19246         
19247         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19248             return false;
19249         }      
19250         
19251         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19252             return false;
19253         } 
19254         
19255         
19256         return true;
19257     },
19258     
19259     setVisible : function(visible)
19260     {
19261         if(!this.getEl()){
19262             return;
19263         }
19264         
19265         this.getEl().removeClass('hidden');
19266         
19267         if(visible){
19268             return;
19269         }
19270         
19271         this.getEl().addClass('hidden');
19272     }
19273    
19274 });
19275
19276 Roo.apply(Roo.bootstrap.DateField,  {
19277     
19278     head : {
19279         tag: 'thead',
19280         cn: [
19281         {
19282             tag: 'tr',
19283             cn: [
19284             {
19285                 tag: 'th',
19286                 cls: 'prev',
19287                 html: '<i class="fa fa-arrow-left"/>'
19288             },
19289             {
19290                 tag: 'th',
19291                 cls: 'switch',
19292                 colspan: '5'
19293             },
19294             {
19295                 tag: 'th',
19296                 cls: 'next',
19297                 html: '<i class="fa fa-arrow-right"/>'
19298             }
19299
19300             ]
19301         }
19302         ]
19303     },
19304     
19305     content : {
19306         tag: 'tbody',
19307         cn: [
19308         {
19309             tag: 'tr',
19310             cn: [
19311             {
19312                 tag: 'td',
19313                 colspan: '7'
19314             }
19315             ]
19316         }
19317         ]
19318     },
19319     
19320     footer : {
19321         tag: 'tfoot',
19322         cn: [
19323         {
19324             tag: 'tr',
19325             cn: [
19326             {
19327                 tag: 'th',
19328                 colspan: '7',
19329                 cls: 'today'
19330             }
19331                     
19332             ]
19333         }
19334         ]
19335     },
19336     
19337     dates:{
19338         en: {
19339             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19340             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19341             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19342             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19343             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19344             today: "Today"
19345         }
19346     },
19347     
19348     modes: [
19349     {
19350         clsName: 'days',
19351         navFnc: 'Month',
19352         navStep: 1
19353     },
19354     {
19355         clsName: 'months',
19356         navFnc: 'FullYear',
19357         navStep: 1
19358     },
19359     {
19360         clsName: 'years',
19361         navFnc: 'FullYear',
19362         navStep: 10
19363     }]
19364 });
19365
19366 Roo.apply(Roo.bootstrap.DateField,  {
19367   
19368     template : {
19369         tag: 'div',
19370         cls: 'datepicker dropdown-menu roo-dynamic',
19371         cn: [
19372         {
19373             tag: 'div',
19374             cls: 'datepicker-days',
19375             cn: [
19376             {
19377                 tag: 'table',
19378                 cls: 'table-condensed',
19379                 cn:[
19380                 Roo.bootstrap.DateField.head,
19381                 {
19382                     tag: 'tbody'
19383                 },
19384                 Roo.bootstrap.DateField.footer
19385                 ]
19386             }
19387             ]
19388         },
19389         {
19390             tag: 'div',
19391             cls: 'datepicker-months',
19392             cn: [
19393             {
19394                 tag: 'table',
19395                 cls: 'table-condensed',
19396                 cn:[
19397                 Roo.bootstrap.DateField.head,
19398                 Roo.bootstrap.DateField.content,
19399                 Roo.bootstrap.DateField.footer
19400                 ]
19401             }
19402             ]
19403         },
19404         {
19405             tag: 'div',
19406             cls: 'datepicker-years',
19407             cn: [
19408             {
19409                 tag: 'table',
19410                 cls: 'table-condensed',
19411                 cn:[
19412                 Roo.bootstrap.DateField.head,
19413                 Roo.bootstrap.DateField.content,
19414                 Roo.bootstrap.DateField.footer
19415                 ]
19416             }
19417             ]
19418         }
19419         ]
19420     }
19421 });
19422
19423  
19424
19425  /*
19426  * - LGPL
19427  *
19428  * TimeField
19429  * 
19430  */
19431
19432 /**
19433  * @class Roo.bootstrap.TimeField
19434  * @extends Roo.bootstrap.Input
19435  * Bootstrap DateField class
19436  * 
19437  * 
19438  * @constructor
19439  * Create a new TimeField
19440  * @param {Object} config The config object
19441  */
19442
19443 Roo.bootstrap.TimeField = function(config){
19444     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19445     this.addEvents({
19446             /**
19447              * @event show
19448              * Fires when this field show.
19449              * @param {Roo.bootstrap.DateField} thisthis
19450              * @param {Mixed} date The date value
19451              */
19452             show : true,
19453             /**
19454              * @event show
19455              * Fires when this field hide.
19456              * @param {Roo.bootstrap.DateField} this
19457              * @param {Mixed} date The date value
19458              */
19459             hide : true,
19460             /**
19461              * @event select
19462              * Fires when select a date.
19463              * @param {Roo.bootstrap.DateField} this
19464              * @param {Mixed} date The date value
19465              */
19466             select : true
19467         });
19468 };
19469
19470 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19471     
19472     /**
19473      * @cfg {String} format
19474      * The default time format string which can be overriden for localization support.  The format must be
19475      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19476      */
19477     format : "H:i",
19478        
19479     onRender: function(ct, position)
19480     {
19481         
19482         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19483                 
19484         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19485         
19486         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19487         
19488         this.pop = this.picker().select('>.datepicker-time',true).first();
19489         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19490         
19491         this.picker().on('mousedown', this.onMousedown, this);
19492         this.picker().on('click', this.onClick, this);
19493         
19494         this.picker().addClass('datepicker-dropdown');
19495     
19496         this.fillTime();
19497         this.update();
19498             
19499         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19500         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19501         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19502         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19503         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19504         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19505
19506     },
19507     
19508     fireKey: function(e){
19509         if (!this.picker().isVisible()){
19510             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19511                 this.show();
19512             }
19513             return;
19514         }
19515
19516         e.preventDefault();
19517         
19518         switch(e.keyCode){
19519             case 27: // escape
19520                 this.hide();
19521                 break;
19522             case 37: // left
19523             case 39: // right
19524                 this.onTogglePeriod();
19525                 break;
19526             case 38: // up
19527                 this.onIncrementMinutes();
19528                 break;
19529             case 40: // down
19530                 this.onDecrementMinutes();
19531                 break;
19532             case 13: // enter
19533             case 9: // tab
19534                 this.setTime();
19535                 break;
19536         }
19537     },
19538     
19539     onClick: function(e) {
19540         e.stopPropagation();
19541         e.preventDefault();
19542     },
19543     
19544     picker : function()
19545     {
19546         return this.el.select('.datepicker', true).first();
19547     },
19548     
19549     fillTime: function()
19550     {    
19551         var time = this.pop.select('tbody', true).first();
19552         
19553         time.dom.innerHTML = '';
19554         
19555         time.createChild({
19556             tag: 'tr',
19557             cn: [
19558                 {
19559                     tag: 'td',
19560                     cn: [
19561                         {
19562                             tag: 'a',
19563                             href: '#',
19564                             cls: 'btn',
19565                             cn: [
19566                                 {
19567                                     tag: 'span',
19568                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19569                                 }
19570                             ]
19571                         } 
19572                     ]
19573                 },
19574                 {
19575                     tag: 'td',
19576                     cls: 'separator'
19577                 },
19578                 {
19579                     tag: 'td',
19580                     cn: [
19581                         {
19582                             tag: 'a',
19583                             href: '#',
19584                             cls: 'btn',
19585                             cn: [
19586                                 {
19587                                     tag: 'span',
19588                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19589                                 }
19590                             ]
19591                         }
19592                     ]
19593                 },
19594                 {
19595                     tag: 'td',
19596                     cls: 'separator'
19597                 }
19598             ]
19599         });
19600         
19601         time.createChild({
19602             tag: 'tr',
19603             cn: [
19604                 {
19605                     tag: 'td',
19606                     cn: [
19607                         {
19608                             tag: 'span',
19609                             cls: 'timepicker-hour',
19610                             html: '00'
19611                         }  
19612                     ]
19613                 },
19614                 {
19615                     tag: 'td',
19616                     cls: 'separator',
19617                     html: ':'
19618                 },
19619                 {
19620                     tag: 'td',
19621                     cn: [
19622                         {
19623                             tag: 'span',
19624                             cls: 'timepicker-minute',
19625                             html: '00'
19626                         }  
19627                     ]
19628                 },
19629                 {
19630                     tag: 'td',
19631                     cls: 'separator'
19632                 },
19633                 {
19634                     tag: 'td',
19635                     cn: [
19636                         {
19637                             tag: 'button',
19638                             type: 'button',
19639                             cls: 'btn btn-primary period',
19640                             html: 'AM'
19641                             
19642                         }
19643                     ]
19644                 }
19645             ]
19646         });
19647         
19648         time.createChild({
19649             tag: 'tr',
19650             cn: [
19651                 {
19652                     tag: 'td',
19653                     cn: [
19654                         {
19655                             tag: 'a',
19656                             href: '#',
19657                             cls: 'btn',
19658                             cn: [
19659                                 {
19660                                     tag: 'span',
19661                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19662                                 }
19663                             ]
19664                         }
19665                     ]
19666                 },
19667                 {
19668                     tag: 'td',
19669                     cls: 'separator'
19670                 },
19671                 {
19672                     tag: 'td',
19673                     cn: [
19674                         {
19675                             tag: 'a',
19676                             href: '#',
19677                             cls: 'btn',
19678                             cn: [
19679                                 {
19680                                     tag: 'span',
19681                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19682                                 }
19683                             ]
19684                         }
19685                     ]
19686                 },
19687                 {
19688                     tag: 'td',
19689                     cls: 'separator'
19690                 }
19691             ]
19692         });
19693         
19694     },
19695     
19696     update: function()
19697     {
19698         
19699         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19700         
19701         this.fill();
19702     },
19703     
19704     fill: function() 
19705     {
19706         var hours = this.time.getHours();
19707         var minutes = this.time.getMinutes();
19708         var period = 'AM';
19709         
19710         if(hours > 11){
19711             period = 'PM';
19712         }
19713         
19714         if(hours == 0){
19715             hours = 12;
19716         }
19717         
19718         
19719         if(hours > 12){
19720             hours = hours - 12;
19721         }
19722         
19723         if(hours < 10){
19724             hours = '0' + hours;
19725         }
19726         
19727         if(minutes < 10){
19728             minutes = '0' + minutes;
19729         }
19730         
19731         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19732         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19733         this.pop.select('button', true).first().dom.innerHTML = period;
19734         
19735     },
19736     
19737     place: function()
19738     {   
19739         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19740         
19741         var cls = ['bottom'];
19742         
19743         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19744             cls.pop();
19745             cls.push('top');
19746         }
19747         
19748         cls.push('right');
19749         
19750         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19751             cls.pop();
19752             cls.push('left');
19753         }
19754         
19755         this.picker().addClass(cls.join('-'));
19756         
19757         var _this = this;
19758         
19759         Roo.each(cls, function(c){
19760             if(c == 'bottom'){
19761                 _this.picker().setTop(_this.inputEl().getHeight());
19762                 return;
19763             }
19764             if(c == 'top'){
19765                 _this.picker().setTop(0 - _this.picker().getHeight());
19766                 return;
19767             }
19768             
19769             if(c == 'left'){
19770                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19771                 return;
19772             }
19773             if(c == 'right'){
19774                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19775                 return;
19776             }
19777         });
19778         
19779     },
19780   
19781     onFocus : function()
19782     {
19783         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19784         this.show();
19785     },
19786     
19787     onBlur : function()
19788     {
19789         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19790         this.hide();
19791     },
19792     
19793     show : function()
19794     {
19795         this.picker().show();
19796         this.pop.show();
19797         this.update();
19798         this.place();
19799         
19800         this.fireEvent('show', this, this.date);
19801     },
19802     
19803     hide : function()
19804     {
19805         this.picker().hide();
19806         this.pop.hide();
19807         
19808         this.fireEvent('hide', this, this.date);
19809     },
19810     
19811     setTime : function()
19812     {
19813         this.hide();
19814         this.setValue(this.time.format(this.format));
19815         
19816         this.fireEvent('select', this, this.date);
19817         
19818         
19819     },
19820     
19821     onMousedown: function(e){
19822         e.stopPropagation();
19823         e.preventDefault();
19824     },
19825     
19826     onIncrementHours: function()
19827     {
19828         Roo.log('onIncrementHours');
19829         this.time = this.time.add(Date.HOUR, 1);
19830         this.update();
19831         
19832     },
19833     
19834     onDecrementHours: function()
19835     {
19836         Roo.log('onDecrementHours');
19837         this.time = this.time.add(Date.HOUR, -1);
19838         this.update();
19839     },
19840     
19841     onIncrementMinutes: function()
19842     {
19843         Roo.log('onIncrementMinutes');
19844         this.time = this.time.add(Date.MINUTE, 1);
19845         this.update();
19846     },
19847     
19848     onDecrementMinutes: function()
19849     {
19850         Roo.log('onDecrementMinutes');
19851         this.time = this.time.add(Date.MINUTE, -1);
19852         this.update();
19853     },
19854     
19855     onTogglePeriod: function()
19856     {
19857         Roo.log('onTogglePeriod');
19858         this.time = this.time.add(Date.HOUR, 12);
19859         this.update();
19860     }
19861     
19862    
19863 });
19864
19865 Roo.apply(Roo.bootstrap.TimeField,  {
19866     
19867     content : {
19868         tag: 'tbody',
19869         cn: [
19870             {
19871                 tag: 'tr',
19872                 cn: [
19873                 {
19874                     tag: 'td',
19875                     colspan: '7'
19876                 }
19877                 ]
19878             }
19879         ]
19880     },
19881     
19882     footer : {
19883         tag: 'tfoot',
19884         cn: [
19885             {
19886                 tag: 'tr',
19887                 cn: [
19888                 {
19889                     tag: 'th',
19890                     colspan: '7',
19891                     cls: '',
19892                     cn: [
19893                         {
19894                             tag: 'button',
19895                             cls: 'btn btn-info ok',
19896                             html: 'OK'
19897                         }
19898                     ]
19899                 }
19900
19901                 ]
19902             }
19903         ]
19904     }
19905 });
19906
19907 Roo.apply(Roo.bootstrap.TimeField,  {
19908   
19909     template : {
19910         tag: 'div',
19911         cls: 'datepicker dropdown-menu',
19912         cn: [
19913             {
19914                 tag: 'div',
19915                 cls: 'datepicker-time',
19916                 cn: [
19917                 {
19918                     tag: 'table',
19919                     cls: 'table-condensed',
19920                     cn:[
19921                     Roo.bootstrap.TimeField.content,
19922                     Roo.bootstrap.TimeField.footer
19923                     ]
19924                 }
19925                 ]
19926             }
19927         ]
19928     }
19929 });
19930
19931  
19932
19933  /*
19934  * - LGPL
19935  *
19936  * MonthField
19937  * 
19938  */
19939
19940 /**
19941  * @class Roo.bootstrap.MonthField
19942  * @extends Roo.bootstrap.Input
19943  * Bootstrap MonthField class
19944  * 
19945  * @cfg {String} language default en
19946  * 
19947  * @constructor
19948  * Create a new MonthField
19949  * @param {Object} config The config object
19950  */
19951
19952 Roo.bootstrap.MonthField = function(config){
19953     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19954     
19955     this.addEvents({
19956         /**
19957          * @event show
19958          * Fires when this field show.
19959          * @param {Roo.bootstrap.MonthField} this
19960          * @param {Mixed} date The date value
19961          */
19962         show : true,
19963         /**
19964          * @event show
19965          * Fires when this field hide.
19966          * @param {Roo.bootstrap.MonthField} this
19967          * @param {Mixed} date The date value
19968          */
19969         hide : true,
19970         /**
19971          * @event select
19972          * Fires when select a date.
19973          * @param {Roo.bootstrap.MonthField} this
19974          * @param {String} oldvalue The old value
19975          * @param {String} newvalue The new value
19976          */
19977         select : true
19978     });
19979 };
19980
19981 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19982     
19983     onRender: function(ct, position)
19984     {
19985         
19986         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19987         
19988         this.language = this.language || 'en';
19989         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19990         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19991         
19992         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19993         this.isInline = false;
19994         this.isInput = true;
19995         this.component = this.el.select('.add-on', true).first() || false;
19996         this.component = (this.component && this.component.length === 0) ? false : this.component;
19997         this.hasInput = this.component && this.inputEL().length;
19998         
19999         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20000         
20001         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20002         
20003         this.picker().on('mousedown', this.onMousedown, this);
20004         this.picker().on('click', this.onClick, this);
20005         
20006         this.picker().addClass('datepicker-dropdown');
20007         
20008         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20009             v.setStyle('width', '189px');
20010         });
20011         
20012         this.fillMonths();
20013         
20014         this.update();
20015         
20016         if(this.isInline) {
20017             this.show();
20018         }
20019         
20020     },
20021     
20022     setValue: function(v, suppressEvent)
20023     {   
20024         var o = this.getValue();
20025         
20026         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20027         
20028         this.update();
20029
20030         if(suppressEvent !== true){
20031             this.fireEvent('select', this, o, v);
20032         }
20033         
20034     },
20035     
20036     getValue: function()
20037     {
20038         return this.value;
20039     },
20040     
20041     onClick: function(e) 
20042     {
20043         e.stopPropagation();
20044         e.preventDefault();
20045         
20046         var target = e.getTarget();
20047         
20048         if(target.nodeName.toLowerCase() === 'i'){
20049             target = Roo.get(target).dom.parentNode;
20050         }
20051         
20052         var nodeName = target.nodeName;
20053         var className = target.className;
20054         var html = target.innerHTML;
20055         
20056         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20057             return;
20058         }
20059         
20060         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20061         
20062         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20063         
20064         this.hide();
20065                         
20066     },
20067     
20068     picker : function()
20069     {
20070         return this.pickerEl;
20071     },
20072     
20073     fillMonths: function()
20074     {    
20075         var i = 0;
20076         var months = this.picker().select('>.datepicker-months td', true).first();
20077         
20078         months.dom.innerHTML = '';
20079         
20080         while (i < 12) {
20081             var month = {
20082                 tag: 'span',
20083                 cls: 'month',
20084                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20085             };
20086             
20087             months.createChild(month);
20088         }
20089         
20090     },
20091     
20092     update: function()
20093     {
20094         var _this = this;
20095         
20096         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20097             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20098         }
20099         
20100         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20101             e.removeClass('active');
20102             
20103             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20104                 e.addClass('active');
20105             }
20106         })
20107     },
20108     
20109     place: function()
20110     {
20111         if(this.isInline) {
20112             return;
20113         }
20114         
20115         this.picker().removeClass(['bottom', 'top']);
20116         
20117         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20118             /*
20119              * place to the top of element!
20120              *
20121              */
20122             
20123             this.picker().addClass('top');
20124             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20125             
20126             return;
20127         }
20128         
20129         this.picker().addClass('bottom');
20130         
20131         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20132     },
20133     
20134     onFocus : function()
20135     {
20136         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20137         this.show();
20138     },
20139     
20140     onBlur : function()
20141     {
20142         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20143         
20144         var d = this.inputEl().getValue();
20145         
20146         this.setValue(d);
20147                 
20148         this.hide();
20149     },
20150     
20151     show : function()
20152     {
20153         this.picker().show();
20154         this.picker().select('>.datepicker-months', true).first().show();
20155         this.update();
20156         this.place();
20157         
20158         this.fireEvent('show', this, this.date);
20159     },
20160     
20161     hide : function()
20162     {
20163         if(this.isInline) {
20164             return;
20165         }
20166         this.picker().hide();
20167         this.fireEvent('hide', this, this.date);
20168         
20169     },
20170     
20171     onMousedown: function(e)
20172     {
20173         e.stopPropagation();
20174         e.preventDefault();
20175     },
20176     
20177     keyup: function(e)
20178     {
20179         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20180         this.update();
20181     },
20182
20183     fireKey: function(e)
20184     {
20185         if (!this.picker().isVisible()){
20186             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20187                 this.show();
20188             }
20189             return;
20190         }
20191         
20192         var dir;
20193         
20194         switch(e.keyCode){
20195             case 27: // escape
20196                 this.hide();
20197                 e.preventDefault();
20198                 break;
20199             case 37: // left
20200             case 39: // right
20201                 dir = e.keyCode == 37 ? -1 : 1;
20202                 
20203                 this.vIndex = this.vIndex + dir;
20204                 
20205                 if(this.vIndex < 0){
20206                     this.vIndex = 0;
20207                 }
20208                 
20209                 if(this.vIndex > 11){
20210                     this.vIndex = 11;
20211                 }
20212                 
20213                 if(isNaN(this.vIndex)){
20214                     this.vIndex = 0;
20215                 }
20216                 
20217                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20218                 
20219                 break;
20220             case 38: // up
20221             case 40: // down
20222                 
20223                 dir = e.keyCode == 38 ? -1 : 1;
20224                 
20225                 this.vIndex = this.vIndex + dir * 4;
20226                 
20227                 if(this.vIndex < 0){
20228                     this.vIndex = 0;
20229                 }
20230                 
20231                 if(this.vIndex > 11){
20232                     this.vIndex = 11;
20233                 }
20234                 
20235                 if(isNaN(this.vIndex)){
20236                     this.vIndex = 0;
20237                 }
20238                 
20239                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20240                 break;
20241                 
20242             case 13: // enter
20243                 
20244                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20245                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20246                 }
20247                 
20248                 this.hide();
20249                 e.preventDefault();
20250                 break;
20251             case 9: // tab
20252                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20253                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20254                 }
20255                 this.hide();
20256                 break;
20257             case 16: // shift
20258             case 17: // ctrl
20259             case 18: // alt
20260                 break;
20261             default :
20262                 this.hide();
20263                 
20264         }
20265     },
20266     
20267     remove: function() 
20268     {
20269         this.picker().remove();
20270     }
20271    
20272 });
20273
20274 Roo.apply(Roo.bootstrap.MonthField,  {
20275     
20276     content : {
20277         tag: 'tbody',
20278         cn: [
20279         {
20280             tag: 'tr',
20281             cn: [
20282             {
20283                 tag: 'td',
20284                 colspan: '7'
20285             }
20286             ]
20287         }
20288         ]
20289     },
20290     
20291     dates:{
20292         en: {
20293             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20294             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20295         }
20296     }
20297 });
20298
20299 Roo.apply(Roo.bootstrap.MonthField,  {
20300   
20301     template : {
20302         tag: 'div',
20303         cls: 'datepicker dropdown-menu roo-dynamic',
20304         cn: [
20305             {
20306                 tag: 'div',
20307                 cls: 'datepicker-months',
20308                 cn: [
20309                 {
20310                     tag: 'table',
20311                     cls: 'table-condensed',
20312                     cn:[
20313                         Roo.bootstrap.DateField.content
20314                     ]
20315                 }
20316                 ]
20317             }
20318         ]
20319     }
20320 });
20321
20322  
20323
20324  
20325  /*
20326  * - LGPL
20327  *
20328  * CheckBox
20329  * 
20330  */
20331
20332 /**
20333  * @class Roo.bootstrap.CheckBox
20334  * @extends Roo.bootstrap.Input
20335  * Bootstrap CheckBox class
20336  * 
20337  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20338  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20339  * @cfg {String} boxLabel The text that appears beside the checkbox
20340  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20341  * @cfg {Boolean} checked initnal the element
20342  * @cfg {Boolean} inline inline the element (default false)
20343  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20344  * @cfg {String} tooltip label tooltip
20345  * 
20346  * @constructor
20347  * Create a new CheckBox
20348  * @param {Object} config The config object
20349  */
20350
20351 Roo.bootstrap.CheckBox = function(config){
20352     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20353    
20354     this.addEvents({
20355         /**
20356         * @event check
20357         * Fires when the element is checked or unchecked.
20358         * @param {Roo.bootstrap.CheckBox} this This input
20359         * @param {Boolean} checked The new checked value
20360         */
20361        check : true,
20362        /**
20363         * @event click
20364         * Fires when the element is click.
20365         * @param {Roo.bootstrap.CheckBox} this This input
20366         */
20367        click : true
20368     });
20369     
20370 };
20371
20372 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20373   
20374     inputType: 'checkbox',
20375     inputValue: 1,
20376     valueOff: 0,
20377     boxLabel: false,
20378     checked: false,
20379     weight : false,
20380     inline: false,
20381     tooltip : '',
20382     
20383     getAutoCreate : function()
20384     {
20385         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20386         
20387         var id = Roo.id();
20388         
20389         var cfg = {};
20390         
20391         cfg.cls = 'form-group ' + this.inputType; //input-group
20392         
20393         if(this.inline){
20394             cfg.cls += ' ' + this.inputType + '-inline';
20395         }
20396         
20397         var input =  {
20398             tag: 'input',
20399             id : id,
20400             type : this.inputType,
20401             value : this.inputValue,
20402             cls : 'roo-' + this.inputType, //'form-box',
20403             placeholder : this.placeholder || ''
20404             
20405         };
20406         
20407         if(this.inputType != 'radio'){
20408             var hidden =  {
20409                 tag: 'input',
20410                 type : 'hidden',
20411                 cls : 'roo-hidden-value',
20412                 value : this.checked ? this.inputValue : this.valueOff
20413             };
20414         }
20415         
20416             
20417         if (this.weight) { // Validity check?
20418             cfg.cls += " " + this.inputType + "-" + this.weight;
20419         }
20420         
20421         if (this.disabled) {
20422             input.disabled=true;
20423         }
20424         
20425         if(this.checked){
20426             input.checked = this.checked;
20427         }
20428         
20429         if (this.name) {
20430             
20431             input.name = this.name;
20432             
20433             if(this.inputType != 'radio'){
20434                 hidden.name = this.name;
20435                 input.name = '_hidden_' + this.name;
20436             }
20437         }
20438         
20439         if (this.size) {
20440             input.cls += ' input-' + this.size;
20441         }
20442         
20443         var settings=this;
20444         
20445         ['xs','sm','md','lg'].map(function(size){
20446             if (settings[size]) {
20447                 cfg.cls += ' col-' + size + '-' + settings[size];
20448             }
20449         });
20450         
20451         var inputblock = input;
20452          
20453         if (this.before || this.after) {
20454             
20455             inputblock = {
20456                 cls : 'input-group',
20457                 cn :  [] 
20458             };
20459             
20460             if (this.before) {
20461                 inputblock.cn.push({
20462                     tag :'span',
20463                     cls : 'input-group-addon',
20464                     html : this.before
20465                 });
20466             }
20467             
20468             inputblock.cn.push(input);
20469             
20470             if(this.inputType != 'radio'){
20471                 inputblock.cn.push(hidden);
20472             }
20473             
20474             if (this.after) {
20475                 inputblock.cn.push({
20476                     tag :'span',
20477                     cls : 'input-group-addon',
20478                     html : this.after
20479                 });
20480             }
20481             
20482         }
20483         
20484         if (align ==='left' && this.fieldLabel.length) {
20485 //                Roo.log("left and has label");
20486             cfg.cn = [
20487                 {
20488                     tag: 'label',
20489                     'for' :  id,
20490                     cls : 'control-label',
20491                     html : this.fieldLabel
20492                 },
20493                 {
20494                     cls : "", 
20495                     cn: [
20496                         inputblock
20497                     ]
20498                 }
20499             ];
20500             
20501             if(this.labelWidth > 12){
20502                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20503             }
20504             
20505             if(this.labelWidth < 13 && this.labelmd == 0){
20506                 this.labelmd = this.labelWidth;
20507             }
20508             
20509             if(this.labellg > 0){
20510                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20511                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20512             }
20513             
20514             if(this.labelmd > 0){
20515                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20516                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20517             }
20518             
20519             if(this.labelsm > 0){
20520                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20521                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20522             }
20523             
20524             if(this.labelxs > 0){
20525                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20526                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20527             }
20528             
20529         } else if ( this.fieldLabel.length) {
20530 //                Roo.log(" label");
20531                 cfg.cn = [
20532                    
20533                     {
20534                         tag: this.boxLabel ? 'span' : 'label',
20535                         'for': id,
20536                         cls: 'control-label box-input-label',
20537                         //cls : 'input-group-addon',
20538                         html : this.fieldLabel
20539                     },
20540                     
20541                     inputblock
20542                     
20543                 ];
20544
20545         } else {
20546             
20547 //                Roo.log(" no label && no align");
20548                 cfg.cn = [  inputblock ] ;
20549                 
20550                 
20551         }
20552         
20553         if(this.boxLabel){
20554              var boxLabelCfg = {
20555                 tag: 'label',
20556                 //'for': id, // box label is handled by onclick - so no for...
20557                 cls: 'box-label',
20558                 html: this.boxLabel
20559             };
20560             
20561             if(this.tooltip){
20562                 boxLabelCfg.tooltip = this.tooltip;
20563             }
20564              
20565             cfg.cn.push(boxLabelCfg);
20566         }
20567         
20568         if(this.inputType != 'radio'){
20569             cfg.cn.push(hidden);
20570         }
20571         
20572         return cfg;
20573         
20574     },
20575     
20576     /**
20577      * return the real input element.
20578      */
20579     inputEl: function ()
20580     {
20581         return this.el.select('input.roo-' + this.inputType,true).first();
20582     },
20583     hiddenEl: function ()
20584     {
20585         return this.el.select('input.roo-hidden-value',true).first();
20586     },
20587     
20588     labelEl: function()
20589     {
20590         return this.el.select('label.control-label',true).first();
20591     },
20592     /* depricated... */
20593     
20594     label: function()
20595     {
20596         return this.labelEl();
20597     },
20598     
20599     boxLabelEl: function()
20600     {
20601         return this.el.select('label.box-label',true).first();
20602     },
20603     
20604     initEvents : function()
20605     {
20606 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20607         
20608         this.inputEl().on('click', this.onClick,  this);
20609         
20610         if (this.boxLabel) { 
20611             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20612         }
20613         
20614         this.startValue = this.getValue();
20615         
20616         if(this.groupId){
20617             Roo.bootstrap.CheckBox.register(this);
20618         }
20619     },
20620     
20621     onClick : function(e)
20622     {   
20623         if(this.fireEvent('click', this, e) !== false){
20624             this.setChecked(!this.checked);
20625         }
20626         
20627     },
20628     
20629     setChecked : function(state,suppressEvent)
20630     {
20631         this.startValue = this.getValue();
20632
20633         if(this.inputType == 'radio'){
20634             
20635             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20636                 e.dom.checked = false;
20637             });
20638             
20639             this.inputEl().dom.checked = true;
20640             
20641             this.inputEl().dom.value = this.inputValue;
20642             
20643             if(suppressEvent !== true){
20644                 this.fireEvent('check', this, true);
20645             }
20646             
20647             this.validate();
20648             
20649             return;
20650         }
20651         
20652         this.checked = state;
20653         
20654         this.inputEl().dom.checked = state;
20655         
20656         
20657         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20658         
20659         if(suppressEvent !== true){
20660             this.fireEvent('check', this, state);
20661         }
20662         
20663         this.validate();
20664     },
20665     
20666     getValue : function()
20667     {
20668         if(this.inputType == 'radio'){
20669             return this.getGroupValue();
20670         }
20671         
20672         return this.hiddenEl().dom.value;
20673         
20674     },
20675     
20676     getGroupValue : function()
20677     {
20678         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20679             return '';
20680         }
20681         
20682         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20683     },
20684     
20685     setValue : function(v,suppressEvent)
20686     {
20687         if(this.inputType == 'radio'){
20688             this.setGroupValue(v, suppressEvent);
20689             return;
20690         }
20691         
20692         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20693         
20694         this.validate();
20695     },
20696     
20697     setGroupValue : function(v, suppressEvent)
20698     {
20699         this.startValue = this.getValue();
20700         
20701         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20702             e.dom.checked = false;
20703             
20704             if(e.dom.value == v){
20705                 e.dom.checked = true;
20706             }
20707         });
20708         
20709         if(suppressEvent !== true){
20710             this.fireEvent('check', this, true);
20711         }
20712
20713         this.validate();
20714         
20715         return;
20716     },
20717     
20718     validate : function()
20719     {
20720         if(this.getVisibilityEl().hasClass('hidden')){
20721             return true;
20722         }
20723         
20724         if(
20725                 this.disabled || 
20726                 (this.inputType == 'radio' && this.validateRadio()) ||
20727                 (this.inputType == 'checkbox' && this.validateCheckbox())
20728         ){
20729             this.markValid();
20730             return true;
20731         }
20732         
20733         this.markInvalid();
20734         return false;
20735     },
20736     
20737     validateRadio : function()
20738     {
20739         if(this.getVisibilityEl().hasClass('hidden')){
20740             return true;
20741         }
20742         
20743         if(this.allowBlank){
20744             return true;
20745         }
20746         
20747         var valid = false;
20748         
20749         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20750             if(!e.dom.checked){
20751                 return;
20752             }
20753             
20754             valid = true;
20755             
20756             return false;
20757         });
20758         
20759         return valid;
20760     },
20761     
20762     validateCheckbox : function()
20763     {
20764         if(!this.groupId){
20765             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20766             //return (this.getValue() == this.inputValue) ? true : false;
20767         }
20768         
20769         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20770         
20771         if(!group){
20772             return false;
20773         }
20774         
20775         var r = false;
20776         
20777         for(var i in group){
20778             if(group[i].el.isVisible(true)){
20779                 r = false;
20780                 break;
20781             }
20782             
20783             r = true;
20784         }
20785         
20786         for(var i in group){
20787             if(r){
20788                 break;
20789             }
20790             
20791             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20792         }
20793         
20794         return r;
20795     },
20796     
20797     /**
20798      * Mark this field as valid
20799      */
20800     markValid : function()
20801     {
20802         var _this = this;
20803         
20804         this.fireEvent('valid', this);
20805         
20806         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20807         
20808         if(this.groupId){
20809             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20810         }
20811         
20812         if(label){
20813             label.markValid();
20814         }
20815
20816         if(this.inputType == 'radio'){
20817             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20818                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20819                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20820             });
20821             
20822             return;
20823         }
20824
20825         if(!this.groupId){
20826             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20827             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20828             return;
20829         }
20830         
20831         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20832         
20833         if(!group){
20834             return;
20835         }
20836         
20837         for(var i in group){
20838             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20839             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20840         }
20841     },
20842     
20843      /**
20844      * Mark this field as invalid
20845      * @param {String} msg The validation message
20846      */
20847     markInvalid : function(msg)
20848     {
20849         if(this.allowBlank){
20850             return;
20851         }
20852         
20853         var _this = this;
20854         
20855         this.fireEvent('invalid', this, msg);
20856         
20857         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20858         
20859         if(this.groupId){
20860             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20861         }
20862         
20863         if(label){
20864             label.markInvalid();
20865         }
20866             
20867         if(this.inputType == 'radio'){
20868             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20869                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20870                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20871             });
20872             
20873             return;
20874         }
20875         
20876         if(!this.groupId){
20877             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20878             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20879             return;
20880         }
20881         
20882         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20883         
20884         if(!group){
20885             return;
20886         }
20887         
20888         for(var i in group){
20889             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20890             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20891         }
20892         
20893     },
20894     
20895     clearInvalid : function()
20896     {
20897         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20898         
20899         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20900         
20901         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20902         
20903         if (label && label.iconEl) {
20904             label.iconEl.removeClass(label.validClass);
20905             label.iconEl.removeClass(label.invalidClass);
20906         }
20907     },
20908     
20909     disable : function()
20910     {
20911         if(this.inputType != 'radio'){
20912             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20913             return;
20914         }
20915         
20916         var _this = this;
20917         
20918         if(this.rendered){
20919             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20920                 _this.getActionEl().addClass(this.disabledClass);
20921                 e.dom.disabled = true;
20922             });
20923         }
20924         
20925         this.disabled = true;
20926         this.fireEvent("disable", this);
20927         return this;
20928     },
20929
20930     enable : function()
20931     {
20932         if(this.inputType != 'radio'){
20933             Roo.bootstrap.CheckBox.superclass.enable.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().removeClass(this.disabledClass);
20942                 e.dom.disabled = false;
20943             });
20944         }
20945         
20946         this.disabled = false;
20947         this.fireEvent("enable", this);
20948         return this;
20949     },
20950     
20951     setBoxLabel : function(v)
20952     {
20953         this.boxLabel = v;
20954         
20955         if(this.rendered){
20956             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20957         }
20958     }
20959
20960 });
20961
20962 Roo.apply(Roo.bootstrap.CheckBox, {
20963     
20964     groups: {},
20965     
20966      /**
20967     * register a CheckBox Group
20968     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20969     */
20970     register : function(checkbox)
20971     {
20972         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20973             this.groups[checkbox.groupId] = {};
20974         }
20975         
20976         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20977             return;
20978         }
20979         
20980         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20981         
20982     },
20983     /**
20984     * fetch a CheckBox Group based on the group ID
20985     * @param {string} the group ID
20986     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20987     */
20988     get: function(groupId) {
20989         if (typeof(this.groups[groupId]) == 'undefined') {
20990             return false;
20991         }
20992         
20993         return this.groups[groupId] ;
20994     }
20995     
20996     
20997 });
20998 /*
20999  * - LGPL
21000  *
21001  * RadioItem
21002  * 
21003  */
21004
21005 /**
21006  * @class Roo.bootstrap.Radio
21007  * @extends Roo.bootstrap.Component
21008  * Bootstrap Radio class
21009  * @cfg {String} boxLabel - the label associated
21010  * @cfg {String} value - the value of radio
21011  * 
21012  * @constructor
21013  * Create a new Radio
21014  * @param {Object} config The config object
21015  */
21016 Roo.bootstrap.Radio = function(config){
21017     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21018     
21019 };
21020
21021 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21022     
21023     boxLabel : '',
21024     
21025     value : '',
21026     
21027     getAutoCreate : function()
21028     {
21029         var cfg = {
21030             tag : 'div',
21031             cls : 'form-group radio',
21032             cn : [
21033                 {
21034                     tag : 'label',
21035                     cls : 'box-label',
21036                     html : this.boxLabel
21037                 }
21038             ]
21039         };
21040         
21041         return cfg;
21042     },
21043     
21044     initEvents : function() 
21045     {
21046         this.parent().register(this);
21047         
21048         this.el.on('click', this.onClick, this);
21049         
21050     },
21051     
21052     onClick : function(e)
21053     {
21054         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21055             this.setChecked(true);
21056         }
21057     },
21058     
21059     setChecked : function(state, suppressEvent)
21060     {
21061         this.parent().setValue(this.value, suppressEvent);
21062         
21063     },
21064     
21065     setBoxLabel : function(v)
21066     {
21067         this.boxLabel = v;
21068         
21069         if(this.rendered){
21070             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21071         }
21072     }
21073     
21074 });
21075  
21076
21077  /*
21078  * - LGPL
21079  *
21080  * Input
21081  * 
21082  */
21083
21084 /**
21085  * @class Roo.bootstrap.SecurePass
21086  * @extends Roo.bootstrap.Input
21087  * Bootstrap SecurePass class
21088  *
21089  * 
21090  * @constructor
21091  * Create a new SecurePass
21092  * @param {Object} config The config object
21093  */
21094  
21095 Roo.bootstrap.SecurePass = function (config) {
21096     // these go here, so the translation tool can replace them..
21097     this.errors = {
21098         PwdEmpty: "Please type a password, and then retype it to confirm.",
21099         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21100         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21101         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21102         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21103         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21104         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21105         TooWeak: "Your password is Too Weak."
21106     },
21107     this.meterLabel = "Password strength:";
21108     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21109     this.meterClass = [
21110         "roo-password-meter-tooweak", 
21111         "roo-password-meter-weak", 
21112         "roo-password-meter-medium", 
21113         "roo-password-meter-strong", 
21114         "roo-password-meter-grey"
21115     ];
21116     
21117     this.errors = {};
21118     
21119     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21120 }
21121
21122 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21123     /**
21124      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21125      * {
21126      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21127      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21128      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21129      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21130      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21131      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21132      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21133      * })
21134      */
21135     // private
21136     
21137     meterWidth: 300,
21138     errorMsg :'',    
21139     errors: false,
21140     imageRoot: '/',
21141     /**
21142      * @cfg {String/Object} Label for the strength meter (defaults to
21143      * 'Password strength:')
21144      */
21145     // private
21146     meterLabel: '',
21147     /**
21148      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21149      * ['Weak', 'Medium', 'Strong'])
21150      */
21151     // private    
21152     pwdStrengths: false,    
21153     // private
21154     strength: 0,
21155     // private
21156     _lastPwd: null,
21157     // private
21158     kCapitalLetter: 0,
21159     kSmallLetter: 1,
21160     kDigit: 2,
21161     kPunctuation: 3,
21162     
21163     insecure: false,
21164     // private
21165     initEvents: function ()
21166     {
21167         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21168
21169         if (this.el.is('input[type=password]') && Roo.isSafari) {
21170             this.el.on('keydown', this.SafariOnKeyDown, this);
21171         }
21172
21173         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21174     },
21175     // private
21176     onRender: function (ct, position)
21177     {
21178         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21179         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21180         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21181
21182         this.trigger.createChild({
21183                    cn: [
21184                     {
21185                     //id: 'PwdMeter',
21186                     tag: 'div',
21187                     cls: 'roo-password-meter-grey col-xs-12',
21188                     style: {
21189                         //width: 0,
21190                         //width: this.meterWidth + 'px'                                                
21191                         }
21192                     },
21193                     {                            
21194                          cls: 'roo-password-meter-text'                          
21195                     }
21196                 ]            
21197         });
21198
21199          
21200         if (this.hideTrigger) {
21201             this.trigger.setDisplayed(false);
21202         }
21203         this.setSize(this.width || '', this.height || '');
21204     },
21205     // private
21206     onDestroy: function ()
21207     {
21208         if (this.trigger) {
21209             this.trigger.removeAllListeners();
21210             this.trigger.remove();
21211         }
21212         if (this.wrap) {
21213             this.wrap.remove();
21214         }
21215         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21216     },
21217     // private
21218     checkStrength: function ()
21219     {
21220         var pwd = this.inputEl().getValue();
21221         if (pwd == this._lastPwd) {
21222             return;
21223         }
21224
21225         var strength;
21226         if (this.ClientSideStrongPassword(pwd)) {
21227             strength = 3;
21228         } else if (this.ClientSideMediumPassword(pwd)) {
21229             strength = 2;
21230         } else if (this.ClientSideWeakPassword(pwd)) {
21231             strength = 1;
21232         } else {
21233             strength = 0;
21234         }
21235         
21236         Roo.log('strength1: ' + strength);
21237         
21238         //var pm = this.trigger.child('div/div/div').dom;
21239         var pm = this.trigger.child('div/div');
21240         pm.removeClass(this.meterClass);
21241         pm.addClass(this.meterClass[strength]);
21242                 
21243         
21244         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21245                 
21246         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21247         
21248         this._lastPwd = pwd;
21249     },
21250     reset: function ()
21251     {
21252         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21253         
21254         this._lastPwd = '';
21255         
21256         var pm = this.trigger.child('div/div');
21257         pm.removeClass(this.meterClass);
21258         pm.addClass('roo-password-meter-grey');        
21259         
21260         
21261         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21262         
21263         pt.innerHTML = '';
21264         this.inputEl().dom.type='password';
21265     },
21266     // private
21267     validateValue: function (value)
21268     {
21269         
21270         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21271             return false;
21272         }
21273         if (value.length == 0) {
21274             if (this.allowBlank) {
21275                 this.clearInvalid();
21276                 return true;
21277             }
21278
21279             this.markInvalid(this.errors.PwdEmpty);
21280             this.errorMsg = this.errors.PwdEmpty;
21281             return false;
21282         }
21283         
21284         if(this.insecure){
21285             return true;
21286         }
21287         
21288         if ('[\x21-\x7e]*'.match(value)) {
21289             this.markInvalid(this.errors.PwdBadChar);
21290             this.errorMsg = this.errors.PwdBadChar;
21291             return false;
21292         }
21293         if (value.length < 6) {
21294             this.markInvalid(this.errors.PwdShort);
21295             this.errorMsg = this.errors.PwdShort;
21296             return false;
21297         }
21298         if (value.length > 16) {
21299             this.markInvalid(this.errors.PwdLong);
21300             this.errorMsg = this.errors.PwdLong;
21301             return false;
21302         }
21303         var strength;
21304         if (this.ClientSideStrongPassword(value)) {
21305             strength = 3;
21306         } else if (this.ClientSideMediumPassword(value)) {
21307             strength = 2;
21308         } else if (this.ClientSideWeakPassword(value)) {
21309             strength = 1;
21310         } else {
21311             strength = 0;
21312         }
21313
21314         
21315         if (strength < 2) {
21316             //this.markInvalid(this.errors.TooWeak);
21317             this.errorMsg = this.errors.TooWeak;
21318             //return false;
21319         }
21320         
21321         
21322         console.log('strength2: ' + strength);
21323         
21324         //var pm = this.trigger.child('div/div/div').dom;
21325         
21326         var pm = this.trigger.child('div/div');
21327         pm.removeClass(this.meterClass);
21328         pm.addClass(this.meterClass[strength]);
21329                 
21330         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21331                 
21332         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21333         
21334         this.errorMsg = ''; 
21335         return true;
21336     },
21337     // private
21338     CharacterSetChecks: function (type)
21339     {
21340         this.type = type;
21341         this.fResult = false;
21342     },
21343     // private
21344     isctype: function (character, type)
21345     {
21346         switch (type) {  
21347             case this.kCapitalLetter:
21348                 if (character >= 'A' && character <= 'Z') {
21349                     return true;
21350                 }
21351                 break;
21352             
21353             case this.kSmallLetter:
21354                 if (character >= 'a' && character <= 'z') {
21355                     return true;
21356                 }
21357                 break;
21358             
21359             case this.kDigit:
21360                 if (character >= '0' && character <= '9') {
21361                     return true;
21362                 }
21363                 break;
21364             
21365             case this.kPunctuation:
21366                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21367                     return true;
21368                 }
21369                 break;
21370             
21371             default:
21372                 return false;
21373         }
21374
21375     },
21376     // private
21377     IsLongEnough: function (pwd, size)
21378     {
21379         return !(pwd == null || isNaN(size) || pwd.length < size);
21380     },
21381     // private
21382     SpansEnoughCharacterSets: function (word, nb)
21383     {
21384         if (!this.IsLongEnough(word, nb))
21385         {
21386             return false;
21387         }
21388
21389         var characterSetChecks = new Array(
21390             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21391             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21392         );
21393         
21394         for (var index = 0; index < word.length; ++index) {
21395             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21396                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21397                     characterSetChecks[nCharSet].fResult = true;
21398                     break;
21399                 }
21400             }
21401         }
21402
21403         var nCharSets = 0;
21404         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21405             if (characterSetChecks[nCharSet].fResult) {
21406                 ++nCharSets;
21407             }
21408         }
21409
21410         if (nCharSets < nb) {
21411             return false;
21412         }
21413         return true;
21414     },
21415     // private
21416     ClientSideStrongPassword: function (pwd)
21417     {
21418         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21419     },
21420     // private
21421     ClientSideMediumPassword: function (pwd)
21422     {
21423         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21424     },
21425     // private
21426     ClientSideWeakPassword: function (pwd)
21427     {
21428         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21429     }
21430           
21431 })//<script type="text/javascript">
21432
21433 /*
21434  * Based  Ext JS Library 1.1.1
21435  * Copyright(c) 2006-2007, Ext JS, LLC.
21436  * LGPL
21437  *
21438  */
21439  
21440 /**
21441  * @class Roo.HtmlEditorCore
21442  * @extends Roo.Component
21443  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21444  *
21445  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21446  */
21447
21448 Roo.HtmlEditorCore = function(config){
21449     
21450     
21451     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21452     
21453     
21454     this.addEvents({
21455         /**
21456          * @event initialize
21457          * Fires when the editor is fully initialized (including the iframe)
21458          * @param {Roo.HtmlEditorCore} this
21459          */
21460         initialize: true,
21461         /**
21462          * @event activate
21463          * Fires when the editor is first receives the focus. Any insertion must wait
21464          * until after this event.
21465          * @param {Roo.HtmlEditorCore} this
21466          */
21467         activate: true,
21468          /**
21469          * @event beforesync
21470          * Fires before the textarea is updated with content from the editor iframe. Return false
21471          * to cancel the sync.
21472          * @param {Roo.HtmlEditorCore} this
21473          * @param {String} html
21474          */
21475         beforesync: true,
21476          /**
21477          * @event beforepush
21478          * Fires before the iframe editor is updated with content from the textarea. Return false
21479          * to cancel the push.
21480          * @param {Roo.HtmlEditorCore} this
21481          * @param {String} html
21482          */
21483         beforepush: true,
21484          /**
21485          * @event sync
21486          * Fires when the textarea is updated with content from the editor iframe.
21487          * @param {Roo.HtmlEditorCore} this
21488          * @param {String} html
21489          */
21490         sync: true,
21491          /**
21492          * @event push
21493          * Fires when the iframe editor is updated with content from the textarea.
21494          * @param {Roo.HtmlEditorCore} this
21495          * @param {String} html
21496          */
21497         push: true,
21498         
21499         /**
21500          * @event editorevent
21501          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21502          * @param {Roo.HtmlEditorCore} this
21503          */
21504         editorevent: true
21505         
21506     });
21507     
21508     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21509     
21510     // defaults : white / black...
21511     this.applyBlacklists();
21512     
21513     
21514     
21515 };
21516
21517
21518 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21519
21520
21521      /**
21522      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21523      */
21524     
21525     owner : false,
21526     
21527      /**
21528      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21529      *                        Roo.resizable.
21530      */
21531     resizable : false,
21532      /**
21533      * @cfg {Number} height (in pixels)
21534      */   
21535     height: 300,
21536    /**
21537      * @cfg {Number} width (in pixels)
21538      */   
21539     width: 500,
21540     
21541     /**
21542      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21543      * 
21544      */
21545     stylesheets: false,
21546     
21547     // id of frame..
21548     frameId: false,
21549     
21550     // private properties
21551     validationEvent : false,
21552     deferHeight: true,
21553     initialized : false,
21554     activated : false,
21555     sourceEditMode : false,
21556     onFocus : Roo.emptyFn,
21557     iframePad:3,
21558     hideMode:'offsets',
21559     
21560     clearUp: true,
21561     
21562     // blacklist + whitelisted elements..
21563     black: false,
21564     white: false,
21565      
21566     bodyCls : '',
21567
21568     /**
21569      * Protected method that will not generally be called directly. It
21570      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21571      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21572      */
21573     getDocMarkup : function(){
21574         // body styles..
21575         var st = '';
21576         
21577         // inherit styels from page...?? 
21578         if (this.stylesheets === false) {
21579             
21580             Roo.get(document.head).select('style').each(function(node) {
21581                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21582             });
21583             
21584             Roo.get(document.head).select('link').each(function(node) { 
21585                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21586             });
21587             
21588         } else if (!this.stylesheets.length) {
21589                 // simple..
21590                 st = '<style type="text/css">' +
21591                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21592                    '</style>';
21593         } else { 
21594             st = '<style type="text/css">' +
21595                     this.stylesheets +
21596                 '</style>';
21597         }
21598         
21599         st +=  '<style type="text/css">' +
21600             'IMG { cursor: pointer } ' +
21601         '</style>';
21602
21603         var cls = 'roo-htmleditor-body';
21604         
21605         if(this.bodyCls.length){
21606             cls += ' ' + this.bodyCls;
21607         }
21608         
21609         return '<html><head>' + st  +
21610             //<style type="text/css">' +
21611             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21612             //'</style>' +
21613             ' </head><body class="' +  cls + '"></body></html>';
21614     },
21615
21616     // private
21617     onRender : function(ct, position)
21618     {
21619         var _t = this;
21620         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21621         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21622         
21623         
21624         this.el.dom.style.border = '0 none';
21625         this.el.dom.setAttribute('tabIndex', -1);
21626         this.el.addClass('x-hidden hide');
21627         
21628         
21629         
21630         if(Roo.isIE){ // fix IE 1px bogus margin
21631             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21632         }
21633        
21634         
21635         this.frameId = Roo.id();
21636         
21637          
21638         
21639         var iframe = this.owner.wrap.createChild({
21640             tag: 'iframe',
21641             cls: 'form-control', // bootstrap..
21642             id: this.frameId,
21643             name: this.frameId,
21644             frameBorder : 'no',
21645             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21646         }, this.el
21647         );
21648         
21649         
21650         this.iframe = iframe.dom;
21651
21652          this.assignDocWin();
21653         
21654         this.doc.designMode = 'on';
21655        
21656         this.doc.open();
21657         this.doc.write(this.getDocMarkup());
21658         this.doc.close();
21659
21660         
21661         var task = { // must defer to wait for browser to be ready
21662             run : function(){
21663                 //console.log("run task?" + this.doc.readyState);
21664                 this.assignDocWin();
21665                 if(this.doc.body || this.doc.readyState == 'complete'){
21666                     try {
21667                         this.doc.designMode="on";
21668                     } catch (e) {
21669                         return;
21670                     }
21671                     Roo.TaskMgr.stop(task);
21672                     this.initEditor.defer(10, this);
21673                 }
21674             },
21675             interval : 10,
21676             duration: 10000,
21677             scope: this
21678         };
21679         Roo.TaskMgr.start(task);
21680
21681     },
21682
21683     // private
21684     onResize : function(w, h)
21685     {
21686          Roo.log('resize: ' +w + ',' + h );
21687         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21688         if(!this.iframe){
21689             return;
21690         }
21691         if(typeof w == 'number'){
21692             
21693             this.iframe.style.width = w + 'px';
21694         }
21695         if(typeof h == 'number'){
21696             
21697             this.iframe.style.height = h + 'px';
21698             if(this.doc){
21699                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21700             }
21701         }
21702         
21703     },
21704
21705     /**
21706      * Toggles the editor between standard and source edit mode.
21707      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21708      */
21709     toggleSourceEdit : function(sourceEditMode){
21710         
21711         this.sourceEditMode = sourceEditMode === true;
21712         
21713         if(this.sourceEditMode){
21714  
21715             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21716             
21717         }else{
21718             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21719             //this.iframe.className = '';
21720             this.deferFocus();
21721         }
21722         //this.setSize(this.owner.wrap.getSize());
21723         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21724     },
21725
21726     
21727   
21728
21729     /**
21730      * Protected method that will not generally be called directly. If you need/want
21731      * custom HTML cleanup, this is the method you should override.
21732      * @param {String} html The HTML to be cleaned
21733      * return {String} The cleaned HTML
21734      */
21735     cleanHtml : function(html){
21736         html = String(html);
21737         if(html.length > 5){
21738             if(Roo.isSafari){ // strip safari nonsense
21739                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21740             }
21741         }
21742         if(html == '&nbsp;'){
21743             html = '';
21744         }
21745         return html;
21746     },
21747
21748     /**
21749      * HTML Editor -> Textarea
21750      * Protected method that will not generally be called directly. Syncs the contents
21751      * of the editor iframe with the textarea.
21752      */
21753     syncValue : function(){
21754         if(this.initialized){
21755             var bd = (this.doc.body || this.doc.documentElement);
21756             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21757             var html = bd.innerHTML;
21758             if(Roo.isSafari){
21759                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21760                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21761                 if(m && m[1]){
21762                     html = '<div style="'+m[0]+'">' + html + '</div>';
21763                 }
21764             }
21765             html = this.cleanHtml(html);
21766             // fix up the special chars.. normaly like back quotes in word...
21767             // however we do not want to do this with chinese..
21768             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21769                 var cc = b.charCodeAt();
21770                 if (
21771                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21772                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21773                     (cc >= 0xf900 && cc < 0xfb00 )
21774                 ) {
21775                         return b;
21776                 }
21777                 return "&#"+cc+";" 
21778             });
21779             if(this.owner.fireEvent('beforesync', this, html) !== false){
21780                 this.el.dom.value = html;
21781                 this.owner.fireEvent('sync', this, html);
21782             }
21783         }
21784     },
21785
21786     /**
21787      * Protected method that will not generally be called directly. Pushes the value of the textarea
21788      * into the iframe editor.
21789      */
21790     pushValue : function(){
21791         if(this.initialized){
21792             var v = this.el.dom.value.trim();
21793             
21794 //            if(v.length < 1){
21795 //                v = '&#160;';
21796 //            }
21797             
21798             if(this.owner.fireEvent('beforepush', this, v) !== false){
21799                 var d = (this.doc.body || this.doc.documentElement);
21800                 d.innerHTML = v;
21801                 this.cleanUpPaste();
21802                 this.el.dom.value = d.innerHTML;
21803                 this.owner.fireEvent('push', this, v);
21804             }
21805         }
21806     },
21807
21808     // private
21809     deferFocus : function(){
21810         this.focus.defer(10, this);
21811     },
21812
21813     // doc'ed in Field
21814     focus : function(){
21815         if(this.win && !this.sourceEditMode){
21816             this.win.focus();
21817         }else{
21818             this.el.focus();
21819         }
21820     },
21821     
21822     assignDocWin: function()
21823     {
21824         var iframe = this.iframe;
21825         
21826          if(Roo.isIE){
21827             this.doc = iframe.contentWindow.document;
21828             this.win = iframe.contentWindow;
21829         } else {
21830 //            if (!Roo.get(this.frameId)) {
21831 //                return;
21832 //            }
21833 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21834 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21835             
21836             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21837                 return;
21838             }
21839             
21840             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21841             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21842         }
21843     },
21844     
21845     // private
21846     initEditor : function(){
21847         //console.log("INIT EDITOR");
21848         this.assignDocWin();
21849         
21850         
21851         
21852         this.doc.designMode="on";
21853         this.doc.open();
21854         this.doc.write(this.getDocMarkup());
21855         this.doc.close();
21856         
21857         var dbody = (this.doc.body || this.doc.documentElement);
21858         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21859         // this copies styles from the containing element into thsi one..
21860         // not sure why we need all of this..
21861         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21862         
21863         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21864         //ss['background-attachment'] = 'fixed'; // w3c
21865         dbody.bgProperties = 'fixed'; // ie
21866         //Roo.DomHelper.applyStyles(dbody, ss);
21867         Roo.EventManager.on(this.doc, {
21868             //'mousedown': this.onEditorEvent,
21869             'mouseup': this.onEditorEvent,
21870             'dblclick': this.onEditorEvent,
21871             'click': this.onEditorEvent,
21872             'keyup': this.onEditorEvent,
21873             buffer:100,
21874             scope: this
21875         });
21876         if(Roo.isGecko){
21877             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21878         }
21879         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21880             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21881         }
21882         this.initialized = true;
21883
21884         this.owner.fireEvent('initialize', this);
21885         this.pushValue();
21886     },
21887
21888     // private
21889     onDestroy : function(){
21890         
21891         
21892         
21893         if(this.rendered){
21894             
21895             //for (var i =0; i < this.toolbars.length;i++) {
21896             //    // fixme - ask toolbars for heights?
21897             //    this.toolbars[i].onDestroy();
21898            // }
21899             
21900             //this.wrap.dom.innerHTML = '';
21901             //this.wrap.remove();
21902         }
21903     },
21904
21905     // private
21906     onFirstFocus : function(){
21907         
21908         this.assignDocWin();
21909         
21910         
21911         this.activated = true;
21912          
21913     
21914         if(Roo.isGecko){ // prevent silly gecko errors
21915             this.win.focus();
21916             var s = this.win.getSelection();
21917             if(!s.focusNode || s.focusNode.nodeType != 3){
21918                 var r = s.getRangeAt(0);
21919                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21920                 r.collapse(true);
21921                 this.deferFocus();
21922             }
21923             try{
21924                 this.execCmd('useCSS', true);
21925                 this.execCmd('styleWithCSS', false);
21926             }catch(e){}
21927         }
21928         this.owner.fireEvent('activate', this);
21929     },
21930
21931     // private
21932     adjustFont: function(btn){
21933         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21934         //if(Roo.isSafari){ // safari
21935         //    adjust *= 2;
21936        // }
21937         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21938         if(Roo.isSafari){ // safari
21939             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21940             v =  (v < 10) ? 10 : v;
21941             v =  (v > 48) ? 48 : v;
21942             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21943             
21944         }
21945         
21946         
21947         v = Math.max(1, v+adjust);
21948         
21949         this.execCmd('FontSize', v  );
21950     },
21951
21952     onEditorEvent : function(e)
21953     {
21954         this.owner.fireEvent('editorevent', this, e);
21955       //  this.updateToolbar();
21956         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21957     },
21958
21959     insertTag : function(tg)
21960     {
21961         // could be a bit smarter... -> wrap the current selected tRoo..
21962         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21963             
21964             range = this.createRange(this.getSelection());
21965             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21966             wrappingNode.appendChild(range.extractContents());
21967             range.insertNode(wrappingNode);
21968
21969             return;
21970             
21971             
21972             
21973         }
21974         this.execCmd("formatblock",   tg);
21975         
21976     },
21977     
21978     insertText : function(txt)
21979     {
21980         
21981         
21982         var range = this.createRange();
21983         range.deleteContents();
21984                //alert(Sender.getAttribute('label'));
21985                
21986         range.insertNode(this.doc.createTextNode(txt));
21987     } ,
21988     
21989      
21990
21991     /**
21992      * Executes a Midas editor command on the editor document and performs necessary focus and
21993      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21994      * @param {String} cmd The Midas command
21995      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21996      */
21997     relayCmd : function(cmd, value){
21998         this.win.focus();
21999         this.execCmd(cmd, value);
22000         this.owner.fireEvent('editorevent', this);
22001         //this.updateToolbar();
22002         this.owner.deferFocus();
22003     },
22004
22005     /**
22006      * Executes a Midas editor command directly on the editor document.
22007      * For visual commands, you should use {@link #relayCmd} instead.
22008      * <b>This should only be called after the editor is initialized.</b>
22009      * @param {String} cmd The Midas command
22010      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22011      */
22012     execCmd : function(cmd, value){
22013         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22014         this.syncValue();
22015     },
22016  
22017  
22018    
22019     /**
22020      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22021      * to insert tRoo.
22022      * @param {String} text | dom node.. 
22023      */
22024     insertAtCursor : function(text)
22025     {
22026         
22027         if(!this.activated){
22028             return;
22029         }
22030         /*
22031         if(Roo.isIE){
22032             this.win.focus();
22033             var r = this.doc.selection.createRange();
22034             if(r){
22035                 r.collapse(true);
22036                 r.pasteHTML(text);
22037                 this.syncValue();
22038                 this.deferFocus();
22039             
22040             }
22041             return;
22042         }
22043         */
22044         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22045             this.win.focus();
22046             
22047             
22048             // from jquery ui (MIT licenced)
22049             var range, node;
22050             var win = this.win;
22051             
22052             if (win.getSelection && win.getSelection().getRangeAt) {
22053                 range = win.getSelection().getRangeAt(0);
22054                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22055                 range.insertNode(node);
22056             } else if (win.document.selection && win.document.selection.createRange) {
22057                 // no firefox support
22058                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22059                 win.document.selection.createRange().pasteHTML(txt);
22060             } else {
22061                 // no firefox support
22062                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22063                 this.execCmd('InsertHTML', txt);
22064             } 
22065             
22066             this.syncValue();
22067             
22068             this.deferFocus();
22069         }
22070     },
22071  // private
22072     mozKeyPress : function(e){
22073         if(e.ctrlKey){
22074             var c = e.getCharCode(), cmd;
22075           
22076             if(c > 0){
22077                 c = String.fromCharCode(c).toLowerCase();
22078                 switch(c){
22079                     case 'b':
22080                         cmd = 'bold';
22081                         break;
22082                     case 'i':
22083                         cmd = 'italic';
22084                         break;
22085                     
22086                     case 'u':
22087                         cmd = 'underline';
22088                         break;
22089                     
22090                     case 'v':
22091                         this.cleanUpPaste.defer(100, this);
22092                         return;
22093                         
22094                 }
22095                 if(cmd){
22096                     this.win.focus();
22097                     this.execCmd(cmd);
22098                     this.deferFocus();
22099                     e.preventDefault();
22100                 }
22101                 
22102             }
22103         }
22104     },
22105
22106     // private
22107     fixKeys : function(){ // load time branching for fastest keydown performance
22108         if(Roo.isIE){
22109             return function(e){
22110                 var k = e.getKey(), r;
22111                 if(k == e.TAB){
22112                     e.stopEvent();
22113                     r = this.doc.selection.createRange();
22114                     if(r){
22115                         r.collapse(true);
22116                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22117                         this.deferFocus();
22118                     }
22119                     return;
22120                 }
22121                 
22122                 if(k == e.ENTER){
22123                     r = this.doc.selection.createRange();
22124                     if(r){
22125                         var target = r.parentElement();
22126                         if(!target || target.tagName.toLowerCase() != 'li'){
22127                             e.stopEvent();
22128                             r.pasteHTML('<br />');
22129                             r.collapse(false);
22130                             r.select();
22131                         }
22132                     }
22133                 }
22134                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22135                     this.cleanUpPaste.defer(100, this);
22136                     return;
22137                 }
22138                 
22139                 
22140             };
22141         }else if(Roo.isOpera){
22142             return function(e){
22143                 var k = e.getKey();
22144                 if(k == e.TAB){
22145                     e.stopEvent();
22146                     this.win.focus();
22147                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22148                     this.deferFocus();
22149                 }
22150                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22151                     this.cleanUpPaste.defer(100, this);
22152                     return;
22153                 }
22154                 
22155             };
22156         }else if(Roo.isSafari){
22157             return function(e){
22158                 var k = e.getKey();
22159                 
22160                 if(k == e.TAB){
22161                     e.stopEvent();
22162                     this.execCmd('InsertText','\t');
22163                     this.deferFocus();
22164                     return;
22165                 }
22166                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22167                     this.cleanUpPaste.defer(100, this);
22168                     return;
22169                 }
22170                 
22171              };
22172         }
22173     }(),
22174     
22175     getAllAncestors: function()
22176     {
22177         var p = this.getSelectedNode();
22178         var a = [];
22179         if (!p) {
22180             a.push(p); // push blank onto stack..
22181             p = this.getParentElement();
22182         }
22183         
22184         
22185         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22186             a.push(p);
22187             p = p.parentNode;
22188         }
22189         a.push(this.doc.body);
22190         return a;
22191     },
22192     lastSel : false,
22193     lastSelNode : false,
22194     
22195     
22196     getSelection : function() 
22197     {
22198         this.assignDocWin();
22199         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22200     },
22201     
22202     getSelectedNode: function() 
22203     {
22204         // this may only work on Gecko!!!
22205         
22206         // should we cache this!!!!
22207         
22208         
22209         
22210          
22211         var range = this.createRange(this.getSelection()).cloneRange();
22212         
22213         if (Roo.isIE) {
22214             var parent = range.parentElement();
22215             while (true) {
22216                 var testRange = range.duplicate();
22217                 testRange.moveToElementText(parent);
22218                 if (testRange.inRange(range)) {
22219                     break;
22220                 }
22221                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22222                     break;
22223                 }
22224                 parent = parent.parentElement;
22225             }
22226             return parent;
22227         }
22228         
22229         // is ancestor a text element.
22230         var ac =  range.commonAncestorContainer;
22231         if (ac.nodeType == 3) {
22232             ac = ac.parentNode;
22233         }
22234         
22235         var ar = ac.childNodes;
22236          
22237         var nodes = [];
22238         var other_nodes = [];
22239         var has_other_nodes = false;
22240         for (var i=0;i<ar.length;i++) {
22241             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22242                 continue;
22243             }
22244             // fullly contained node.
22245             
22246             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22247                 nodes.push(ar[i]);
22248                 continue;
22249             }
22250             
22251             // probably selected..
22252             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22253                 other_nodes.push(ar[i]);
22254                 continue;
22255             }
22256             // outer..
22257             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22258                 continue;
22259             }
22260             
22261             
22262             has_other_nodes = true;
22263         }
22264         if (!nodes.length && other_nodes.length) {
22265             nodes= other_nodes;
22266         }
22267         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22268             return false;
22269         }
22270         
22271         return nodes[0];
22272     },
22273     createRange: function(sel)
22274     {
22275         // this has strange effects when using with 
22276         // top toolbar - not sure if it's a great idea.
22277         //this.editor.contentWindow.focus();
22278         if (typeof sel != "undefined") {
22279             try {
22280                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22281             } catch(e) {
22282                 return this.doc.createRange();
22283             }
22284         } else {
22285             return this.doc.createRange();
22286         }
22287     },
22288     getParentElement: function()
22289     {
22290         
22291         this.assignDocWin();
22292         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22293         
22294         var range = this.createRange(sel);
22295          
22296         try {
22297             var p = range.commonAncestorContainer;
22298             while (p.nodeType == 3) { // text node
22299                 p = p.parentNode;
22300             }
22301             return p;
22302         } catch (e) {
22303             return null;
22304         }
22305     
22306     },
22307     /***
22308      *
22309      * Range intersection.. the hard stuff...
22310      *  '-1' = before
22311      *  '0' = hits..
22312      *  '1' = after.
22313      *         [ -- selected range --- ]
22314      *   [fail]                        [fail]
22315      *
22316      *    basically..
22317      *      if end is before start or  hits it. fail.
22318      *      if start is after end or hits it fail.
22319      *
22320      *   if either hits (but other is outside. - then it's not 
22321      *   
22322      *    
22323      **/
22324     
22325     
22326     // @see http://www.thismuchiknow.co.uk/?p=64.
22327     rangeIntersectsNode : function(range, node)
22328     {
22329         var nodeRange = node.ownerDocument.createRange();
22330         try {
22331             nodeRange.selectNode(node);
22332         } catch (e) {
22333             nodeRange.selectNodeContents(node);
22334         }
22335     
22336         var rangeStartRange = range.cloneRange();
22337         rangeStartRange.collapse(true);
22338     
22339         var rangeEndRange = range.cloneRange();
22340         rangeEndRange.collapse(false);
22341     
22342         var nodeStartRange = nodeRange.cloneRange();
22343         nodeStartRange.collapse(true);
22344     
22345         var nodeEndRange = nodeRange.cloneRange();
22346         nodeEndRange.collapse(false);
22347     
22348         return rangeStartRange.compareBoundaryPoints(
22349                  Range.START_TO_START, nodeEndRange) == -1 &&
22350                rangeEndRange.compareBoundaryPoints(
22351                  Range.START_TO_START, nodeStartRange) == 1;
22352         
22353          
22354     },
22355     rangeCompareNode : function(range, node)
22356     {
22357         var nodeRange = node.ownerDocument.createRange();
22358         try {
22359             nodeRange.selectNode(node);
22360         } catch (e) {
22361             nodeRange.selectNodeContents(node);
22362         }
22363         
22364         
22365         range.collapse(true);
22366     
22367         nodeRange.collapse(true);
22368      
22369         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22370         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22371          
22372         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22373         
22374         var nodeIsBefore   =  ss == 1;
22375         var nodeIsAfter    = ee == -1;
22376         
22377         if (nodeIsBefore && nodeIsAfter) {
22378             return 0; // outer
22379         }
22380         if (!nodeIsBefore && nodeIsAfter) {
22381             return 1; //right trailed.
22382         }
22383         
22384         if (nodeIsBefore && !nodeIsAfter) {
22385             return 2;  // left trailed.
22386         }
22387         // fully contined.
22388         return 3;
22389     },
22390
22391     // private? - in a new class?
22392     cleanUpPaste :  function()
22393     {
22394         // cleans up the whole document..
22395         Roo.log('cleanuppaste');
22396         
22397         this.cleanUpChildren(this.doc.body);
22398         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22399         if (clean != this.doc.body.innerHTML) {
22400             this.doc.body.innerHTML = clean;
22401         }
22402         
22403     },
22404     
22405     cleanWordChars : function(input) {// change the chars to hex code
22406         var he = Roo.HtmlEditorCore;
22407         
22408         var output = input;
22409         Roo.each(he.swapCodes, function(sw) { 
22410             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22411             
22412             output = output.replace(swapper, sw[1]);
22413         });
22414         
22415         return output;
22416     },
22417     
22418     
22419     cleanUpChildren : function (n)
22420     {
22421         if (!n.childNodes.length) {
22422             return;
22423         }
22424         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22425            this.cleanUpChild(n.childNodes[i]);
22426         }
22427     },
22428     
22429     
22430         
22431     
22432     cleanUpChild : function (node)
22433     {
22434         var ed = this;
22435         //console.log(node);
22436         if (node.nodeName == "#text") {
22437             // clean up silly Windows -- stuff?
22438             return; 
22439         }
22440         if (node.nodeName == "#comment") {
22441             node.parentNode.removeChild(node);
22442             // clean up silly Windows -- stuff?
22443             return; 
22444         }
22445         var lcname = node.tagName.toLowerCase();
22446         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22447         // whitelist of tags..
22448         
22449         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22450             // remove node.
22451             node.parentNode.removeChild(node);
22452             return;
22453             
22454         }
22455         
22456         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22457         
22458         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22459         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22460         
22461         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22462         //    remove_keep_children = true;
22463         //}
22464         
22465         if (remove_keep_children) {
22466             this.cleanUpChildren(node);
22467             // inserts everything just before this node...
22468             while (node.childNodes.length) {
22469                 var cn = node.childNodes[0];
22470                 node.removeChild(cn);
22471                 node.parentNode.insertBefore(cn, node);
22472             }
22473             node.parentNode.removeChild(node);
22474             return;
22475         }
22476         
22477         if (!node.attributes || !node.attributes.length) {
22478             this.cleanUpChildren(node);
22479             return;
22480         }
22481         
22482         function cleanAttr(n,v)
22483         {
22484             
22485             if (v.match(/^\./) || v.match(/^\//)) {
22486                 return;
22487             }
22488             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22489                 return;
22490             }
22491             if (v.match(/^#/)) {
22492                 return;
22493             }
22494 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22495             node.removeAttribute(n);
22496             
22497         }
22498         
22499         var cwhite = this.cwhite;
22500         var cblack = this.cblack;
22501             
22502         function cleanStyle(n,v)
22503         {
22504             if (v.match(/expression/)) { //XSS?? should we even bother..
22505                 node.removeAttribute(n);
22506                 return;
22507             }
22508             
22509             var parts = v.split(/;/);
22510             var clean = [];
22511             
22512             Roo.each(parts, function(p) {
22513                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22514                 if (!p.length) {
22515                     return true;
22516                 }
22517                 var l = p.split(':').shift().replace(/\s+/g,'');
22518                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22519                 
22520                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22521 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22522                     //node.removeAttribute(n);
22523                     return true;
22524                 }
22525                 //Roo.log()
22526                 // only allow 'c whitelisted system attributes'
22527                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22528 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22529                     //node.removeAttribute(n);
22530                     return true;
22531                 }
22532                 
22533                 
22534                  
22535                 
22536                 clean.push(p);
22537                 return true;
22538             });
22539             if (clean.length) { 
22540                 node.setAttribute(n, clean.join(';'));
22541             } else {
22542                 node.removeAttribute(n);
22543             }
22544             
22545         }
22546         
22547         
22548         for (var i = node.attributes.length-1; i > -1 ; i--) {
22549             var a = node.attributes[i];
22550             //console.log(a);
22551             
22552             if (a.name.toLowerCase().substr(0,2)=='on')  {
22553                 node.removeAttribute(a.name);
22554                 continue;
22555             }
22556             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22557                 node.removeAttribute(a.name);
22558                 continue;
22559             }
22560             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22561                 cleanAttr(a.name,a.value); // fixme..
22562                 continue;
22563             }
22564             if (a.name == 'style') {
22565                 cleanStyle(a.name,a.value);
22566                 continue;
22567             }
22568             /// clean up MS crap..
22569             // tecnically this should be a list of valid class'es..
22570             
22571             
22572             if (a.name == 'class') {
22573                 if (a.value.match(/^Mso/)) {
22574                     node.className = '';
22575                 }
22576                 
22577                 if (a.value.match(/^body$/)) {
22578                     node.className = '';
22579                 }
22580                 continue;
22581             }
22582             
22583             // style cleanup!?
22584             // class cleanup?
22585             
22586         }
22587         
22588         
22589         this.cleanUpChildren(node);
22590         
22591         
22592     },
22593     
22594     /**
22595      * Clean up MS wordisms...
22596      */
22597     cleanWord : function(node)
22598     {
22599         
22600         
22601         if (!node) {
22602             this.cleanWord(this.doc.body);
22603             return;
22604         }
22605         if (node.nodeName == "#text") {
22606             // clean up silly Windows -- stuff?
22607             return; 
22608         }
22609         if (node.nodeName == "#comment") {
22610             node.parentNode.removeChild(node);
22611             // clean up silly Windows -- stuff?
22612             return; 
22613         }
22614         
22615         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22616             node.parentNode.removeChild(node);
22617             return;
22618         }
22619         
22620         // remove - but keep children..
22621         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22622             while (node.childNodes.length) {
22623                 var cn = node.childNodes[0];
22624                 node.removeChild(cn);
22625                 node.parentNode.insertBefore(cn, node);
22626             }
22627             node.parentNode.removeChild(node);
22628             this.iterateChildren(node, this.cleanWord);
22629             return;
22630         }
22631         // clean styles
22632         if (node.className.length) {
22633             
22634             var cn = node.className.split(/\W+/);
22635             var cna = [];
22636             Roo.each(cn, function(cls) {
22637                 if (cls.match(/Mso[a-zA-Z]+/)) {
22638                     return;
22639                 }
22640                 cna.push(cls);
22641             });
22642             node.className = cna.length ? cna.join(' ') : '';
22643             if (!cna.length) {
22644                 node.removeAttribute("class");
22645             }
22646         }
22647         
22648         if (node.hasAttribute("lang")) {
22649             node.removeAttribute("lang");
22650         }
22651         
22652         if (node.hasAttribute("style")) {
22653             
22654             var styles = node.getAttribute("style").split(";");
22655             var nstyle = [];
22656             Roo.each(styles, function(s) {
22657                 if (!s.match(/:/)) {
22658                     return;
22659                 }
22660                 var kv = s.split(":");
22661                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22662                     return;
22663                 }
22664                 // what ever is left... we allow.
22665                 nstyle.push(s);
22666             });
22667             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22668             if (!nstyle.length) {
22669                 node.removeAttribute('style');
22670             }
22671         }
22672         this.iterateChildren(node, this.cleanWord);
22673         
22674         
22675         
22676     },
22677     /**
22678      * iterateChildren of a Node, calling fn each time, using this as the scole..
22679      * @param {DomNode} node node to iterate children of.
22680      * @param {Function} fn method of this class to call on each item.
22681      */
22682     iterateChildren : function(node, fn)
22683     {
22684         if (!node.childNodes.length) {
22685                 return;
22686         }
22687         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22688            fn.call(this, node.childNodes[i])
22689         }
22690     },
22691     
22692     
22693     /**
22694      * cleanTableWidths.
22695      *
22696      * Quite often pasting from word etc.. results in tables with column and widths.
22697      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22698      *
22699      */
22700     cleanTableWidths : function(node)
22701     {
22702          
22703          
22704         if (!node) {
22705             this.cleanTableWidths(this.doc.body);
22706             return;
22707         }
22708         
22709         // ignore list...
22710         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22711             return; 
22712         }
22713         Roo.log(node.tagName);
22714         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22715             this.iterateChildren(node, this.cleanTableWidths);
22716             return;
22717         }
22718         if (node.hasAttribute('width')) {
22719             node.removeAttribute('width');
22720         }
22721         
22722          
22723         if (node.hasAttribute("style")) {
22724             // pretty basic...
22725             
22726             var styles = node.getAttribute("style").split(";");
22727             var nstyle = [];
22728             Roo.each(styles, function(s) {
22729                 if (!s.match(/:/)) {
22730                     return;
22731                 }
22732                 var kv = s.split(":");
22733                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22734                     return;
22735                 }
22736                 // what ever is left... we allow.
22737                 nstyle.push(s);
22738             });
22739             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22740             if (!nstyle.length) {
22741                 node.removeAttribute('style');
22742             }
22743         }
22744         
22745         this.iterateChildren(node, this.cleanTableWidths);
22746         
22747         
22748     },
22749     
22750     
22751     
22752     
22753     domToHTML : function(currentElement, depth, nopadtext) {
22754         
22755         depth = depth || 0;
22756         nopadtext = nopadtext || false;
22757     
22758         if (!currentElement) {
22759             return this.domToHTML(this.doc.body);
22760         }
22761         
22762         //Roo.log(currentElement);
22763         var j;
22764         var allText = false;
22765         var nodeName = currentElement.nodeName;
22766         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22767         
22768         if  (nodeName == '#text') {
22769             
22770             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22771         }
22772         
22773         
22774         var ret = '';
22775         if (nodeName != 'BODY') {
22776              
22777             var i = 0;
22778             // Prints the node tagName, such as <A>, <IMG>, etc
22779             if (tagName) {
22780                 var attr = [];
22781                 for(i = 0; i < currentElement.attributes.length;i++) {
22782                     // quoting?
22783                     var aname = currentElement.attributes.item(i).name;
22784                     if (!currentElement.attributes.item(i).value.length) {
22785                         continue;
22786                     }
22787                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22788                 }
22789                 
22790                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22791             } 
22792             else {
22793                 
22794                 // eack
22795             }
22796         } else {
22797             tagName = false;
22798         }
22799         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22800             return ret;
22801         }
22802         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22803             nopadtext = true;
22804         }
22805         
22806         
22807         // Traverse the tree
22808         i = 0;
22809         var currentElementChild = currentElement.childNodes.item(i);
22810         var allText = true;
22811         var innerHTML  = '';
22812         lastnode = '';
22813         while (currentElementChild) {
22814             // Formatting code (indent the tree so it looks nice on the screen)
22815             var nopad = nopadtext;
22816             if (lastnode == 'SPAN') {
22817                 nopad  = true;
22818             }
22819             // text
22820             if  (currentElementChild.nodeName == '#text') {
22821                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22822                 toadd = nopadtext ? toadd : toadd.trim();
22823                 if (!nopad && toadd.length > 80) {
22824                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22825                 }
22826                 innerHTML  += toadd;
22827                 
22828                 i++;
22829                 currentElementChild = currentElement.childNodes.item(i);
22830                 lastNode = '';
22831                 continue;
22832             }
22833             allText = false;
22834             
22835             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22836                 
22837             // Recursively traverse the tree structure of the child node
22838             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22839             lastnode = currentElementChild.nodeName;
22840             i++;
22841             currentElementChild=currentElement.childNodes.item(i);
22842         }
22843         
22844         ret += innerHTML;
22845         
22846         if (!allText) {
22847                 // The remaining code is mostly for formatting the tree
22848             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22849         }
22850         
22851         
22852         if (tagName) {
22853             ret+= "</"+tagName+">";
22854         }
22855         return ret;
22856         
22857     },
22858         
22859     applyBlacklists : function()
22860     {
22861         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22862         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22863         
22864         this.white = [];
22865         this.black = [];
22866         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22867             if (b.indexOf(tag) > -1) {
22868                 return;
22869             }
22870             this.white.push(tag);
22871             
22872         }, this);
22873         
22874         Roo.each(w, function(tag) {
22875             if (b.indexOf(tag) > -1) {
22876                 return;
22877             }
22878             if (this.white.indexOf(tag) > -1) {
22879                 return;
22880             }
22881             this.white.push(tag);
22882             
22883         }, this);
22884         
22885         
22886         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22887             if (w.indexOf(tag) > -1) {
22888                 return;
22889             }
22890             this.black.push(tag);
22891             
22892         }, this);
22893         
22894         Roo.each(b, function(tag) {
22895             if (w.indexOf(tag) > -1) {
22896                 return;
22897             }
22898             if (this.black.indexOf(tag) > -1) {
22899                 return;
22900             }
22901             this.black.push(tag);
22902             
22903         }, this);
22904         
22905         
22906         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22907         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22908         
22909         this.cwhite = [];
22910         this.cblack = [];
22911         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22912             if (b.indexOf(tag) > -1) {
22913                 return;
22914             }
22915             this.cwhite.push(tag);
22916             
22917         }, this);
22918         
22919         Roo.each(w, function(tag) {
22920             if (b.indexOf(tag) > -1) {
22921                 return;
22922             }
22923             if (this.cwhite.indexOf(tag) > -1) {
22924                 return;
22925             }
22926             this.cwhite.push(tag);
22927             
22928         }, this);
22929         
22930         
22931         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22932             if (w.indexOf(tag) > -1) {
22933                 return;
22934             }
22935             this.cblack.push(tag);
22936             
22937         }, this);
22938         
22939         Roo.each(b, function(tag) {
22940             if (w.indexOf(tag) > -1) {
22941                 return;
22942             }
22943             if (this.cblack.indexOf(tag) > -1) {
22944                 return;
22945             }
22946             this.cblack.push(tag);
22947             
22948         }, this);
22949     },
22950     
22951     setStylesheets : function(stylesheets)
22952     {
22953         if(typeof(stylesheets) == 'string'){
22954             Roo.get(this.iframe.contentDocument.head).createChild({
22955                 tag : 'link',
22956                 rel : 'stylesheet',
22957                 type : 'text/css',
22958                 href : stylesheets
22959             });
22960             
22961             return;
22962         }
22963         var _this = this;
22964      
22965         Roo.each(stylesheets, function(s) {
22966             if(!s.length){
22967                 return;
22968             }
22969             
22970             Roo.get(_this.iframe.contentDocument.head).createChild({
22971                 tag : 'link',
22972                 rel : 'stylesheet',
22973                 type : 'text/css',
22974                 href : s
22975             });
22976         });
22977
22978         
22979     },
22980     
22981     removeStylesheets : function()
22982     {
22983         var _this = this;
22984         
22985         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22986             s.remove();
22987         });
22988     },
22989     
22990     setStyle : function(style)
22991     {
22992         Roo.get(this.iframe.contentDocument.head).createChild({
22993             tag : 'style',
22994             type : 'text/css',
22995             html : style
22996         });
22997
22998         return;
22999     }
23000     
23001     // hide stuff that is not compatible
23002     /**
23003      * @event blur
23004      * @hide
23005      */
23006     /**
23007      * @event change
23008      * @hide
23009      */
23010     /**
23011      * @event focus
23012      * @hide
23013      */
23014     /**
23015      * @event specialkey
23016      * @hide
23017      */
23018     /**
23019      * @cfg {String} fieldClass @hide
23020      */
23021     /**
23022      * @cfg {String} focusClass @hide
23023      */
23024     /**
23025      * @cfg {String} autoCreate @hide
23026      */
23027     /**
23028      * @cfg {String} inputType @hide
23029      */
23030     /**
23031      * @cfg {String} invalidClass @hide
23032      */
23033     /**
23034      * @cfg {String} invalidText @hide
23035      */
23036     /**
23037      * @cfg {String} msgFx @hide
23038      */
23039     /**
23040      * @cfg {String} validateOnBlur @hide
23041      */
23042 });
23043
23044 Roo.HtmlEditorCore.white = [
23045         'area', 'br', 'img', 'input', 'hr', 'wbr',
23046         
23047        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23048        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23049        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23050        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23051        'table',   'ul',         'xmp', 
23052        
23053        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23054       'thead',   'tr', 
23055      
23056       'dir', 'menu', 'ol', 'ul', 'dl',
23057        
23058       'embed',  'object'
23059 ];
23060
23061
23062 Roo.HtmlEditorCore.black = [
23063     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23064         'applet', // 
23065         'base',   'basefont', 'bgsound', 'blink',  'body', 
23066         'frame',  'frameset', 'head',    'html',   'ilayer', 
23067         'iframe', 'layer',  'link',     'meta',    'object',   
23068         'script', 'style' ,'title',  'xml' // clean later..
23069 ];
23070 Roo.HtmlEditorCore.clean = [
23071     'script', 'style', 'title', 'xml'
23072 ];
23073 Roo.HtmlEditorCore.remove = [
23074     'font'
23075 ];
23076 // attributes..
23077
23078 Roo.HtmlEditorCore.ablack = [
23079     'on'
23080 ];
23081     
23082 Roo.HtmlEditorCore.aclean = [ 
23083     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23084 ];
23085
23086 // protocols..
23087 Roo.HtmlEditorCore.pwhite= [
23088         'http',  'https',  'mailto'
23089 ];
23090
23091 // white listed style attributes.
23092 Roo.HtmlEditorCore.cwhite= [
23093       //  'text-align', /// default is to allow most things..
23094       
23095          
23096 //        'font-size'//??
23097 ];
23098
23099 // black listed style attributes.
23100 Roo.HtmlEditorCore.cblack= [
23101       //  'font-size' -- this can be set by the project 
23102 ];
23103
23104
23105 Roo.HtmlEditorCore.swapCodes   =[ 
23106     [    8211, "--" ], 
23107     [    8212, "--" ], 
23108     [    8216,  "'" ],  
23109     [    8217, "'" ],  
23110     [    8220, '"' ],  
23111     [    8221, '"' ],  
23112     [    8226, "*" ],  
23113     [    8230, "..." ]
23114 ]; 
23115
23116     /*
23117  * - LGPL
23118  *
23119  * HtmlEditor
23120  * 
23121  */
23122
23123 /**
23124  * @class Roo.bootstrap.HtmlEditor
23125  * @extends Roo.bootstrap.TextArea
23126  * Bootstrap HtmlEditor class
23127
23128  * @constructor
23129  * Create a new HtmlEditor
23130  * @param {Object} config The config object
23131  */
23132
23133 Roo.bootstrap.HtmlEditor = function(config){
23134     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23135     if (!this.toolbars) {
23136         this.toolbars = [];
23137     }
23138     
23139     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23140     this.addEvents({
23141             /**
23142              * @event initialize
23143              * Fires when the editor is fully initialized (including the iframe)
23144              * @param {HtmlEditor} this
23145              */
23146             initialize: true,
23147             /**
23148              * @event activate
23149              * Fires when the editor is first receives the focus. Any insertion must wait
23150              * until after this event.
23151              * @param {HtmlEditor} this
23152              */
23153             activate: true,
23154              /**
23155              * @event beforesync
23156              * Fires before the textarea is updated with content from the editor iframe. Return false
23157              * to cancel the sync.
23158              * @param {HtmlEditor} this
23159              * @param {String} html
23160              */
23161             beforesync: true,
23162              /**
23163              * @event beforepush
23164              * Fires before the iframe editor is updated with content from the textarea. Return false
23165              * to cancel the push.
23166              * @param {HtmlEditor} this
23167              * @param {String} html
23168              */
23169             beforepush: true,
23170              /**
23171              * @event sync
23172              * Fires when the textarea is updated with content from the editor iframe.
23173              * @param {HtmlEditor} this
23174              * @param {String} html
23175              */
23176             sync: true,
23177              /**
23178              * @event push
23179              * Fires when the iframe editor is updated with content from the textarea.
23180              * @param {HtmlEditor} this
23181              * @param {String} html
23182              */
23183             push: true,
23184              /**
23185              * @event editmodechange
23186              * Fires when the editor switches edit modes
23187              * @param {HtmlEditor} this
23188              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23189              */
23190             editmodechange: true,
23191             /**
23192              * @event editorevent
23193              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23194              * @param {HtmlEditor} this
23195              */
23196             editorevent: true,
23197             /**
23198              * @event firstfocus
23199              * Fires when on first focus - needed by toolbars..
23200              * @param {HtmlEditor} this
23201              */
23202             firstfocus: true,
23203             /**
23204              * @event autosave
23205              * Auto save the htmlEditor value as a file into Events
23206              * @param {HtmlEditor} this
23207              */
23208             autosave: true,
23209             /**
23210              * @event savedpreview
23211              * preview the saved version of htmlEditor
23212              * @param {HtmlEditor} this
23213              */
23214             savedpreview: true
23215         });
23216 };
23217
23218
23219 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23220     
23221     
23222       /**
23223      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23224      */
23225     toolbars : false,
23226     
23227      /**
23228     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23229     */
23230     btns : [],
23231    
23232      /**
23233      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23234      *                        Roo.resizable.
23235      */
23236     resizable : false,
23237      /**
23238      * @cfg {Number} height (in pixels)
23239      */   
23240     height: 300,
23241    /**
23242      * @cfg {Number} width (in pixels)
23243      */   
23244     width: false,
23245     
23246     /**
23247      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23248      * 
23249      */
23250     stylesheets: false,
23251     
23252     // id of frame..
23253     frameId: false,
23254     
23255     // private properties
23256     validationEvent : false,
23257     deferHeight: true,
23258     initialized : false,
23259     activated : false,
23260     
23261     onFocus : Roo.emptyFn,
23262     iframePad:3,
23263     hideMode:'offsets',
23264     
23265     tbContainer : false,
23266     
23267     bodyCls : '',
23268     
23269     toolbarContainer :function() {
23270         return this.wrap.select('.x-html-editor-tb',true).first();
23271     },
23272
23273     /**
23274      * Protected method that will not generally be called directly. It
23275      * is called when the editor creates its toolbar. Override this method if you need to
23276      * add custom toolbar buttons.
23277      * @param {HtmlEditor} editor
23278      */
23279     createToolbar : function(){
23280         Roo.log('renewing');
23281         Roo.log("create toolbars");
23282         
23283         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23284         this.toolbars[0].render(this.toolbarContainer());
23285         
23286         return;
23287         
23288 //        if (!editor.toolbars || !editor.toolbars.length) {
23289 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23290 //        }
23291 //        
23292 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23293 //            editor.toolbars[i] = Roo.factory(
23294 //                    typeof(editor.toolbars[i]) == 'string' ?
23295 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23296 //                Roo.bootstrap.HtmlEditor);
23297 //            editor.toolbars[i].init(editor);
23298 //        }
23299     },
23300
23301      
23302     // private
23303     onRender : function(ct, position)
23304     {
23305        // Roo.log("Call onRender: " + this.xtype);
23306         var _t = this;
23307         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23308       
23309         this.wrap = this.inputEl().wrap({
23310             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23311         });
23312         
23313         this.editorcore.onRender(ct, position);
23314          
23315         if (this.resizable) {
23316             this.resizeEl = new Roo.Resizable(this.wrap, {
23317                 pinned : true,
23318                 wrap: true,
23319                 dynamic : true,
23320                 minHeight : this.height,
23321                 height: this.height,
23322                 handles : this.resizable,
23323                 width: this.width,
23324                 listeners : {
23325                     resize : function(r, w, h) {
23326                         _t.onResize(w,h); // -something
23327                     }
23328                 }
23329             });
23330             
23331         }
23332         this.createToolbar(this);
23333        
23334         
23335         if(!this.width && this.resizable){
23336             this.setSize(this.wrap.getSize());
23337         }
23338         if (this.resizeEl) {
23339             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23340             // should trigger onReize..
23341         }
23342         
23343     },
23344
23345     // private
23346     onResize : function(w, h)
23347     {
23348         Roo.log('resize: ' +w + ',' + h );
23349         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23350         var ew = false;
23351         var eh = false;
23352         
23353         if(this.inputEl() ){
23354             if(typeof w == 'number'){
23355                 var aw = w - this.wrap.getFrameWidth('lr');
23356                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23357                 ew = aw;
23358             }
23359             if(typeof h == 'number'){
23360                  var tbh = -11;  // fixme it needs to tool bar size!
23361                 for (var i =0; i < this.toolbars.length;i++) {
23362                     // fixme - ask toolbars for heights?
23363                     tbh += this.toolbars[i].el.getHeight();
23364                     //if (this.toolbars[i].footer) {
23365                     //    tbh += this.toolbars[i].footer.el.getHeight();
23366                     //}
23367                 }
23368               
23369                 
23370                 
23371                 
23372                 
23373                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23374                 ah -= 5; // knock a few pixes off for look..
23375                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23376                 var eh = ah;
23377             }
23378         }
23379         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23380         this.editorcore.onResize(ew,eh);
23381         
23382     },
23383
23384     /**
23385      * Toggles the editor between standard and source edit mode.
23386      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23387      */
23388     toggleSourceEdit : function(sourceEditMode)
23389     {
23390         this.editorcore.toggleSourceEdit(sourceEditMode);
23391         
23392         if(this.editorcore.sourceEditMode){
23393             Roo.log('editor - showing textarea');
23394             
23395 //            Roo.log('in');
23396 //            Roo.log(this.syncValue());
23397             this.syncValue();
23398             this.inputEl().removeClass(['hide', 'x-hidden']);
23399             this.inputEl().dom.removeAttribute('tabIndex');
23400             this.inputEl().focus();
23401         }else{
23402             Roo.log('editor - hiding textarea');
23403 //            Roo.log('out')
23404 //            Roo.log(this.pushValue()); 
23405             this.pushValue();
23406             
23407             this.inputEl().addClass(['hide', 'x-hidden']);
23408             this.inputEl().dom.setAttribute('tabIndex', -1);
23409             //this.deferFocus();
23410         }
23411          
23412         if(this.resizable){
23413             this.setSize(this.wrap.getSize());
23414         }
23415         
23416         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23417     },
23418  
23419     // private (for BoxComponent)
23420     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23421
23422     // private (for BoxComponent)
23423     getResizeEl : function(){
23424         return this.wrap;
23425     },
23426
23427     // private (for BoxComponent)
23428     getPositionEl : function(){
23429         return this.wrap;
23430     },
23431
23432     // private
23433     initEvents : function(){
23434         this.originalValue = this.getValue();
23435     },
23436
23437 //    /**
23438 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23439 //     * @method
23440 //     */
23441 //    markInvalid : Roo.emptyFn,
23442 //    /**
23443 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23444 //     * @method
23445 //     */
23446 //    clearInvalid : Roo.emptyFn,
23447
23448     setValue : function(v){
23449         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23450         this.editorcore.pushValue();
23451     },
23452
23453      
23454     // private
23455     deferFocus : function(){
23456         this.focus.defer(10, this);
23457     },
23458
23459     // doc'ed in Field
23460     focus : function(){
23461         this.editorcore.focus();
23462         
23463     },
23464       
23465
23466     // private
23467     onDestroy : function(){
23468         
23469         
23470         
23471         if(this.rendered){
23472             
23473             for (var i =0; i < this.toolbars.length;i++) {
23474                 // fixme - ask toolbars for heights?
23475                 this.toolbars[i].onDestroy();
23476             }
23477             
23478             this.wrap.dom.innerHTML = '';
23479             this.wrap.remove();
23480         }
23481     },
23482
23483     // private
23484     onFirstFocus : function(){
23485         //Roo.log("onFirstFocus");
23486         this.editorcore.onFirstFocus();
23487          for (var i =0; i < this.toolbars.length;i++) {
23488             this.toolbars[i].onFirstFocus();
23489         }
23490         
23491     },
23492     
23493     // private
23494     syncValue : function()
23495     {   
23496         this.editorcore.syncValue();
23497     },
23498     
23499     pushValue : function()
23500     {   
23501         this.editorcore.pushValue();
23502     }
23503      
23504     
23505     // hide stuff that is not compatible
23506     /**
23507      * @event blur
23508      * @hide
23509      */
23510     /**
23511      * @event change
23512      * @hide
23513      */
23514     /**
23515      * @event focus
23516      * @hide
23517      */
23518     /**
23519      * @event specialkey
23520      * @hide
23521      */
23522     /**
23523      * @cfg {String} fieldClass @hide
23524      */
23525     /**
23526      * @cfg {String} focusClass @hide
23527      */
23528     /**
23529      * @cfg {String} autoCreate @hide
23530      */
23531     /**
23532      * @cfg {String} inputType @hide
23533      */
23534     /**
23535      * @cfg {String} invalidClass @hide
23536      */
23537     /**
23538      * @cfg {String} invalidText @hide
23539      */
23540     /**
23541      * @cfg {String} msgFx @hide
23542      */
23543     /**
23544      * @cfg {String} validateOnBlur @hide
23545      */
23546 });
23547  
23548     
23549    
23550    
23551    
23552       
23553 Roo.namespace('Roo.bootstrap.htmleditor');
23554 /**
23555  * @class Roo.bootstrap.HtmlEditorToolbar1
23556  * Basic Toolbar
23557  * 
23558  * Usage:
23559  *
23560  new Roo.bootstrap.HtmlEditor({
23561     ....
23562     toolbars : [
23563         new Roo.bootstrap.HtmlEditorToolbar1({
23564             disable : { fonts: 1 , format: 1, ..., ... , ...],
23565             btns : [ .... ]
23566         })
23567     }
23568      
23569  * 
23570  * @cfg {Object} disable List of elements to disable..
23571  * @cfg {Array} btns List of additional buttons.
23572  * 
23573  * 
23574  * NEEDS Extra CSS? 
23575  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23576  */
23577  
23578 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23579 {
23580     
23581     Roo.apply(this, config);
23582     
23583     // default disabled, based on 'good practice'..
23584     this.disable = this.disable || {};
23585     Roo.applyIf(this.disable, {
23586         fontSize : true,
23587         colors : true,
23588         specialElements : true
23589     });
23590     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23591     
23592     this.editor = config.editor;
23593     this.editorcore = config.editor.editorcore;
23594     
23595     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23596     
23597     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23598     // dont call parent... till later.
23599 }
23600 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23601      
23602     bar : true,
23603     
23604     editor : false,
23605     editorcore : false,
23606     
23607     
23608     formats : [
23609         "p" ,  
23610         "h1","h2","h3","h4","h5","h6", 
23611         "pre", "code", 
23612         "abbr", "acronym", "address", "cite", "samp", "var",
23613         'div','span'
23614     ],
23615     
23616     onRender : function(ct, position)
23617     {
23618        // Roo.log("Call onRender: " + this.xtype);
23619         
23620        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23621        Roo.log(this.el);
23622        this.el.dom.style.marginBottom = '0';
23623        var _this = this;
23624        var editorcore = this.editorcore;
23625        var editor= this.editor;
23626        
23627        var children = [];
23628        var btn = function(id,cmd , toggle, handler, html){
23629        
23630             var  event = toggle ? 'toggle' : 'click';
23631        
23632             var a = {
23633                 size : 'sm',
23634                 xtype: 'Button',
23635                 xns: Roo.bootstrap,
23636                 glyphicon : id,
23637                 cmd : id || cmd,
23638                 enableToggle:toggle !== false,
23639                 html : html || '',
23640                 pressed : toggle ? false : null,
23641                 listeners : {}
23642             };
23643             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23644                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23645             };
23646             children.push(a);
23647             return a;
23648        }
23649        
23650     //    var cb_box = function...
23651         
23652         var style = {
23653                 xtype: 'Button',
23654                 size : 'sm',
23655                 xns: Roo.bootstrap,
23656                 glyphicon : 'font',
23657                 //html : 'submit'
23658                 menu : {
23659                     xtype: 'Menu',
23660                     xns: Roo.bootstrap,
23661                     items:  []
23662                 }
23663         };
23664         Roo.each(this.formats, function(f) {
23665             style.menu.items.push({
23666                 xtype :'MenuItem',
23667                 xns: Roo.bootstrap,
23668                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23669                 tagname : f,
23670                 listeners : {
23671                     click : function()
23672                     {
23673                         editorcore.insertTag(this.tagname);
23674                         editor.focus();
23675                     }
23676                 }
23677                 
23678             });
23679         });
23680         children.push(style);   
23681         
23682         btn('bold',false,true);
23683         btn('italic',false,true);
23684         btn('align-left', 'justifyleft',true);
23685         btn('align-center', 'justifycenter',true);
23686         btn('align-right' , 'justifyright',true);
23687         btn('link', false, false, function(btn) {
23688             //Roo.log("create link?");
23689             var url = prompt(this.createLinkText, this.defaultLinkValue);
23690             if(url && url != 'http:/'+'/'){
23691                 this.editorcore.relayCmd('createlink', url);
23692             }
23693         }),
23694         btn('list','insertunorderedlist',true);
23695         btn('pencil', false,true, function(btn){
23696                 Roo.log(this);
23697                 this.toggleSourceEdit(btn.pressed);
23698         });
23699         
23700         if (this.editor.btns.length > 0) {
23701             for (var i = 0; i<this.editor.btns.length; i++) {
23702                 children.push(this.editor.btns[i]);
23703             }
23704         }
23705         
23706         /*
23707         var cog = {
23708                 xtype: 'Button',
23709                 size : 'sm',
23710                 xns: Roo.bootstrap,
23711                 glyphicon : 'cog',
23712                 //html : 'submit'
23713                 menu : {
23714                     xtype: 'Menu',
23715                     xns: Roo.bootstrap,
23716                     items:  []
23717                 }
23718         };
23719         
23720         cog.menu.items.push({
23721             xtype :'MenuItem',
23722             xns: Roo.bootstrap,
23723             html : Clean styles,
23724             tagname : f,
23725             listeners : {
23726                 click : function()
23727                 {
23728                     editorcore.insertTag(this.tagname);
23729                     editor.focus();
23730                 }
23731             }
23732             
23733         });
23734        */
23735         
23736          
23737        this.xtype = 'NavSimplebar';
23738         
23739         for(var i=0;i< children.length;i++) {
23740             
23741             this.buttons.add(this.addxtypeChild(children[i]));
23742             
23743         }
23744         
23745         editor.on('editorevent', this.updateToolbar, this);
23746     },
23747     onBtnClick : function(id)
23748     {
23749        this.editorcore.relayCmd(id);
23750        this.editorcore.focus();
23751     },
23752     
23753     /**
23754      * Protected method that will not generally be called directly. It triggers
23755      * a toolbar update by reading the markup state of the current selection in the editor.
23756      */
23757     updateToolbar: function(){
23758
23759         if(!this.editorcore.activated){
23760             this.editor.onFirstFocus(); // is this neeed?
23761             return;
23762         }
23763
23764         var btns = this.buttons; 
23765         var doc = this.editorcore.doc;
23766         btns.get('bold').setActive(doc.queryCommandState('bold'));
23767         btns.get('italic').setActive(doc.queryCommandState('italic'));
23768         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23769         
23770         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23771         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23772         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23773         
23774         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23775         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23776          /*
23777         
23778         var ans = this.editorcore.getAllAncestors();
23779         if (this.formatCombo) {
23780             
23781             
23782             var store = this.formatCombo.store;
23783             this.formatCombo.setValue("");
23784             for (var i =0; i < ans.length;i++) {
23785                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23786                     // select it..
23787                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23788                     break;
23789                 }
23790             }
23791         }
23792         
23793         
23794         
23795         // hides menus... - so this cant be on a menu...
23796         Roo.bootstrap.MenuMgr.hideAll();
23797         */
23798         Roo.bootstrap.MenuMgr.hideAll();
23799         //this.editorsyncValue();
23800     },
23801     onFirstFocus: function() {
23802         this.buttons.each(function(item){
23803            item.enable();
23804         });
23805     },
23806     toggleSourceEdit : function(sourceEditMode){
23807         
23808           
23809         if(sourceEditMode){
23810             Roo.log("disabling buttons");
23811            this.buttons.each( function(item){
23812                 if(item.cmd != 'pencil'){
23813                     item.disable();
23814                 }
23815             });
23816           
23817         }else{
23818             Roo.log("enabling buttons");
23819             if(this.editorcore.initialized){
23820                 this.buttons.each( function(item){
23821                     item.enable();
23822                 });
23823             }
23824             
23825         }
23826         Roo.log("calling toggole on editor");
23827         // tell the editor that it's been pressed..
23828         this.editor.toggleSourceEdit(sourceEditMode);
23829        
23830     }
23831 });
23832
23833
23834
23835
23836
23837 /**
23838  * @class Roo.bootstrap.Table.AbstractSelectionModel
23839  * @extends Roo.util.Observable
23840  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23841  * implemented by descendant classes.  This class should not be directly instantiated.
23842  * @constructor
23843  */
23844 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23845     this.locked = false;
23846     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23847 };
23848
23849
23850 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23851     /** @ignore Called by the grid automatically. Do not call directly. */
23852     init : function(grid){
23853         this.grid = grid;
23854         this.initEvents();
23855     },
23856
23857     /**
23858      * Locks the selections.
23859      */
23860     lock : function(){
23861         this.locked = true;
23862     },
23863
23864     /**
23865      * Unlocks the selections.
23866      */
23867     unlock : function(){
23868         this.locked = false;
23869     },
23870
23871     /**
23872      * Returns true if the selections are locked.
23873      * @return {Boolean}
23874      */
23875     isLocked : function(){
23876         return this.locked;
23877     }
23878 });
23879 /**
23880  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23881  * @class Roo.bootstrap.Table.RowSelectionModel
23882  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23883  * It supports multiple selections and keyboard selection/navigation. 
23884  * @constructor
23885  * @param {Object} config
23886  */
23887
23888 Roo.bootstrap.Table.RowSelectionModel = function(config){
23889     Roo.apply(this, config);
23890     this.selections = new Roo.util.MixedCollection(false, function(o){
23891         return o.id;
23892     });
23893
23894     this.last = false;
23895     this.lastActive = false;
23896
23897     this.addEvents({
23898         /**
23899              * @event selectionchange
23900              * Fires when the selection changes
23901              * @param {SelectionModel} this
23902              */
23903             "selectionchange" : true,
23904         /**
23905              * @event afterselectionchange
23906              * Fires after the selection changes (eg. by key press or clicking)
23907              * @param {SelectionModel} this
23908              */
23909             "afterselectionchange" : true,
23910         /**
23911              * @event beforerowselect
23912              * Fires when a row is selected being selected, return false to cancel.
23913              * @param {SelectionModel} this
23914              * @param {Number} rowIndex The selected index
23915              * @param {Boolean} keepExisting False if other selections will be cleared
23916              */
23917             "beforerowselect" : true,
23918         /**
23919              * @event rowselect
23920              * Fires when a row is selected.
23921              * @param {SelectionModel} this
23922              * @param {Number} rowIndex The selected index
23923              * @param {Roo.data.Record} r The record
23924              */
23925             "rowselect" : true,
23926         /**
23927              * @event rowdeselect
23928              * Fires when a row is deselected.
23929              * @param {SelectionModel} this
23930              * @param {Number} rowIndex The selected index
23931              */
23932         "rowdeselect" : true
23933     });
23934     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23935     this.locked = false;
23936  };
23937
23938 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23939     /**
23940      * @cfg {Boolean} singleSelect
23941      * True to allow selection of only one row at a time (defaults to false)
23942      */
23943     singleSelect : false,
23944
23945     // private
23946     initEvents : function()
23947     {
23948
23949         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23950         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23951         //}else{ // allow click to work like normal
23952          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23953         //}
23954         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23955         this.grid.on("rowclick", this.handleMouseDown, this);
23956         
23957         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23958             "up" : function(e){
23959                 if(!e.shiftKey){
23960                     this.selectPrevious(e.shiftKey);
23961                 }else if(this.last !== false && this.lastActive !== false){
23962                     var last = this.last;
23963                     this.selectRange(this.last,  this.lastActive-1);
23964                     this.grid.getView().focusRow(this.lastActive);
23965                     if(last !== false){
23966                         this.last = last;
23967                     }
23968                 }else{
23969                     this.selectFirstRow();
23970                 }
23971                 this.fireEvent("afterselectionchange", this);
23972             },
23973             "down" : function(e){
23974                 if(!e.shiftKey){
23975                     this.selectNext(e.shiftKey);
23976                 }else if(this.last !== false && this.lastActive !== false){
23977                     var last = this.last;
23978                     this.selectRange(this.last,  this.lastActive+1);
23979                     this.grid.getView().focusRow(this.lastActive);
23980                     if(last !== false){
23981                         this.last = last;
23982                     }
23983                 }else{
23984                     this.selectFirstRow();
23985                 }
23986                 this.fireEvent("afterselectionchange", this);
23987             },
23988             scope: this
23989         });
23990         this.grid.store.on('load', function(){
23991             this.selections.clear();
23992         },this);
23993         /*
23994         var view = this.grid.view;
23995         view.on("refresh", this.onRefresh, this);
23996         view.on("rowupdated", this.onRowUpdated, this);
23997         view.on("rowremoved", this.onRemove, this);
23998         */
23999     },
24000
24001     // private
24002     onRefresh : function()
24003     {
24004         var ds = this.grid.store, i, v = this.grid.view;
24005         var s = this.selections;
24006         s.each(function(r){
24007             if((i = ds.indexOfId(r.id)) != -1){
24008                 v.onRowSelect(i);
24009             }else{
24010                 s.remove(r);
24011             }
24012         });
24013     },
24014
24015     // private
24016     onRemove : function(v, index, r){
24017         this.selections.remove(r);
24018     },
24019
24020     // private
24021     onRowUpdated : function(v, index, r){
24022         if(this.isSelected(r)){
24023             v.onRowSelect(index);
24024         }
24025     },
24026
24027     /**
24028      * Select records.
24029      * @param {Array} records The records to select
24030      * @param {Boolean} keepExisting (optional) True to keep existing selections
24031      */
24032     selectRecords : function(records, keepExisting)
24033     {
24034         if(!keepExisting){
24035             this.clearSelections();
24036         }
24037             var ds = this.grid.store;
24038         for(var i = 0, len = records.length; i < len; i++){
24039             this.selectRow(ds.indexOf(records[i]), true);
24040         }
24041     },
24042
24043     /**
24044      * Gets the number of selected rows.
24045      * @return {Number}
24046      */
24047     getCount : function(){
24048         return this.selections.length;
24049     },
24050
24051     /**
24052      * Selects the first row in the grid.
24053      */
24054     selectFirstRow : function(){
24055         this.selectRow(0);
24056     },
24057
24058     /**
24059      * Select the last row.
24060      * @param {Boolean} keepExisting (optional) True to keep existing selections
24061      */
24062     selectLastRow : function(keepExisting){
24063         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24064         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24065     },
24066
24067     /**
24068      * Selects the row immediately following the last selected row.
24069      * @param {Boolean} keepExisting (optional) True to keep existing selections
24070      */
24071     selectNext : function(keepExisting)
24072     {
24073             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24074             this.selectRow(this.last+1, keepExisting);
24075             this.grid.getView().focusRow(this.last);
24076         }
24077     },
24078
24079     /**
24080      * Selects the row that precedes the last selected row.
24081      * @param {Boolean} keepExisting (optional) True to keep existing selections
24082      */
24083     selectPrevious : function(keepExisting){
24084         if(this.last){
24085             this.selectRow(this.last-1, keepExisting);
24086             this.grid.getView().focusRow(this.last);
24087         }
24088     },
24089
24090     /**
24091      * Returns the selected records
24092      * @return {Array} Array of selected records
24093      */
24094     getSelections : function(){
24095         return [].concat(this.selections.items);
24096     },
24097
24098     /**
24099      * Returns the first selected record.
24100      * @return {Record}
24101      */
24102     getSelected : function(){
24103         return this.selections.itemAt(0);
24104     },
24105
24106
24107     /**
24108      * Clears all selections.
24109      */
24110     clearSelections : function(fast)
24111     {
24112         if(this.locked) {
24113             return;
24114         }
24115         if(fast !== true){
24116                 var ds = this.grid.store;
24117             var s = this.selections;
24118             s.each(function(r){
24119                 this.deselectRow(ds.indexOfId(r.id));
24120             }, this);
24121             s.clear();
24122         }else{
24123             this.selections.clear();
24124         }
24125         this.last = false;
24126     },
24127
24128
24129     /**
24130      * Selects all rows.
24131      */
24132     selectAll : function(){
24133         if(this.locked) {
24134             return;
24135         }
24136         this.selections.clear();
24137         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24138             this.selectRow(i, true);
24139         }
24140     },
24141
24142     /**
24143      * Returns True if there is a selection.
24144      * @return {Boolean}
24145      */
24146     hasSelection : function(){
24147         return this.selections.length > 0;
24148     },
24149
24150     /**
24151      * Returns True if the specified row is selected.
24152      * @param {Number/Record} record The record or index of the record to check
24153      * @return {Boolean}
24154      */
24155     isSelected : function(index){
24156             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24157         return (r && this.selections.key(r.id) ? true : false);
24158     },
24159
24160     /**
24161      * Returns True if the specified record id is selected.
24162      * @param {String} id The id of record to check
24163      * @return {Boolean}
24164      */
24165     isIdSelected : function(id){
24166         return (this.selections.key(id) ? true : false);
24167     },
24168
24169
24170     // private
24171     handleMouseDBClick : function(e, t){
24172         
24173     },
24174     // private
24175     handleMouseDown : function(e, t)
24176     {
24177             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24178         if(this.isLocked() || rowIndex < 0 ){
24179             return;
24180         };
24181         if(e.shiftKey && this.last !== false){
24182             var last = this.last;
24183             this.selectRange(last, rowIndex, e.ctrlKey);
24184             this.last = last; // reset the last
24185             t.focus();
24186     
24187         }else{
24188             var isSelected = this.isSelected(rowIndex);
24189             //Roo.log("select row:" + rowIndex);
24190             if(isSelected){
24191                 this.deselectRow(rowIndex);
24192             } else {
24193                         this.selectRow(rowIndex, true);
24194             }
24195     
24196             /*
24197                 if(e.button !== 0 && isSelected){
24198                 alert('rowIndex 2: ' + rowIndex);
24199                     view.focusRow(rowIndex);
24200                 }else if(e.ctrlKey && isSelected){
24201                     this.deselectRow(rowIndex);
24202                 }else if(!isSelected){
24203                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24204                     view.focusRow(rowIndex);
24205                 }
24206             */
24207         }
24208         this.fireEvent("afterselectionchange", this);
24209     },
24210     // private
24211     handleDragableRowClick :  function(grid, rowIndex, e) 
24212     {
24213         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24214             this.selectRow(rowIndex, false);
24215             grid.view.focusRow(rowIndex);
24216              this.fireEvent("afterselectionchange", this);
24217         }
24218     },
24219     
24220     /**
24221      * Selects multiple rows.
24222      * @param {Array} rows Array of the indexes of the row to select
24223      * @param {Boolean} keepExisting (optional) True to keep existing selections
24224      */
24225     selectRows : function(rows, keepExisting){
24226         if(!keepExisting){
24227             this.clearSelections();
24228         }
24229         for(var i = 0, len = rows.length; i < len; i++){
24230             this.selectRow(rows[i], true);
24231         }
24232     },
24233
24234     /**
24235      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24236      * @param {Number} startRow The index of the first row in the range
24237      * @param {Number} endRow The index of the last row in the range
24238      * @param {Boolean} keepExisting (optional) True to retain existing selections
24239      */
24240     selectRange : function(startRow, endRow, keepExisting){
24241         if(this.locked) {
24242             return;
24243         }
24244         if(!keepExisting){
24245             this.clearSelections();
24246         }
24247         if(startRow <= endRow){
24248             for(var i = startRow; i <= endRow; i++){
24249                 this.selectRow(i, true);
24250             }
24251         }else{
24252             for(var i = startRow; i >= endRow; i--){
24253                 this.selectRow(i, true);
24254             }
24255         }
24256     },
24257
24258     /**
24259      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24260      * @param {Number} startRow The index of the first row in the range
24261      * @param {Number} endRow The index of the last row in the range
24262      */
24263     deselectRange : function(startRow, endRow, preventViewNotify){
24264         if(this.locked) {
24265             return;
24266         }
24267         for(var i = startRow; i <= endRow; i++){
24268             this.deselectRow(i, preventViewNotify);
24269         }
24270     },
24271
24272     /**
24273      * Selects a row.
24274      * @param {Number} row The index of the row to select
24275      * @param {Boolean} keepExisting (optional) True to keep existing selections
24276      */
24277     selectRow : function(index, keepExisting, preventViewNotify)
24278     {
24279             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24280             return;
24281         }
24282         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24283             if(!keepExisting || this.singleSelect){
24284                 this.clearSelections();
24285             }
24286             
24287             var r = this.grid.store.getAt(index);
24288             //console.log('selectRow - record id :' + r.id);
24289             
24290             this.selections.add(r);
24291             this.last = this.lastActive = index;
24292             if(!preventViewNotify){
24293                 var proxy = new Roo.Element(
24294                                 this.grid.getRowDom(index)
24295                 );
24296                 proxy.addClass('bg-info info');
24297             }
24298             this.fireEvent("rowselect", this, index, r);
24299             this.fireEvent("selectionchange", this);
24300         }
24301     },
24302
24303     /**
24304      * Deselects a row.
24305      * @param {Number} row The index of the row to deselect
24306      */
24307     deselectRow : function(index, preventViewNotify)
24308     {
24309         if(this.locked) {
24310             return;
24311         }
24312         if(this.last == index){
24313             this.last = false;
24314         }
24315         if(this.lastActive == index){
24316             this.lastActive = false;
24317         }
24318         
24319         var r = this.grid.store.getAt(index);
24320         if (!r) {
24321             return;
24322         }
24323         
24324         this.selections.remove(r);
24325         //.console.log('deselectRow - record id :' + r.id);
24326         if(!preventViewNotify){
24327         
24328             var proxy = new Roo.Element(
24329                 this.grid.getRowDom(index)
24330             );
24331             proxy.removeClass('bg-info info');
24332         }
24333         this.fireEvent("rowdeselect", this, index);
24334         this.fireEvent("selectionchange", this);
24335     },
24336
24337     // private
24338     restoreLast : function(){
24339         if(this._last){
24340             this.last = this._last;
24341         }
24342     },
24343
24344     // private
24345     acceptsNav : function(row, col, cm){
24346         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24347     },
24348
24349     // private
24350     onEditorKey : function(field, e){
24351         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24352         if(k == e.TAB){
24353             e.stopEvent();
24354             ed.completeEdit();
24355             if(e.shiftKey){
24356                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24357             }else{
24358                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24359             }
24360         }else if(k == e.ENTER && !e.ctrlKey){
24361             e.stopEvent();
24362             ed.completeEdit();
24363             if(e.shiftKey){
24364                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24365             }else{
24366                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24367             }
24368         }else if(k == e.ESC){
24369             ed.cancelEdit();
24370         }
24371         if(newCell){
24372             g.startEditing(newCell[0], newCell[1]);
24373         }
24374     }
24375 });
24376 /*
24377  * Based on:
24378  * Ext JS Library 1.1.1
24379  * Copyright(c) 2006-2007, Ext JS, LLC.
24380  *
24381  * Originally Released Under LGPL - original licence link has changed is not relivant.
24382  *
24383  * Fork - LGPL
24384  * <script type="text/javascript">
24385  */
24386  
24387 /**
24388  * @class Roo.bootstrap.PagingToolbar
24389  * @extends Roo.bootstrap.NavSimplebar
24390  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24391  * @constructor
24392  * Create a new PagingToolbar
24393  * @param {Object} config The config object
24394  * @param {Roo.data.Store} store
24395  */
24396 Roo.bootstrap.PagingToolbar = function(config)
24397 {
24398     // old args format still supported... - xtype is prefered..
24399         // created from xtype...
24400     
24401     this.ds = config.dataSource;
24402     
24403     if (config.store && !this.ds) {
24404         this.store= Roo.factory(config.store, Roo.data);
24405         this.ds = this.store;
24406         this.ds.xmodule = this.xmodule || false;
24407     }
24408     
24409     this.toolbarItems = [];
24410     if (config.items) {
24411         this.toolbarItems = config.items;
24412     }
24413     
24414     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24415     
24416     this.cursor = 0;
24417     
24418     if (this.ds) { 
24419         this.bind(this.ds);
24420     }
24421     
24422     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24423     
24424 };
24425
24426 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24427     /**
24428      * @cfg {Roo.data.Store} dataSource
24429      * The underlying data store providing the paged data
24430      */
24431     /**
24432      * @cfg {String/HTMLElement/Element} container
24433      * container The id or element that will contain the toolbar
24434      */
24435     /**
24436      * @cfg {Boolean} displayInfo
24437      * True to display the displayMsg (defaults to false)
24438      */
24439     /**
24440      * @cfg {Number} pageSize
24441      * The number of records to display per page (defaults to 20)
24442      */
24443     pageSize: 20,
24444     /**
24445      * @cfg {String} displayMsg
24446      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24447      */
24448     displayMsg : 'Displaying {0} - {1} of {2}',
24449     /**
24450      * @cfg {String} emptyMsg
24451      * The message to display when no records are found (defaults to "No data to display")
24452      */
24453     emptyMsg : 'No data to display',
24454     /**
24455      * Customizable piece of the default paging text (defaults to "Page")
24456      * @type String
24457      */
24458     beforePageText : "Page",
24459     /**
24460      * Customizable piece of the default paging text (defaults to "of %0")
24461      * @type String
24462      */
24463     afterPageText : "of {0}",
24464     /**
24465      * Customizable piece of the default paging text (defaults to "First Page")
24466      * @type String
24467      */
24468     firstText : "First Page",
24469     /**
24470      * Customizable piece of the default paging text (defaults to "Previous Page")
24471      * @type String
24472      */
24473     prevText : "Previous Page",
24474     /**
24475      * Customizable piece of the default paging text (defaults to "Next Page")
24476      * @type String
24477      */
24478     nextText : "Next Page",
24479     /**
24480      * Customizable piece of the default paging text (defaults to "Last Page")
24481      * @type String
24482      */
24483     lastText : "Last Page",
24484     /**
24485      * Customizable piece of the default paging text (defaults to "Refresh")
24486      * @type String
24487      */
24488     refreshText : "Refresh",
24489
24490     buttons : false,
24491     // private
24492     onRender : function(ct, position) 
24493     {
24494         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24495         this.navgroup.parentId = this.id;
24496         this.navgroup.onRender(this.el, null);
24497         // add the buttons to the navgroup
24498         
24499         if(this.displayInfo){
24500             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24501             this.displayEl = this.el.select('.x-paging-info', true).first();
24502 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24503 //            this.displayEl = navel.el.select('span',true).first();
24504         }
24505         
24506         var _this = this;
24507         
24508         if(this.buttons){
24509             Roo.each(_this.buttons, function(e){ // this might need to use render????
24510                Roo.factory(e).onRender(_this.el, null);
24511             });
24512         }
24513             
24514         Roo.each(_this.toolbarItems, function(e) {
24515             _this.navgroup.addItem(e);
24516         });
24517         
24518         
24519         this.first = this.navgroup.addItem({
24520             tooltip: this.firstText,
24521             cls: "prev",
24522             icon : 'fa fa-backward',
24523             disabled: true,
24524             preventDefault: true,
24525             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24526         });
24527         
24528         this.prev =  this.navgroup.addItem({
24529             tooltip: this.prevText,
24530             cls: "prev",
24531             icon : 'fa fa-step-backward',
24532             disabled: true,
24533             preventDefault: true,
24534             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24535         });
24536     //this.addSeparator();
24537         
24538         
24539         var field = this.navgroup.addItem( {
24540             tagtype : 'span',
24541             cls : 'x-paging-position',
24542             
24543             html : this.beforePageText  +
24544                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24545                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24546          } ); //?? escaped?
24547         
24548         this.field = field.el.select('input', true).first();
24549         this.field.on("keydown", this.onPagingKeydown, this);
24550         this.field.on("focus", function(){this.dom.select();});
24551     
24552     
24553         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24554         //this.field.setHeight(18);
24555         //this.addSeparator();
24556         this.next = this.navgroup.addItem({
24557             tooltip: this.nextText,
24558             cls: "next",
24559             html : ' <i class="fa fa-step-forward">',
24560             disabled: true,
24561             preventDefault: true,
24562             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24563         });
24564         this.last = this.navgroup.addItem({
24565             tooltip: this.lastText,
24566             icon : 'fa fa-forward',
24567             cls: "next",
24568             disabled: true,
24569             preventDefault: true,
24570             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24571         });
24572     //this.addSeparator();
24573         this.loading = this.navgroup.addItem({
24574             tooltip: this.refreshText,
24575             icon: 'fa fa-refresh',
24576             preventDefault: true,
24577             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24578         });
24579         
24580     },
24581
24582     // private
24583     updateInfo : function(){
24584         if(this.displayEl){
24585             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24586             var msg = count == 0 ?
24587                 this.emptyMsg :
24588                 String.format(
24589                     this.displayMsg,
24590                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24591                 );
24592             this.displayEl.update(msg);
24593         }
24594     },
24595
24596     // private
24597     onLoad : function(ds, r, o)
24598     {
24599         this.cursor = o.params.start ? o.params.start : 0;
24600         
24601         var d = this.getPageData(),
24602             ap = d.activePage,
24603             ps = d.pages;
24604         
24605         
24606         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24607         this.field.dom.value = ap;
24608         this.first.setDisabled(ap == 1);
24609         this.prev.setDisabled(ap == 1);
24610         this.next.setDisabled(ap == ps);
24611         this.last.setDisabled(ap == ps);
24612         this.loading.enable();
24613         this.updateInfo();
24614     },
24615
24616     // private
24617     getPageData : function(){
24618         var total = this.ds.getTotalCount();
24619         return {
24620             total : total,
24621             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24622             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24623         };
24624     },
24625
24626     // private
24627     onLoadError : function(){
24628         this.loading.enable();
24629     },
24630
24631     // private
24632     onPagingKeydown : function(e){
24633         var k = e.getKey();
24634         var d = this.getPageData();
24635         if(k == e.RETURN){
24636             var v = this.field.dom.value, pageNum;
24637             if(!v || isNaN(pageNum = parseInt(v, 10))){
24638                 this.field.dom.value = d.activePage;
24639                 return;
24640             }
24641             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24642             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24643             e.stopEvent();
24644         }
24645         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))
24646         {
24647           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24648           this.field.dom.value = pageNum;
24649           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24650           e.stopEvent();
24651         }
24652         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24653         {
24654           var v = this.field.dom.value, pageNum; 
24655           var increment = (e.shiftKey) ? 10 : 1;
24656           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24657                 increment *= -1;
24658           }
24659           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24660             this.field.dom.value = d.activePage;
24661             return;
24662           }
24663           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24664           {
24665             this.field.dom.value = parseInt(v, 10) + increment;
24666             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24667             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24668           }
24669           e.stopEvent();
24670         }
24671     },
24672
24673     // private
24674     beforeLoad : function(){
24675         if(this.loading){
24676             this.loading.disable();
24677         }
24678     },
24679
24680     // private
24681     onClick : function(which){
24682         
24683         var ds = this.ds;
24684         if (!ds) {
24685             return;
24686         }
24687         
24688         switch(which){
24689             case "first":
24690                 ds.load({params:{start: 0, limit: this.pageSize}});
24691             break;
24692             case "prev":
24693                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24694             break;
24695             case "next":
24696                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24697             break;
24698             case "last":
24699                 var total = ds.getTotalCount();
24700                 var extra = total % this.pageSize;
24701                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24702                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24703             break;
24704             case "refresh":
24705                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24706             break;
24707         }
24708     },
24709
24710     /**
24711      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24712      * @param {Roo.data.Store} store The data store to unbind
24713      */
24714     unbind : function(ds){
24715         ds.un("beforeload", this.beforeLoad, this);
24716         ds.un("load", this.onLoad, this);
24717         ds.un("loadexception", this.onLoadError, this);
24718         ds.un("remove", this.updateInfo, this);
24719         ds.un("add", this.updateInfo, this);
24720         this.ds = undefined;
24721     },
24722
24723     /**
24724      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24725      * @param {Roo.data.Store} store The data store to bind
24726      */
24727     bind : function(ds){
24728         ds.on("beforeload", this.beforeLoad, this);
24729         ds.on("load", this.onLoad, this);
24730         ds.on("loadexception", this.onLoadError, this);
24731         ds.on("remove", this.updateInfo, this);
24732         ds.on("add", this.updateInfo, this);
24733         this.ds = ds;
24734     }
24735 });/*
24736  * - LGPL
24737  *
24738  * element
24739  * 
24740  */
24741
24742 /**
24743  * @class Roo.bootstrap.MessageBar
24744  * @extends Roo.bootstrap.Component
24745  * Bootstrap MessageBar class
24746  * @cfg {String} html contents of the MessageBar
24747  * @cfg {String} weight (info | success | warning | danger) default info
24748  * @cfg {String} beforeClass insert the bar before the given class
24749  * @cfg {Boolean} closable (true | false) default false
24750  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24751  * 
24752  * @constructor
24753  * Create a new Element
24754  * @param {Object} config The config object
24755  */
24756
24757 Roo.bootstrap.MessageBar = function(config){
24758     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24759 };
24760
24761 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24762     
24763     html: '',
24764     weight: 'info',
24765     closable: false,
24766     fixed: false,
24767     beforeClass: 'bootstrap-sticky-wrap',
24768     
24769     getAutoCreate : function(){
24770         
24771         var cfg = {
24772             tag: 'div',
24773             cls: 'alert alert-dismissable alert-' + this.weight,
24774             cn: [
24775                 {
24776                     tag: 'span',
24777                     cls: 'message',
24778                     html: this.html || ''
24779                 }
24780             ]
24781         };
24782         
24783         if(this.fixed){
24784             cfg.cls += ' alert-messages-fixed';
24785         }
24786         
24787         if(this.closable){
24788             cfg.cn.push({
24789                 tag: 'button',
24790                 cls: 'close',
24791                 html: 'x'
24792             });
24793         }
24794         
24795         return cfg;
24796     },
24797     
24798     onRender : function(ct, position)
24799     {
24800         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24801         
24802         if(!this.el){
24803             var cfg = Roo.apply({},  this.getAutoCreate());
24804             cfg.id = Roo.id();
24805             
24806             if (this.cls) {
24807                 cfg.cls += ' ' + this.cls;
24808             }
24809             if (this.style) {
24810                 cfg.style = this.style;
24811             }
24812             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24813             
24814             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24815         }
24816         
24817         this.el.select('>button.close').on('click', this.hide, this);
24818         
24819     },
24820     
24821     show : function()
24822     {
24823         if (!this.rendered) {
24824             this.render();
24825         }
24826         
24827         this.el.show();
24828         
24829         this.fireEvent('show', this);
24830         
24831     },
24832     
24833     hide : function()
24834     {
24835         if (!this.rendered) {
24836             this.render();
24837         }
24838         
24839         this.el.hide();
24840         
24841         this.fireEvent('hide', this);
24842     },
24843     
24844     update : function()
24845     {
24846 //        var e = this.el.dom.firstChild;
24847 //        
24848 //        if(this.closable){
24849 //            e = e.nextSibling;
24850 //        }
24851 //        
24852 //        e.data = this.html || '';
24853
24854         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24855     }
24856    
24857 });
24858
24859  
24860
24861      /*
24862  * - LGPL
24863  *
24864  * Graph
24865  * 
24866  */
24867
24868
24869 /**
24870  * @class Roo.bootstrap.Graph
24871  * @extends Roo.bootstrap.Component
24872  * Bootstrap Graph class
24873 > Prameters
24874  -sm {number} sm 4
24875  -md {number} md 5
24876  @cfg {String} graphtype  bar | vbar | pie
24877  @cfg {number} g_x coodinator | centre x (pie)
24878  @cfg {number} g_y coodinator | centre y (pie)
24879  @cfg {number} g_r radius (pie)
24880  @cfg {number} g_height height of the chart (respected by all elements in the set)
24881  @cfg {number} g_width width of the chart (respected by all elements in the set)
24882  @cfg {Object} title The title of the chart
24883     
24884  -{Array}  values
24885  -opts (object) options for the chart 
24886      o {
24887      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24888      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24889      o vgutter (number)
24890      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.
24891      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24892      o to
24893      o stretch (boolean)
24894      o }
24895  -opts (object) options for the pie
24896      o{
24897      o cut
24898      o startAngle (number)
24899      o endAngle (number)
24900      } 
24901  *
24902  * @constructor
24903  * Create a new Input
24904  * @param {Object} config The config object
24905  */
24906
24907 Roo.bootstrap.Graph = function(config){
24908     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24909     
24910     this.addEvents({
24911         // img events
24912         /**
24913          * @event click
24914          * The img click event for the img.
24915          * @param {Roo.EventObject} e
24916          */
24917         "click" : true
24918     });
24919 };
24920
24921 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24922     
24923     sm: 4,
24924     md: 5,
24925     graphtype: 'bar',
24926     g_height: 250,
24927     g_width: 400,
24928     g_x: 50,
24929     g_y: 50,
24930     g_r: 30,
24931     opts:{
24932         //g_colors: this.colors,
24933         g_type: 'soft',
24934         g_gutter: '20%'
24935
24936     },
24937     title : false,
24938
24939     getAutoCreate : function(){
24940         
24941         var cfg = {
24942             tag: 'div',
24943             html : null
24944         };
24945         
24946         
24947         return  cfg;
24948     },
24949
24950     onRender : function(ct,position){
24951         
24952         
24953         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24954         
24955         if (typeof(Raphael) == 'undefined') {
24956             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24957             return;
24958         }
24959         
24960         this.raphael = Raphael(this.el.dom);
24961         
24962                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24963                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24964                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24965                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24966                 /*
24967                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24968                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24969                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24970                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24971                 
24972                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24973                 r.barchart(330, 10, 300, 220, data1);
24974                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24975                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24976                 */
24977                 
24978                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24979                 // r.barchart(30, 30, 560, 250,  xdata, {
24980                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24981                 //     axis : "0 0 1 1",
24982                 //     axisxlabels :  xdata
24983                 //     //yvalues : cols,
24984                    
24985                 // });
24986 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24987 //        
24988 //        this.load(null,xdata,{
24989 //                axis : "0 0 1 1",
24990 //                axisxlabels :  xdata
24991 //                });
24992
24993     },
24994
24995     load : function(graphtype,xdata,opts)
24996     {
24997         this.raphael.clear();
24998         if(!graphtype) {
24999             graphtype = this.graphtype;
25000         }
25001         if(!opts){
25002             opts = this.opts;
25003         }
25004         var r = this.raphael,
25005             fin = function () {
25006                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25007             },
25008             fout = function () {
25009                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25010             },
25011             pfin = function() {
25012                 this.sector.stop();
25013                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25014
25015                 if (this.label) {
25016                     this.label[0].stop();
25017                     this.label[0].attr({ r: 7.5 });
25018                     this.label[1].attr({ "font-weight": 800 });
25019                 }
25020             },
25021             pfout = function() {
25022                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25023
25024                 if (this.label) {
25025                     this.label[0].animate({ r: 5 }, 500, "bounce");
25026                     this.label[1].attr({ "font-weight": 400 });
25027                 }
25028             };
25029
25030         switch(graphtype){
25031             case 'bar':
25032                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25033                 break;
25034             case 'hbar':
25035                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25036                 break;
25037             case 'pie':
25038 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25039 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25040 //            
25041                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25042                 
25043                 break;
25044
25045         }
25046         
25047         if(this.title){
25048             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25049         }
25050         
25051     },
25052     
25053     setTitle: function(o)
25054     {
25055         this.title = o;
25056     },
25057     
25058     initEvents: function() {
25059         
25060         if(!this.href){
25061             this.el.on('click', this.onClick, this);
25062         }
25063     },
25064     
25065     onClick : function(e)
25066     {
25067         Roo.log('img onclick');
25068         this.fireEvent('click', this, e);
25069     }
25070    
25071 });
25072
25073  
25074 /*
25075  * - LGPL
25076  *
25077  * numberBox
25078  * 
25079  */
25080 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25081
25082 /**
25083  * @class Roo.bootstrap.dash.NumberBox
25084  * @extends Roo.bootstrap.Component
25085  * Bootstrap NumberBox class
25086  * @cfg {String} headline Box headline
25087  * @cfg {String} content Box content
25088  * @cfg {String} icon Box icon
25089  * @cfg {String} footer Footer text
25090  * @cfg {String} fhref Footer href
25091  * 
25092  * @constructor
25093  * Create a new NumberBox
25094  * @param {Object} config The config object
25095  */
25096
25097
25098 Roo.bootstrap.dash.NumberBox = function(config){
25099     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25100     
25101 };
25102
25103 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25104     
25105     headline : '',
25106     content : '',
25107     icon : '',
25108     footer : '',
25109     fhref : '',
25110     ficon : '',
25111     
25112     getAutoCreate : function(){
25113         
25114         var cfg = {
25115             tag : 'div',
25116             cls : 'small-box ',
25117             cn : [
25118                 {
25119                     tag : 'div',
25120                     cls : 'inner',
25121                     cn :[
25122                         {
25123                             tag : 'h3',
25124                             cls : 'roo-headline',
25125                             html : this.headline
25126                         },
25127                         {
25128                             tag : 'p',
25129                             cls : 'roo-content',
25130                             html : this.content
25131                         }
25132                     ]
25133                 }
25134             ]
25135         };
25136         
25137         if(this.icon){
25138             cfg.cn.push({
25139                 tag : 'div',
25140                 cls : 'icon',
25141                 cn :[
25142                     {
25143                         tag : 'i',
25144                         cls : 'ion ' + this.icon
25145                     }
25146                 ]
25147             });
25148         }
25149         
25150         if(this.footer){
25151             var footer = {
25152                 tag : 'a',
25153                 cls : 'small-box-footer',
25154                 href : this.fhref || '#',
25155                 html : this.footer
25156             };
25157             
25158             cfg.cn.push(footer);
25159             
25160         }
25161         
25162         return  cfg;
25163     },
25164
25165     onRender : function(ct,position){
25166         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25167
25168
25169        
25170                 
25171     },
25172
25173     setHeadline: function (value)
25174     {
25175         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25176     },
25177     
25178     setFooter: function (value, href)
25179     {
25180         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25181         
25182         if(href){
25183             this.el.select('a.small-box-footer',true).first().attr('href', href);
25184         }
25185         
25186     },
25187
25188     setContent: function (value)
25189     {
25190         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25191     },
25192
25193     initEvents: function() 
25194     {   
25195         
25196     }
25197     
25198 });
25199
25200  
25201 /*
25202  * - LGPL
25203  *
25204  * TabBox
25205  * 
25206  */
25207 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25208
25209 /**
25210  * @class Roo.bootstrap.dash.TabBox
25211  * @extends Roo.bootstrap.Component
25212  * Bootstrap TabBox class
25213  * @cfg {String} title Title of the TabBox
25214  * @cfg {String} icon Icon of the TabBox
25215  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25216  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25217  * 
25218  * @constructor
25219  * Create a new TabBox
25220  * @param {Object} config The config object
25221  */
25222
25223
25224 Roo.bootstrap.dash.TabBox = function(config){
25225     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25226     this.addEvents({
25227         // raw events
25228         /**
25229          * @event addpane
25230          * When a pane is added
25231          * @param {Roo.bootstrap.dash.TabPane} pane
25232          */
25233         "addpane" : true,
25234         /**
25235          * @event activatepane
25236          * When a pane is activated
25237          * @param {Roo.bootstrap.dash.TabPane} pane
25238          */
25239         "activatepane" : true
25240         
25241          
25242     });
25243     
25244     this.panes = [];
25245 };
25246
25247 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25248
25249     title : '',
25250     icon : false,
25251     showtabs : true,
25252     tabScrollable : false,
25253     
25254     getChildContainer : function()
25255     {
25256         return this.el.select('.tab-content', true).first();
25257     },
25258     
25259     getAutoCreate : function(){
25260         
25261         var header = {
25262             tag: 'li',
25263             cls: 'pull-left header',
25264             html: this.title,
25265             cn : []
25266         };
25267         
25268         if(this.icon){
25269             header.cn.push({
25270                 tag: 'i',
25271                 cls: 'fa ' + this.icon
25272             });
25273         }
25274         
25275         var h = {
25276             tag: 'ul',
25277             cls: 'nav nav-tabs pull-right',
25278             cn: [
25279                 header
25280             ]
25281         };
25282         
25283         if(this.tabScrollable){
25284             h = {
25285                 tag: 'div',
25286                 cls: 'tab-header',
25287                 cn: [
25288                     {
25289                         tag: 'ul',
25290                         cls: 'nav nav-tabs pull-right',
25291                         cn: [
25292                             header
25293                         ]
25294                     }
25295                 ]
25296             };
25297         }
25298         
25299         var cfg = {
25300             tag: 'div',
25301             cls: 'nav-tabs-custom',
25302             cn: [
25303                 h,
25304                 {
25305                     tag: 'div',
25306                     cls: 'tab-content no-padding',
25307                     cn: []
25308                 }
25309             ]
25310         };
25311
25312         return  cfg;
25313     },
25314     initEvents : function()
25315     {
25316         //Roo.log('add add pane handler');
25317         this.on('addpane', this.onAddPane, this);
25318     },
25319      /**
25320      * Updates the box title
25321      * @param {String} html to set the title to.
25322      */
25323     setTitle : function(value)
25324     {
25325         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25326     },
25327     onAddPane : function(pane)
25328     {
25329         this.panes.push(pane);
25330         //Roo.log('addpane');
25331         //Roo.log(pane);
25332         // tabs are rendere left to right..
25333         if(!this.showtabs){
25334             return;
25335         }
25336         
25337         var ctr = this.el.select('.nav-tabs', true).first();
25338          
25339          
25340         var existing = ctr.select('.nav-tab',true);
25341         var qty = existing.getCount();;
25342         
25343         
25344         var tab = ctr.createChild({
25345             tag : 'li',
25346             cls : 'nav-tab' + (qty ? '' : ' active'),
25347             cn : [
25348                 {
25349                     tag : 'a',
25350                     href:'#',
25351                     html : pane.title
25352                 }
25353             ]
25354         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25355         pane.tab = tab;
25356         
25357         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25358         if (!qty) {
25359             pane.el.addClass('active');
25360         }
25361         
25362                 
25363     },
25364     onTabClick : function(ev,un,ob,pane)
25365     {
25366         //Roo.log('tab - prev default');
25367         ev.preventDefault();
25368         
25369         
25370         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25371         pane.tab.addClass('active');
25372         //Roo.log(pane.title);
25373         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25374         // technically we should have a deactivate event.. but maybe add later.
25375         // and it should not de-activate the selected tab...
25376         this.fireEvent('activatepane', pane);
25377         pane.el.addClass('active');
25378         pane.fireEvent('activate');
25379         
25380         
25381     },
25382     
25383     getActivePane : function()
25384     {
25385         var r = false;
25386         Roo.each(this.panes, function(p) {
25387             if(p.el.hasClass('active')){
25388                 r = p;
25389                 return false;
25390             }
25391             
25392             return;
25393         });
25394         
25395         return r;
25396     }
25397     
25398     
25399 });
25400
25401  
25402 /*
25403  * - LGPL
25404  *
25405  * Tab pane
25406  * 
25407  */
25408 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25409 /**
25410  * @class Roo.bootstrap.TabPane
25411  * @extends Roo.bootstrap.Component
25412  * Bootstrap TabPane class
25413  * @cfg {Boolean} active (false | true) Default false
25414  * @cfg {String} title title of panel
25415
25416  * 
25417  * @constructor
25418  * Create a new TabPane
25419  * @param {Object} config The config object
25420  */
25421
25422 Roo.bootstrap.dash.TabPane = function(config){
25423     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25424     
25425     this.addEvents({
25426         // raw events
25427         /**
25428          * @event activate
25429          * When a pane is activated
25430          * @param {Roo.bootstrap.dash.TabPane} pane
25431          */
25432         "activate" : true
25433          
25434     });
25435 };
25436
25437 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25438     
25439     active : false,
25440     title : '',
25441     
25442     // the tabBox that this is attached to.
25443     tab : false,
25444      
25445     getAutoCreate : function() 
25446     {
25447         var cfg = {
25448             tag: 'div',
25449             cls: 'tab-pane'
25450         };
25451         
25452         if(this.active){
25453             cfg.cls += ' active';
25454         }
25455         
25456         return cfg;
25457     },
25458     initEvents  : function()
25459     {
25460         //Roo.log('trigger add pane handler');
25461         this.parent().fireEvent('addpane', this)
25462     },
25463     
25464      /**
25465      * Updates the tab title 
25466      * @param {String} html to set the title to.
25467      */
25468     setTitle: function(str)
25469     {
25470         if (!this.tab) {
25471             return;
25472         }
25473         this.title = str;
25474         this.tab.select('a', true).first().dom.innerHTML = str;
25475         
25476     }
25477     
25478     
25479     
25480 });
25481
25482  
25483
25484
25485  /*
25486  * - LGPL
25487  *
25488  * menu
25489  * 
25490  */
25491 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25492
25493 /**
25494  * @class Roo.bootstrap.menu.Menu
25495  * @extends Roo.bootstrap.Component
25496  * Bootstrap Menu class - container for Menu
25497  * @cfg {String} html Text of the menu
25498  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25499  * @cfg {String} icon Font awesome icon
25500  * @cfg {String} pos Menu align to (top | bottom) default bottom
25501  * 
25502  * 
25503  * @constructor
25504  * Create a new Menu
25505  * @param {Object} config The config object
25506  */
25507
25508
25509 Roo.bootstrap.menu.Menu = function(config){
25510     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25511     
25512     this.addEvents({
25513         /**
25514          * @event beforeshow
25515          * Fires before this menu is displayed
25516          * @param {Roo.bootstrap.menu.Menu} this
25517          */
25518         beforeshow : true,
25519         /**
25520          * @event beforehide
25521          * Fires before this menu is hidden
25522          * @param {Roo.bootstrap.menu.Menu} this
25523          */
25524         beforehide : true,
25525         /**
25526          * @event show
25527          * Fires after this menu is displayed
25528          * @param {Roo.bootstrap.menu.Menu} this
25529          */
25530         show : true,
25531         /**
25532          * @event hide
25533          * Fires after this menu is hidden
25534          * @param {Roo.bootstrap.menu.Menu} this
25535          */
25536         hide : true,
25537         /**
25538          * @event click
25539          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25540          * @param {Roo.bootstrap.menu.Menu} this
25541          * @param {Roo.EventObject} e
25542          */
25543         click : true
25544     });
25545     
25546 };
25547
25548 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25549     
25550     submenu : false,
25551     html : '',
25552     weight : 'default',
25553     icon : false,
25554     pos : 'bottom',
25555     
25556     
25557     getChildContainer : function() {
25558         if(this.isSubMenu){
25559             return this.el;
25560         }
25561         
25562         return this.el.select('ul.dropdown-menu', true).first();  
25563     },
25564     
25565     getAutoCreate : function()
25566     {
25567         var text = [
25568             {
25569                 tag : 'span',
25570                 cls : 'roo-menu-text',
25571                 html : this.html
25572             }
25573         ];
25574         
25575         if(this.icon){
25576             text.unshift({
25577                 tag : 'i',
25578                 cls : 'fa ' + this.icon
25579             })
25580         }
25581         
25582         
25583         var cfg = {
25584             tag : 'div',
25585             cls : 'btn-group',
25586             cn : [
25587                 {
25588                     tag : 'button',
25589                     cls : 'dropdown-button btn btn-' + this.weight,
25590                     cn : text
25591                 },
25592                 {
25593                     tag : 'button',
25594                     cls : 'dropdown-toggle btn btn-' + this.weight,
25595                     cn : [
25596                         {
25597                             tag : 'span',
25598                             cls : 'caret'
25599                         }
25600                     ]
25601                 },
25602                 {
25603                     tag : 'ul',
25604                     cls : 'dropdown-menu'
25605                 }
25606             ]
25607             
25608         };
25609         
25610         if(this.pos == 'top'){
25611             cfg.cls += ' dropup';
25612         }
25613         
25614         if(this.isSubMenu){
25615             cfg = {
25616                 tag : 'ul',
25617                 cls : 'dropdown-menu'
25618             }
25619         }
25620         
25621         return cfg;
25622     },
25623     
25624     onRender : function(ct, position)
25625     {
25626         this.isSubMenu = ct.hasClass('dropdown-submenu');
25627         
25628         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25629     },
25630     
25631     initEvents : function() 
25632     {
25633         if(this.isSubMenu){
25634             return;
25635         }
25636         
25637         this.hidden = true;
25638         
25639         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25640         this.triggerEl.on('click', this.onTriggerPress, this);
25641         
25642         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25643         this.buttonEl.on('click', this.onClick, this);
25644         
25645     },
25646     
25647     list : function()
25648     {
25649         if(this.isSubMenu){
25650             return this.el;
25651         }
25652         
25653         return this.el.select('ul.dropdown-menu', true).first();
25654     },
25655     
25656     onClick : function(e)
25657     {
25658         this.fireEvent("click", this, e);
25659     },
25660     
25661     onTriggerPress  : function(e)
25662     {   
25663         if (this.isVisible()) {
25664             this.hide();
25665         } else {
25666             this.show();
25667         }
25668     },
25669     
25670     isVisible : function(){
25671         return !this.hidden;
25672     },
25673     
25674     show : function()
25675     {
25676         this.fireEvent("beforeshow", this);
25677         
25678         this.hidden = false;
25679         this.el.addClass('open');
25680         
25681         Roo.get(document).on("mouseup", this.onMouseUp, this);
25682         
25683         this.fireEvent("show", this);
25684         
25685         
25686     },
25687     
25688     hide : function()
25689     {
25690         this.fireEvent("beforehide", this);
25691         
25692         this.hidden = true;
25693         this.el.removeClass('open');
25694         
25695         Roo.get(document).un("mouseup", this.onMouseUp);
25696         
25697         this.fireEvent("hide", this);
25698     },
25699     
25700     onMouseUp : function()
25701     {
25702         this.hide();
25703     }
25704     
25705 });
25706
25707  
25708  /*
25709  * - LGPL
25710  *
25711  * menu item
25712  * 
25713  */
25714 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25715
25716 /**
25717  * @class Roo.bootstrap.menu.Item
25718  * @extends Roo.bootstrap.Component
25719  * Bootstrap MenuItem class
25720  * @cfg {Boolean} submenu (true | false) default false
25721  * @cfg {String} html text of the item
25722  * @cfg {String} href the link
25723  * @cfg {Boolean} disable (true | false) default false
25724  * @cfg {Boolean} preventDefault (true | false) default true
25725  * @cfg {String} icon Font awesome icon
25726  * @cfg {String} pos Submenu align to (left | right) default right 
25727  * 
25728  * 
25729  * @constructor
25730  * Create a new Item
25731  * @param {Object} config The config object
25732  */
25733
25734
25735 Roo.bootstrap.menu.Item = function(config){
25736     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25737     this.addEvents({
25738         /**
25739          * @event mouseover
25740          * Fires when the mouse is hovering over this menu
25741          * @param {Roo.bootstrap.menu.Item} this
25742          * @param {Roo.EventObject} e
25743          */
25744         mouseover : true,
25745         /**
25746          * @event mouseout
25747          * Fires when the mouse exits this menu
25748          * @param {Roo.bootstrap.menu.Item} this
25749          * @param {Roo.EventObject} e
25750          */
25751         mouseout : true,
25752         // raw events
25753         /**
25754          * @event click
25755          * The raw click event for the entire grid.
25756          * @param {Roo.EventObject} e
25757          */
25758         click : true
25759     });
25760 };
25761
25762 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25763     
25764     submenu : false,
25765     href : '',
25766     html : '',
25767     preventDefault: true,
25768     disable : false,
25769     icon : false,
25770     pos : 'right',
25771     
25772     getAutoCreate : function()
25773     {
25774         var text = [
25775             {
25776                 tag : 'span',
25777                 cls : 'roo-menu-item-text',
25778                 html : this.html
25779             }
25780         ];
25781         
25782         if(this.icon){
25783             text.unshift({
25784                 tag : 'i',
25785                 cls : 'fa ' + this.icon
25786             })
25787         }
25788         
25789         var cfg = {
25790             tag : 'li',
25791             cn : [
25792                 {
25793                     tag : 'a',
25794                     href : this.href || '#',
25795                     cn : text
25796                 }
25797             ]
25798         };
25799         
25800         if(this.disable){
25801             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25802         }
25803         
25804         if(this.submenu){
25805             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25806             
25807             if(this.pos == 'left'){
25808                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25809             }
25810         }
25811         
25812         return cfg;
25813     },
25814     
25815     initEvents : function() 
25816     {
25817         this.el.on('mouseover', this.onMouseOver, this);
25818         this.el.on('mouseout', this.onMouseOut, this);
25819         
25820         this.el.select('a', true).first().on('click', this.onClick, this);
25821         
25822     },
25823     
25824     onClick : function(e)
25825     {
25826         if(this.preventDefault){
25827             e.preventDefault();
25828         }
25829         
25830         this.fireEvent("click", this, e);
25831     },
25832     
25833     onMouseOver : function(e)
25834     {
25835         if(this.submenu && this.pos == 'left'){
25836             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25837         }
25838         
25839         this.fireEvent("mouseover", this, e);
25840     },
25841     
25842     onMouseOut : function(e)
25843     {
25844         this.fireEvent("mouseout", this, e);
25845     }
25846 });
25847
25848  
25849
25850  /*
25851  * - LGPL
25852  *
25853  * menu separator
25854  * 
25855  */
25856 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25857
25858 /**
25859  * @class Roo.bootstrap.menu.Separator
25860  * @extends Roo.bootstrap.Component
25861  * Bootstrap Separator class
25862  * 
25863  * @constructor
25864  * Create a new Separator
25865  * @param {Object} config The config object
25866  */
25867
25868
25869 Roo.bootstrap.menu.Separator = function(config){
25870     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25871 };
25872
25873 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25874     
25875     getAutoCreate : function(){
25876         var cfg = {
25877             tag : 'li',
25878             cls: 'divider'
25879         };
25880         
25881         return cfg;
25882     }
25883    
25884 });
25885
25886  
25887
25888  /*
25889  * - LGPL
25890  *
25891  * Tooltip
25892  * 
25893  */
25894
25895 /**
25896  * @class Roo.bootstrap.Tooltip
25897  * Bootstrap Tooltip class
25898  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25899  * to determine which dom element triggers the tooltip.
25900  * 
25901  * It needs to add support for additional attributes like tooltip-position
25902  * 
25903  * @constructor
25904  * Create a new Toolti
25905  * @param {Object} config The config object
25906  */
25907
25908 Roo.bootstrap.Tooltip = function(config){
25909     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25910     
25911     this.alignment = Roo.bootstrap.Tooltip.alignment;
25912     
25913     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25914         this.alignment = config.alignment;
25915     }
25916     
25917 };
25918
25919 Roo.apply(Roo.bootstrap.Tooltip, {
25920     /**
25921      * @function init initialize tooltip monitoring.
25922      * @static
25923      */
25924     currentEl : false,
25925     currentTip : false,
25926     currentRegion : false,
25927     
25928     //  init : delay?
25929     
25930     init : function()
25931     {
25932         Roo.get(document).on('mouseover', this.enter ,this);
25933         Roo.get(document).on('mouseout', this.leave, this);
25934          
25935         
25936         this.currentTip = new Roo.bootstrap.Tooltip();
25937     },
25938     
25939     enter : function(ev)
25940     {
25941         var dom = ev.getTarget();
25942         
25943         //Roo.log(['enter',dom]);
25944         var el = Roo.fly(dom);
25945         if (this.currentEl) {
25946             //Roo.log(dom);
25947             //Roo.log(this.currentEl);
25948             //Roo.log(this.currentEl.contains(dom));
25949             if (this.currentEl == el) {
25950                 return;
25951             }
25952             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25953                 return;
25954             }
25955
25956         }
25957         
25958         if (this.currentTip.el) {
25959             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25960         }    
25961         //Roo.log(ev);
25962         
25963         if(!el || el.dom == document){
25964             return;
25965         }
25966         
25967         var bindEl = el;
25968         
25969         // you can not look for children, as if el is the body.. then everythign is the child..
25970         if (!el.attr('tooltip')) { //
25971             if (!el.select("[tooltip]").elements.length) {
25972                 return;
25973             }
25974             // is the mouse over this child...?
25975             bindEl = el.select("[tooltip]").first();
25976             var xy = ev.getXY();
25977             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25978                 //Roo.log("not in region.");
25979                 return;
25980             }
25981             //Roo.log("child element over..");
25982             
25983         }
25984         this.currentEl = bindEl;
25985         this.currentTip.bind(bindEl);
25986         this.currentRegion = Roo.lib.Region.getRegion(dom);
25987         this.currentTip.enter();
25988         
25989     },
25990     leave : function(ev)
25991     {
25992         var dom = ev.getTarget();
25993         //Roo.log(['leave',dom]);
25994         if (!this.currentEl) {
25995             return;
25996         }
25997         
25998         
25999         if (dom != this.currentEl.dom) {
26000             return;
26001         }
26002         var xy = ev.getXY();
26003         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26004             return;
26005         }
26006         // only activate leave if mouse cursor is outside... bounding box..
26007         
26008         
26009         
26010         
26011         if (this.currentTip) {
26012             this.currentTip.leave();
26013         }
26014         //Roo.log('clear currentEl');
26015         this.currentEl = false;
26016         
26017         
26018     },
26019     alignment : {
26020         'left' : ['r-l', [-2,0], 'right'],
26021         'right' : ['l-r', [2,0], 'left'],
26022         'bottom' : ['t-b', [0,2], 'top'],
26023         'top' : [ 'b-t', [0,-2], 'bottom']
26024     }
26025     
26026 });
26027
26028
26029 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26030     
26031     
26032     bindEl : false,
26033     
26034     delay : null, // can be { show : 300 , hide: 500}
26035     
26036     timeout : null,
26037     
26038     hoverState : null, //???
26039     
26040     placement : 'bottom', 
26041     
26042     alignment : false,
26043     
26044     getAutoCreate : function(){
26045     
26046         var cfg = {
26047            cls : 'tooltip',
26048            role : 'tooltip',
26049            cn : [
26050                 {
26051                     cls : 'tooltip-arrow'
26052                 },
26053                 {
26054                     cls : 'tooltip-inner'
26055                 }
26056            ]
26057         };
26058         
26059         return cfg;
26060     },
26061     bind : function(el)
26062     {
26063         this.bindEl = el;
26064     },
26065       
26066     
26067     enter : function () {
26068        
26069         if (this.timeout != null) {
26070             clearTimeout(this.timeout);
26071         }
26072         
26073         this.hoverState = 'in';
26074          //Roo.log("enter - show");
26075         if (!this.delay || !this.delay.show) {
26076             this.show();
26077             return;
26078         }
26079         var _t = this;
26080         this.timeout = setTimeout(function () {
26081             if (_t.hoverState == 'in') {
26082                 _t.show();
26083             }
26084         }, this.delay.show);
26085     },
26086     leave : function()
26087     {
26088         clearTimeout(this.timeout);
26089     
26090         this.hoverState = 'out';
26091          if (!this.delay || !this.delay.hide) {
26092             this.hide();
26093             return;
26094         }
26095        
26096         var _t = this;
26097         this.timeout = setTimeout(function () {
26098             //Roo.log("leave - timeout");
26099             
26100             if (_t.hoverState == 'out') {
26101                 _t.hide();
26102                 Roo.bootstrap.Tooltip.currentEl = false;
26103             }
26104         }, delay);
26105     },
26106     
26107     show : function (msg)
26108     {
26109         if (!this.el) {
26110             this.render(document.body);
26111         }
26112         // set content.
26113         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26114         
26115         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26116         
26117         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26118         
26119         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26120         
26121         var placement = typeof this.placement == 'function' ?
26122             this.placement.call(this, this.el, on_el) :
26123             this.placement;
26124             
26125         var autoToken = /\s?auto?\s?/i;
26126         var autoPlace = autoToken.test(placement);
26127         if (autoPlace) {
26128             placement = placement.replace(autoToken, '') || 'top';
26129         }
26130         
26131         //this.el.detach()
26132         //this.el.setXY([0,0]);
26133         this.el.show();
26134         //this.el.dom.style.display='block';
26135         
26136         //this.el.appendTo(on_el);
26137         
26138         var p = this.getPosition();
26139         var box = this.el.getBox();
26140         
26141         if (autoPlace) {
26142             // fixme..
26143         }
26144         
26145         var align = this.alignment[placement];
26146         
26147         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26148         
26149         if(placement == 'top' || placement == 'bottom'){
26150             if(xy[0] < 0){
26151                 placement = 'right';
26152             }
26153             
26154             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26155                 placement = 'left';
26156             }
26157             
26158             var scroll = Roo.select('body', true).first().getScroll();
26159             
26160             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26161                 placement = 'top';
26162             }
26163             
26164         }
26165         
26166         this.el.alignTo(this.bindEl, align[0],align[1]);
26167         //var arrow = this.el.select('.arrow',true).first();
26168         //arrow.set(align[2], 
26169         
26170         this.el.addClass(placement);
26171         
26172         this.el.addClass('in fade');
26173         
26174         this.hoverState = null;
26175         
26176         if (this.el.hasClass('fade')) {
26177             // fade it?
26178         }
26179         
26180     },
26181     hide : function()
26182     {
26183          
26184         if (!this.el) {
26185             return;
26186         }
26187         //this.el.setXY([0,0]);
26188         this.el.removeClass('in');
26189         //this.el.hide();
26190         
26191     }
26192     
26193 });
26194  
26195
26196  /*
26197  * - LGPL
26198  *
26199  * Location Picker
26200  * 
26201  */
26202
26203 /**
26204  * @class Roo.bootstrap.LocationPicker
26205  * @extends Roo.bootstrap.Component
26206  * Bootstrap LocationPicker class
26207  * @cfg {Number} latitude Position when init default 0
26208  * @cfg {Number} longitude Position when init default 0
26209  * @cfg {Number} zoom default 15
26210  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26211  * @cfg {Boolean} mapTypeControl default false
26212  * @cfg {Boolean} disableDoubleClickZoom default false
26213  * @cfg {Boolean} scrollwheel default true
26214  * @cfg {Boolean} streetViewControl default false
26215  * @cfg {Number} radius default 0
26216  * @cfg {String} locationName
26217  * @cfg {Boolean} draggable default true
26218  * @cfg {Boolean} enableAutocomplete default false
26219  * @cfg {Boolean} enableReverseGeocode default true
26220  * @cfg {String} markerTitle
26221  * 
26222  * @constructor
26223  * Create a new LocationPicker
26224  * @param {Object} config The config object
26225  */
26226
26227
26228 Roo.bootstrap.LocationPicker = function(config){
26229     
26230     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26231     
26232     this.addEvents({
26233         /**
26234          * @event initial
26235          * Fires when the picker initialized.
26236          * @param {Roo.bootstrap.LocationPicker} this
26237          * @param {Google Location} location
26238          */
26239         initial : true,
26240         /**
26241          * @event positionchanged
26242          * Fires when the picker position changed.
26243          * @param {Roo.bootstrap.LocationPicker} this
26244          * @param {Google Location} location
26245          */
26246         positionchanged : true,
26247         /**
26248          * @event resize
26249          * Fires when the map resize.
26250          * @param {Roo.bootstrap.LocationPicker} this
26251          */
26252         resize : true,
26253         /**
26254          * @event show
26255          * Fires when the map show.
26256          * @param {Roo.bootstrap.LocationPicker} this
26257          */
26258         show : true,
26259         /**
26260          * @event hide
26261          * Fires when the map hide.
26262          * @param {Roo.bootstrap.LocationPicker} this
26263          */
26264         hide : true,
26265         /**
26266          * @event mapClick
26267          * Fires when click the map.
26268          * @param {Roo.bootstrap.LocationPicker} this
26269          * @param {Map event} e
26270          */
26271         mapClick : true,
26272         /**
26273          * @event mapRightClick
26274          * Fires when right click the map.
26275          * @param {Roo.bootstrap.LocationPicker} this
26276          * @param {Map event} e
26277          */
26278         mapRightClick : true,
26279         /**
26280          * @event markerClick
26281          * Fires when click the marker.
26282          * @param {Roo.bootstrap.LocationPicker} this
26283          * @param {Map event} e
26284          */
26285         markerClick : true,
26286         /**
26287          * @event markerRightClick
26288          * Fires when right click the marker.
26289          * @param {Roo.bootstrap.LocationPicker} this
26290          * @param {Map event} e
26291          */
26292         markerRightClick : true,
26293         /**
26294          * @event OverlayViewDraw
26295          * Fires when OverlayView Draw
26296          * @param {Roo.bootstrap.LocationPicker} this
26297          */
26298         OverlayViewDraw : true,
26299         /**
26300          * @event OverlayViewOnAdd
26301          * Fires when OverlayView Draw
26302          * @param {Roo.bootstrap.LocationPicker} this
26303          */
26304         OverlayViewOnAdd : true,
26305         /**
26306          * @event OverlayViewOnRemove
26307          * Fires when OverlayView Draw
26308          * @param {Roo.bootstrap.LocationPicker} this
26309          */
26310         OverlayViewOnRemove : true,
26311         /**
26312          * @event OverlayViewShow
26313          * Fires when OverlayView Draw
26314          * @param {Roo.bootstrap.LocationPicker} this
26315          * @param {Pixel} cpx
26316          */
26317         OverlayViewShow : true,
26318         /**
26319          * @event OverlayViewHide
26320          * Fires when OverlayView Draw
26321          * @param {Roo.bootstrap.LocationPicker} this
26322          */
26323         OverlayViewHide : true,
26324         /**
26325          * @event loadexception
26326          * Fires when load google lib failed.
26327          * @param {Roo.bootstrap.LocationPicker} this
26328          */
26329         loadexception : true
26330     });
26331         
26332 };
26333
26334 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26335     
26336     gMapContext: false,
26337     
26338     latitude: 0,
26339     longitude: 0,
26340     zoom: 15,
26341     mapTypeId: false,
26342     mapTypeControl: false,
26343     disableDoubleClickZoom: false,
26344     scrollwheel: true,
26345     streetViewControl: false,
26346     radius: 0,
26347     locationName: '',
26348     draggable: true,
26349     enableAutocomplete: false,
26350     enableReverseGeocode: true,
26351     markerTitle: '',
26352     
26353     getAutoCreate: function()
26354     {
26355
26356         var cfg = {
26357             tag: 'div',
26358             cls: 'roo-location-picker'
26359         };
26360         
26361         return cfg
26362     },
26363     
26364     initEvents: function(ct, position)
26365     {       
26366         if(!this.el.getWidth() || this.isApplied()){
26367             return;
26368         }
26369         
26370         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26371         
26372         this.initial();
26373     },
26374     
26375     initial: function()
26376     {
26377         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26378             this.fireEvent('loadexception', this);
26379             return;
26380         }
26381         
26382         if(!this.mapTypeId){
26383             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26384         }
26385         
26386         this.gMapContext = this.GMapContext();
26387         
26388         this.initOverlayView();
26389         
26390         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26391         
26392         var _this = this;
26393                 
26394         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26395             _this.setPosition(_this.gMapContext.marker.position);
26396         });
26397         
26398         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26399             _this.fireEvent('mapClick', this, event);
26400             
26401         });
26402
26403         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26404             _this.fireEvent('mapRightClick', this, event);
26405             
26406         });
26407         
26408         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26409             _this.fireEvent('markerClick', this, event);
26410             
26411         });
26412
26413         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26414             _this.fireEvent('markerRightClick', this, event);
26415             
26416         });
26417         
26418         this.setPosition(this.gMapContext.location);
26419         
26420         this.fireEvent('initial', this, this.gMapContext.location);
26421     },
26422     
26423     initOverlayView: function()
26424     {
26425         var _this = this;
26426         
26427         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26428             
26429             draw: function()
26430             {
26431                 _this.fireEvent('OverlayViewDraw', _this);
26432             },
26433             
26434             onAdd: function()
26435             {
26436                 _this.fireEvent('OverlayViewOnAdd', _this);
26437             },
26438             
26439             onRemove: function()
26440             {
26441                 _this.fireEvent('OverlayViewOnRemove', _this);
26442             },
26443             
26444             show: function(cpx)
26445             {
26446                 _this.fireEvent('OverlayViewShow', _this, cpx);
26447             },
26448             
26449             hide: function()
26450             {
26451                 _this.fireEvent('OverlayViewHide', _this);
26452             }
26453             
26454         });
26455     },
26456     
26457     fromLatLngToContainerPixel: function(event)
26458     {
26459         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26460     },
26461     
26462     isApplied: function() 
26463     {
26464         return this.getGmapContext() == false ? false : true;
26465     },
26466     
26467     getGmapContext: function() 
26468     {
26469         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26470     },
26471     
26472     GMapContext: function() 
26473     {
26474         var position = new google.maps.LatLng(this.latitude, this.longitude);
26475         
26476         var _map = new google.maps.Map(this.el.dom, {
26477             center: position,
26478             zoom: this.zoom,
26479             mapTypeId: this.mapTypeId,
26480             mapTypeControl: this.mapTypeControl,
26481             disableDoubleClickZoom: this.disableDoubleClickZoom,
26482             scrollwheel: this.scrollwheel,
26483             streetViewControl: this.streetViewControl,
26484             locationName: this.locationName,
26485             draggable: this.draggable,
26486             enableAutocomplete: this.enableAutocomplete,
26487             enableReverseGeocode: this.enableReverseGeocode
26488         });
26489         
26490         var _marker = new google.maps.Marker({
26491             position: position,
26492             map: _map,
26493             title: this.markerTitle,
26494             draggable: this.draggable
26495         });
26496         
26497         return {
26498             map: _map,
26499             marker: _marker,
26500             circle: null,
26501             location: position,
26502             radius: this.radius,
26503             locationName: this.locationName,
26504             addressComponents: {
26505                 formatted_address: null,
26506                 addressLine1: null,
26507                 addressLine2: null,
26508                 streetName: null,
26509                 streetNumber: null,
26510                 city: null,
26511                 district: null,
26512                 state: null,
26513                 stateOrProvince: null
26514             },
26515             settings: this,
26516             domContainer: this.el.dom,
26517             geodecoder: new google.maps.Geocoder()
26518         };
26519     },
26520     
26521     drawCircle: function(center, radius, options) 
26522     {
26523         if (this.gMapContext.circle != null) {
26524             this.gMapContext.circle.setMap(null);
26525         }
26526         if (radius > 0) {
26527             radius *= 1;
26528             options = Roo.apply({}, options, {
26529                 strokeColor: "#0000FF",
26530                 strokeOpacity: .35,
26531                 strokeWeight: 2,
26532                 fillColor: "#0000FF",
26533                 fillOpacity: .2
26534             });
26535             
26536             options.map = this.gMapContext.map;
26537             options.radius = radius;
26538             options.center = center;
26539             this.gMapContext.circle = new google.maps.Circle(options);
26540             return this.gMapContext.circle;
26541         }
26542         
26543         return null;
26544     },
26545     
26546     setPosition: function(location) 
26547     {
26548         this.gMapContext.location = location;
26549         this.gMapContext.marker.setPosition(location);
26550         this.gMapContext.map.panTo(location);
26551         this.drawCircle(location, this.gMapContext.radius, {});
26552         
26553         var _this = this;
26554         
26555         if (this.gMapContext.settings.enableReverseGeocode) {
26556             this.gMapContext.geodecoder.geocode({
26557                 latLng: this.gMapContext.location
26558             }, function(results, status) {
26559                 
26560                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26561                     _this.gMapContext.locationName = results[0].formatted_address;
26562                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26563                     
26564                     _this.fireEvent('positionchanged', this, location);
26565                 }
26566             });
26567             
26568             return;
26569         }
26570         
26571         this.fireEvent('positionchanged', this, location);
26572     },
26573     
26574     resize: function()
26575     {
26576         google.maps.event.trigger(this.gMapContext.map, "resize");
26577         
26578         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26579         
26580         this.fireEvent('resize', this);
26581     },
26582     
26583     setPositionByLatLng: function(latitude, longitude)
26584     {
26585         this.setPosition(new google.maps.LatLng(latitude, longitude));
26586     },
26587     
26588     getCurrentPosition: function() 
26589     {
26590         return {
26591             latitude: this.gMapContext.location.lat(),
26592             longitude: this.gMapContext.location.lng()
26593         };
26594     },
26595     
26596     getAddressName: function() 
26597     {
26598         return this.gMapContext.locationName;
26599     },
26600     
26601     getAddressComponents: function() 
26602     {
26603         return this.gMapContext.addressComponents;
26604     },
26605     
26606     address_component_from_google_geocode: function(address_components) 
26607     {
26608         var result = {};
26609         
26610         for (var i = 0; i < address_components.length; i++) {
26611             var component = address_components[i];
26612             if (component.types.indexOf("postal_code") >= 0) {
26613                 result.postalCode = component.short_name;
26614             } else if (component.types.indexOf("street_number") >= 0) {
26615                 result.streetNumber = component.short_name;
26616             } else if (component.types.indexOf("route") >= 0) {
26617                 result.streetName = component.short_name;
26618             } else if (component.types.indexOf("neighborhood") >= 0) {
26619                 result.city = component.short_name;
26620             } else if (component.types.indexOf("locality") >= 0) {
26621                 result.city = component.short_name;
26622             } else if (component.types.indexOf("sublocality") >= 0) {
26623                 result.district = component.short_name;
26624             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26625                 result.stateOrProvince = component.short_name;
26626             } else if (component.types.indexOf("country") >= 0) {
26627                 result.country = component.short_name;
26628             }
26629         }
26630         
26631         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26632         result.addressLine2 = "";
26633         return result;
26634     },
26635     
26636     setZoomLevel: function(zoom)
26637     {
26638         this.gMapContext.map.setZoom(zoom);
26639     },
26640     
26641     show: function()
26642     {
26643         if(!this.el){
26644             return;
26645         }
26646         
26647         this.el.show();
26648         
26649         this.resize();
26650         
26651         this.fireEvent('show', this);
26652     },
26653     
26654     hide: function()
26655     {
26656         if(!this.el){
26657             return;
26658         }
26659         
26660         this.el.hide();
26661         
26662         this.fireEvent('hide', this);
26663     }
26664     
26665 });
26666
26667 Roo.apply(Roo.bootstrap.LocationPicker, {
26668     
26669     OverlayView : function(map, options)
26670     {
26671         options = options || {};
26672         
26673         this.setMap(map);
26674     }
26675     
26676     
26677 });/*
26678  * - LGPL
26679  *
26680  * Alert
26681  * 
26682  */
26683
26684 /**
26685  * @class Roo.bootstrap.Alert
26686  * @extends Roo.bootstrap.Component
26687  * Bootstrap Alert class
26688  * @cfg {String} title The title of alert
26689  * @cfg {String} html The content of alert
26690  * @cfg {String} weight (  success | info | warning | danger )
26691  * @cfg {String} faicon font-awesomeicon
26692  * 
26693  * @constructor
26694  * Create a new alert
26695  * @param {Object} config The config object
26696  */
26697
26698
26699 Roo.bootstrap.Alert = function(config){
26700     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26701     
26702 };
26703
26704 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26705     
26706     title: '',
26707     html: '',
26708     weight: false,
26709     faicon: false,
26710     
26711     getAutoCreate : function()
26712     {
26713         
26714         var cfg = {
26715             tag : 'div',
26716             cls : 'alert',
26717             cn : [
26718                 {
26719                     tag : 'i',
26720                     cls : 'roo-alert-icon'
26721                     
26722                 },
26723                 {
26724                     tag : 'b',
26725                     cls : 'roo-alert-title',
26726                     html : this.title
26727                 },
26728                 {
26729                     tag : 'span',
26730                     cls : 'roo-alert-text',
26731                     html : this.html
26732                 }
26733             ]
26734         };
26735         
26736         if(this.faicon){
26737             cfg.cn[0].cls += ' fa ' + this.faicon;
26738         }
26739         
26740         if(this.weight){
26741             cfg.cls += ' alert-' + this.weight;
26742         }
26743         
26744         return cfg;
26745     },
26746     
26747     initEvents: function() 
26748     {
26749         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26750     },
26751     
26752     setTitle : function(str)
26753     {
26754         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26755     },
26756     
26757     setText : function(str)
26758     {
26759         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26760     },
26761     
26762     setWeight : function(weight)
26763     {
26764         if(this.weight){
26765             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26766         }
26767         
26768         this.weight = weight;
26769         
26770         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26771     },
26772     
26773     setIcon : function(icon)
26774     {
26775         if(this.faicon){
26776             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26777         }
26778         
26779         this.faicon = icon;
26780         
26781         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26782     },
26783     
26784     hide: function() 
26785     {
26786         this.el.hide();   
26787     },
26788     
26789     show: function() 
26790     {  
26791         this.el.show();   
26792     }
26793     
26794 });
26795
26796  
26797 /*
26798 * Licence: LGPL
26799 */
26800
26801 /**
26802  * @class Roo.bootstrap.UploadCropbox
26803  * @extends Roo.bootstrap.Component
26804  * Bootstrap UploadCropbox class
26805  * @cfg {String} emptyText show when image has been loaded
26806  * @cfg {String} rotateNotify show when image too small to rotate
26807  * @cfg {Number} errorTimeout default 3000
26808  * @cfg {Number} minWidth default 300
26809  * @cfg {Number} minHeight default 300
26810  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26811  * @cfg {Boolean} isDocument (true|false) default false
26812  * @cfg {String} url action url
26813  * @cfg {String} paramName default 'imageUpload'
26814  * @cfg {String} method default POST
26815  * @cfg {Boolean} loadMask (true|false) default true
26816  * @cfg {Boolean} loadingText default 'Loading...'
26817  * 
26818  * @constructor
26819  * Create a new UploadCropbox
26820  * @param {Object} config The config object
26821  */
26822
26823 Roo.bootstrap.UploadCropbox = function(config){
26824     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26825     
26826     this.addEvents({
26827         /**
26828          * @event beforeselectfile
26829          * Fire before select file
26830          * @param {Roo.bootstrap.UploadCropbox} this
26831          */
26832         "beforeselectfile" : true,
26833         /**
26834          * @event initial
26835          * Fire after initEvent
26836          * @param {Roo.bootstrap.UploadCropbox} this
26837          */
26838         "initial" : true,
26839         /**
26840          * @event crop
26841          * Fire after initEvent
26842          * @param {Roo.bootstrap.UploadCropbox} this
26843          * @param {String} data
26844          */
26845         "crop" : true,
26846         /**
26847          * @event prepare
26848          * Fire when preparing the file data
26849          * @param {Roo.bootstrap.UploadCropbox} this
26850          * @param {Object} file
26851          */
26852         "prepare" : true,
26853         /**
26854          * @event exception
26855          * Fire when get exception
26856          * @param {Roo.bootstrap.UploadCropbox} this
26857          * @param {XMLHttpRequest} xhr
26858          */
26859         "exception" : true,
26860         /**
26861          * @event beforeloadcanvas
26862          * Fire before load the canvas
26863          * @param {Roo.bootstrap.UploadCropbox} this
26864          * @param {String} src
26865          */
26866         "beforeloadcanvas" : true,
26867         /**
26868          * @event trash
26869          * Fire when trash image
26870          * @param {Roo.bootstrap.UploadCropbox} this
26871          */
26872         "trash" : true,
26873         /**
26874          * @event download
26875          * Fire when download the image
26876          * @param {Roo.bootstrap.UploadCropbox} this
26877          */
26878         "download" : true,
26879         /**
26880          * @event footerbuttonclick
26881          * Fire when footerbuttonclick
26882          * @param {Roo.bootstrap.UploadCropbox} this
26883          * @param {String} type
26884          */
26885         "footerbuttonclick" : true,
26886         /**
26887          * @event resize
26888          * Fire when resize
26889          * @param {Roo.bootstrap.UploadCropbox} this
26890          */
26891         "resize" : true,
26892         /**
26893          * @event rotate
26894          * Fire when rotate the image
26895          * @param {Roo.bootstrap.UploadCropbox} this
26896          * @param {String} pos
26897          */
26898         "rotate" : true,
26899         /**
26900          * @event inspect
26901          * Fire when inspect the file
26902          * @param {Roo.bootstrap.UploadCropbox} this
26903          * @param {Object} file
26904          */
26905         "inspect" : true,
26906         /**
26907          * @event upload
26908          * Fire when xhr upload the file
26909          * @param {Roo.bootstrap.UploadCropbox} this
26910          * @param {Object} data
26911          */
26912         "upload" : true,
26913         /**
26914          * @event arrange
26915          * Fire when arrange the file data
26916          * @param {Roo.bootstrap.UploadCropbox} this
26917          * @param {Object} formData
26918          */
26919         "arrange" : true
26920     });
26921     
26922     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26923 };
26924
26925 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26926     
26927     emptyText : 'Click to upload image',
26928     rotateNotify : 'Image is too small to rotate',
26929     errorTimeout : 3000,
26930     scale : 0,
26931     baseScale : 1,
26932     rotate : 0,
26933     dragable : false,
26934     pinching : false,
26935     mouseX : 0,
26936     mouseY : 0,
26937     cropData : false,
26938     minWidth : 300,
26939     minHeight : 300,
26940     file : false,
26941     exif : {},
26942     baseRotate : 1,
26943     cropType : 'image/jpeg',
26944     buttons : false,
26945     canvasLoaded : false,
26946     isDocument : false,
26947     method : 'POST',
26948     paramName : 'imageUpload',
26949     loadMask : true,
26950     loadingText : 'Loading...',
26951     maskEl : false,
26952     
26953     getAutoCreate : function()
26954     {
26955         var cfg = {
26956             tag : 'div',
26957             cls : 'roo-upload-cropbox',
26958             cn : [
26959                 {
26960                     tag : 'input',
26961                     cls : 'roo-upload-cropbox-selector',
26962                     type : 'file'
26963                 },
26964                 {
26965                     tag : 'div',
26966                     cls : 'roo-upload-cropbox-body',
26967                     style : 'cursor:pointer',
26968                     cn : [
26969                         {
26970                             tag : 'div',
26971                             cls : 'roo-upload-cropbox-preview'
26972                         },
26973                         {
26974                             tag : 'div',
26975                             cls : 'roo-upload-cropbox-thumb'
26976                         },
26977                         {
26978                             tag : 'div',
26979                             cls : 'roo-upload-cropbox-empty-notify',
26980                             html : this.emptyText
26981                         },
26982                         {
26983                             tag : 'div',
26984                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26985                             html : this.rotateNotify
26986                         }
26987                     ]
26988                 },
26989                 {
26990                     tag : 'div',
26991                     cls : 'roo-upload-cropbox-footer',
26992                     cn : {
26993                         tag : 'div',
26994                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26995                         cn : []
26996                     }
26997                 }
26998             ]
26999         };
27000         
27001         return cfg;
27002     },
27003     
27004     onRender : function(ct, position)
27005     {
27006         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27007         
27008         if (this.buttons.length) {
27009             
27010             Roo.each(this.buttons, function(bb) {
27011                 
27012                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27013                 
27014                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27015                 
27016             }, this);
27017         }
27018         
27019         if(this.loadMask){
27020             this.maskEl = this.el;
27021         }
27022     },
27023     
27024     initEvents : function()
27025     {
27026         this.urlAPI = (window.createObjectURL && window) || 
27027                                 (window.URL && URL.revokeObjectURL && URL) || 
27028                                 (window.webkitURL && webkitURL);
27029                         
27030         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27031         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27032         
27033         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27034         this.selectorEl.hide();
27035         
27036         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27037         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27038         
27039         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27040         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27041         this.thumbEl.hide();
27042         
27043         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27044         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27045         
27046         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27047         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27048         this.errorEl.hide();
27049         
27050         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27051         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27052         this.footerEl.hide();
27053         
27054         this.setThumbBoxSize();
27055         
27056         this.bind();
27057         
27058         this.resize();
27059         
27060         this.fireEvent('initial', this);
27061     },
27062
27063     bind : function()
27064     {
27065         var _this = this;
27066         
27067         window.addEventListener("resize", function() { _this.resize(); } );
27068         
27069         this.bodyEl.on('click', this.beforeSelectFile, this);
27070         
27071         if(Roo.isTouch){
27072             this.bodyEl.on('touchstart', this.onTouchStart, this);
27073             this.bodyEl.on('touchmove', this.onTouchMove, this);
27074             this.bodyEl.on('touchend', this.onTouchEnd, this);
27075         }
27076         
27077         if(!Roo.isTouch){
27078             this.bodyEl.on('mousedown', this.onMouseDown, this);
27079             this.bodyEl.on('mousemove', this.onMouseMove, this);
27080             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27081             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27082             Roo.get(document).on('mouseup', this.onMouseUp, this);
27083         }
27084         
27085         this.selectorEl.on('change', this.onFileSelected, this);
27086     },
27087     
27088     reset : function()
27089     {    
27090         this.scale = 0;
27091         this.baseScale = 1;
27092         this.rotate = 0;
27093         this.baseRotate = 1;
27094         this.dragable = false;
27095         this.pinching = false;
27096         this.mouseX = 0;
27097         this.mouseY = 0;
27098         this.cropData = false;
27099         this.notifyEl.dom.innerHTML = this.emptyText;
27100         
27101         this.selectorEl.dom.value = '';
27102         
27103     },
27104     
27105     resize : function()
27106     {
27107         if(this.fireEvent('resize', this) != false){
27108             this.setThumbBoxPosition();
27109             this.setCanvasPosition();
27110         }
27111     },
27112     
27113     onFooterButtonClick : function(e, el, o, type)
27114     {
27115         switch (type) {
27116             case 'rotate-left' :
27117                 this.onRotateLeft(e);
27118                 break;
27119             case 'rotate-right' :
27120                 this.onRotateRight(e);
27121                 break;
27122             case 'picture' :
27123                 this.beforeSelectFile(e);
27124                 break;
27125             case 'trash' :
27126                 this.trash(e);
27127                 break;
27128             case 'crop' :
27129                 this.crop(e);
27130                 break;
27131             case 'download' :
27132                 this.download(e);
27133                 break;
27134             default :
27135                 break;
27136         }
27137         
27138         this.fireEvent('footerbuttonclick', this, type);
27139     },
27140     
27141     beforeSelectFile : function(e)
27142     {
27143         e.preventDefault();
27144         
27145         if(this.fireEvent('beforeselectfile', this) != false){
27146             this.selectorEl.dom.click();
27147         }
27148     },
27149     
27150     onFileSelected : function(e)
27151     {
27152         e.preventDefault();
27153         
27154         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27155             return;
27156         }
27157         
27158         var file = this.selectorEl.dom.files[0];
27159         
27160         if(this.fireEvent('inspect', this, file) != false){
27161             this.prepare(file);
27162         }
27163         
27164     },
27165     
27166     trash : function(e)
27167     {
27168         this.fireEvent('trash', this);
27169     },
27170     
27171     download : function(e)
27172     {
27173         this.fireEvent('download', this);
27174     },
27175     
27176     loadCanvas : function(src)
27177     {   
27178         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27179             
27180             this.reset();
27181             
27182             this.imageEl = document.createElement('img');
27183             
27184             var _this = this;
27185             
27186             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27187             
27188             this.imageEl.src = src;
27189         }
27190     },
27191     
27192     onLoadCanvas : function()
27193     {   
27194         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27195         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27196         
27197         this.bodyEl.un('click', this.beforeSelectFile, this);
27198         
27199         this.notifyEl.hide();
27200         this.thumbEl.show();
27201         this.footerEl.show();
27202         
27203         this.baseRotateLevel();
27204         
27205         if(this.isDocument){
27206             this.setThumbBoxSize();
27207         }
27208         
27209         this.setThumbBoxPosition();
27210         
27211         this.baseScaleLevel();
27212         
27213         this.draw();
27214         
27215         this.resize();
27216         
27217         this.canvasLoaded = true;
27218         
27219         if(this.loadMask){
27220             this.maskEl.unmask();
27221         }
27222         
27223     },
27224     
27225     setCanvasPosition : function()
27226     {   
27227         if(!this.canvasEl){
27228             return;
27229         }
27230         
27231         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27232         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27233         
27234         this.previewEl.setLeft(pw);
27235         this.previewEl.setTop(ph);
27236         
27237     },
27238     
27239     onMouseDown : function(e)
27240     {   
27241         e.stopEvent();
27242         
27243         this.dragable = true;
27244         this.pinching = false;
27245         
27246         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27247             this.dragable = false;
27248             return;
27249         }
27250         
27251         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27252         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27253         
27254     },
27255     
27256     onMouseMove : function(e)
27257     {   
27258         e.stopEvent();
27259         
27260         if(!this.canvasLoaded){
27261             return;
27262         }
27263         
27264         if (!this.dragable){
27265             return;
27266         }
27267         
27268         var minX = Math.ceil(this.thumbEl.getLeft(true));
27269         var minY = Math.ceil(this.thumbEl.getTop(true));
27270         
27271         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27272         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27273         
27274         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27275         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27276         
27277         x = x - this.mouseX;
27278         y = y - this.mouseY;
27279         
27280         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27281         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27282         
27283         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27284         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27285         
27286         this.previewEl.setLeft(bgX);
27287         this.previewEl.setTop(bgY);
27288         
27289         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27290         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27291     },
27292     
27293     onMouseUp : function(e)
27294     {   
27295         e.stopEvent();
27296         
27297         this.dragable = false;
27298     },
27299     
27300     onMouseWheel : function(e)
27301     {   
27302         e.stopEvent();
27303         
27304         this.startScale = this.scale;
27305         
27306         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27307         
27308         if(!this.zoomable()){
27309             this.scale = this.startScale;
27310             return;
27311         }
27312         
27313         this.draw();
27314         
27315         return;
27316     },
27317     
27318     zoomable : function()
27319     {
27320         var minScale = this.thumbEl.getWidth() / this.minWidth;
27321         
27322         if(this.minWidth < this.minHeight){
27323             minScale = this.thumbEl.getHeight() / this.minHeight;
27324         }
27325         
27326         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27327         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27328         
27329         if(
27330                 this.isDocument &&
27331                 (this.rotate == 0 || this.rotate == 180) && 
27332                 (
27333                     width > this.imageEl.OriginWidth || 
27334                     height > this.imageEl.OriginHeight ||
27335                     (width < this.minWidth && height < this.minHeight)
27336                 )
27337         ){
27338             return false;
27339         }
27340         
27341         if(
27342                 this.isDocument &&
27343                 (this.rotate == 90 || this.rotate == 270) && 
27344                 (
27345                     width > this.imageEl.OriginWidth || 
27346                     height > this.imageEl.OriginHeight ||
27347                     (width < this.minHeight && height < this.minWidth)
27348                 )
27349         ){
27350             return false;
27351         }
27352         
27353         if(
27354                 !this.isDocument &&
27355                 (this.rotate == 0 || this.rotate == 180) && 
27356                 (
27357                     width < this.minWidth || 
27358                     width > this.imageEl.OriginWidth || 
27359                     height < this.minHeight || 
27360                     height > this.imageEl.OriginHeight
27361                 )
27362         ){
27363             return false;
27364         }
27365         
27366         if(
27367                 !this.isDocument &&
27368                 (this.rotate == 90 || this.rotate == 270) && 
27369                 (
27370                     width < this.minHeight || 
27371                     width > this.imageEl.OriginWidth || 
27372                     height < this.minWidth || 
27373                     height > this.imageEl.OriginHeight
27374                 )
27375         ){
27376             return false;
27377         }
27378         
27379         return true;
27380         
27381     },
27382     
27383     onRotateLeft : function(e)
27384     {   
27385         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27386             
27387             var minScale = this.thumbEl.getWidth() / this.minWidth;
27388             
27389             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27390             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27391             
27392             this.startScale = this.scale;
27393             
27394             while (this.getScaleLevel() < minScale){
27395             
27396                 this.scale = this.scale + 1;
27397                 
27398                 if(!this.zoomable()){
27399                     break;
27400                 }
27401                 
27402                 if(
27403                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27404                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27405                 ){
27406                     continue;
27407                 }
27408                 
27409                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27410
27411                 this.draw();
27412                 
27413                 return;
27414             }
27415             
27416             this.scale = this.startScale;
27417             
27418             this.onRotateFail();
27419             
27420             return false;
27421         }
27422         
27423         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27424
27425         if(this.isDocument){
27426             this.setThumbBoxSize();
27427             this.setThumbBoxPosition();
27428             this.setCanvasPosition();
27429         }
27430         
27431         this.draw();
27432         
27433         this.fireEvent('rotate', this, 'left');
27434         
27435     },
27436     
27437     onRotateRight : function(e)
27438     {
27439         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27440             
27441             var minScale = this.thumbEl.getWidth() / this.minWidth;
27442         
27443             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27444             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27445             
27446             this.startScale = this.scale;
27447             
27448             while (this.getScaleLevel() < minScale){
27449             
27450                 this.scale = this.scale + 1;
27451                 
27452                 if(!this.zoomable()){
27453                     break;
27454                 }
27455                 
27456                 if(
27457                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27458                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27459                 ){
27460                     continue;
27461                 }
27462                 
27463                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27464
27465                 this.draw();
27466                 
27467                 return;
27468             }
27469             
27470             this.scale = this.startScale;
27471             
27472             this.onRotateFail();
27473             
27474             return false;
27475         }
27476         
27477         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27478
27479         if(this.isDocument){
27480             this.setThumbBoxSize();
27481             this.setThumbBoxPosition();
27482             this.setCanvasPosition();
27483         }
27484         
27485         this.draw();
27486         
27487         this.fireEvent('rotate', this, 'right');
27488     },
27489     
27490     onRotateFail : function()
27491     {
27492         this.errorEl.show(true);
27493         
27494         var _this = this;
27495         
27496         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27497     },
27498     
27499     draw : function()
27500     {
27501         this.previewEl.dom.innerHTML = '';
27502         
27503         var canvasEl = document.createElement("canvas");
27504         
27505         var contextEl = canvasEl.getContext("2d");
27506         
27507         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27508         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27509         var center = this.imageEl.OriginWidth / 2;
27510         
27511         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27512             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27513             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27514             center = this.imageEl.OriginHeight / 2;
27515         }
27516         
27517         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27518         
27519         contextEl.translate(center, center);
27520         contextEl.rotate(this.rotate * Math.PI / 180);
27521
27522         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27523         
27524         this.canvasEl = document.createElement("canvas");
27525         
27526         this.contextEl = this.canvasEl.getContext("2d");
27527         
27528         switch (this.rotate) {
27529             case 0 :
27530                 
27531                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27532                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27533                 
27534                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27535                 
27536                 break;
27537             case 90 : 
27538                 
27539                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27540                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27541                 
27542                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27543                     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);
27544                     break;
27545                 }
27546                 
27547                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27548                 
27549                 break;
27550             case 180 :
27551                 
27552                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27553                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27554                 
27555                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27556                     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);
27557                     break;
27558                 }
27559                 
27560                 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);
27561                 
27562                 break;
27563             case 270 :
27564                 
27565                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27566                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27567         
27568                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27569                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27570                     break;
27571                 }
27572                 
27573                 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);
27574                 
27575                 break;
27576             default : 
27577                 break;
27578         }
27579         
27580         this.previewEl.appendChild(this.canvasEl);
27581         
27582         this.setCanvasPosition();
27583     },
27584     
27585     crop : function()
27586     {
27587         if(!this.canvasLoaded){
27588             return;
27589         }
27590         
27591         var imageCanvas = document.createElement("canvas");
27592         
27593         var imageContext = imageCanvas.getContext("2d");
27594         
27595         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27596         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27597         
27598         var center = imageCanvas.width / 2;
27599         
27600         imageContext.translate(center, center);
27601         
27602         imageContext.rotate(this.rotate * Math.PI / 180);
27603         
27604         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27605         
27606         var canvas = document.createElement("canvas");
27607         
27608         var context = canvas.getContext("2d");
27609                 
27610         canvas.width = this.minWidth;
27611         canvas.height = this.minHeight;
27612
27613         switch (this.rotate) {
27614             case 0 :
27615                 
27616                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27617                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27618                 
27619                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27620                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27621                 
27622                 var targetWidth = this.minWidth - 2 * x;
27623                 var targetHeight = this.minHeight - 2 * y;
27624                 
27625                 var scale = 1;
27626                 
27627                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27628                     scale = targetWidth / width;
27629                 }
27630                 
27631                 if(x > 0 && y == 0){
27632                     scale = targetHeight / height;
27633                 }
27634                 
27635                 if(x > 0 && y > 0){
27636                     scale = targetWidth / width;
27637                     
27638                     if(width < height){
27639                         scale = targetHeight / height;
27640                     }
27641                 }
27642                 
27643                 context.scale(scale, scale);
27644                 
27645                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27646                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27647
27648                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27649                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27650
27651                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27652                 
27653                 break;
27654             case 90 : 
27655                 
27656                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27657                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27658                 
27659                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27660                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27661                 
27662                 var targetWidth = this.minWidth - 2 * x;
27663                 var targetHeight = this.minHeight - 2 * y;
27664                 
27665                 var scale = 1;
27666                 
27667                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27668                     scale = targetWidth / width;
27669                 }
27670                 
27671                 if(x > 0 && y == 0){
27672                     scale = targetHeight / height;
27673                 }
27674                 
27675                 if(x > 0 && y > 0){
27676                     scale = targetWidth / width;
27677                     
27678                     if(width < height){
27679                         scale = targetHeight / height;
27680                     }
27681                 }
27682                 
27683                 context.scale(scale, scale);
27684                 
27685                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27686                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27687
27688                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27689                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27690                 
27691                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27692                 
27693                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27694                 
27695                 break;
27696             case 180 :
27697                 
27698                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27699                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27700                 
27701                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27702                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27703                 
27704                 var targetWidth = this.minWidth - 2 * x;
27705                 var targetHeight = this.minHeight - 2 * y;
27706                 
27707                 var scale = 1;
27708                 
27709                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27710                     scale = targetWidth / width;
27711                 }
27712                 
27713                 if(x > 0 && y == 0){
27714                     scale = targetHeight / height;
27715                 }
27716                 
27717                 if(x > 0 && y > 0){
27718                     scale = targetWidth / width;
27719                     
27720                     if(width < height){
27721                         scale = targetHeight / height;
27722                     }
27723                 }
27724                 
27725                 context.scale(scale, scale);
27726                 
27727                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27728                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27729
27730                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27731                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27732
27733                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27734                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27735                 
27736                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27737                 
27738                 break;
27739             case 270 :
27740                 
27741                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27742                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27743                 
27744                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27745                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27746                 
27747                 var targetWidth = this.minWidth - 2 * x;
27748                 var targetHeight = this.minHeight - 2 * y;
27749                 
27750                 var scale = 1;
27751                 
27752                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27753                     scale = targetWidth / width;
27754                 }
27755                 
27756                 if(x > 0 && y == 0){
27757                     scale = targetHeight / height;
27758                 }
27759                 
27760                 if(x > 0 && y > 0){
27761                     scale = targetWidth / width;
27762                     
27763                     if(width < height){
27764                         scale = targetHeight / height;
27765                     }
27766                 }
27767                 
27768                 context.scale(scale, scale);
27769                 
27770                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27771                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27772
27773                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27774                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27775                 
27776                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27777                 
27778                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27779                 
27780                 break;
27781             default : 
27782                 break;
27783         }
27784         
27785         this.cropData = canvas.toDataURL(this.cropType);
27786         
27787         if(this.fireEvent('crop', this, this.cropData) !== false){
27788             this.process(this.file, this.cropData);
27789         }
27790         
27791         return;
27792         
27793     },
27794     
27795     setThumbBoxSize : function()
27796     {
27797         var width, height;
27798         
27799         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27800             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27801             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27802             
27803             this.minWidth = width;
27804             this.minHeight = height;
27805             
27806             if(this.rotate == 90 || this.rotate == 270){
27807                 this.minWidth = height;
27808                 this.minHeight = width;
27809             }
27810         }
27811         
27812         height = 300;
27813         width = Math.ceil(this.minWidth * height / this.minHeight);
27814         
27815         if(this.minWidth > this.minHeight){
27816             width = 300;
27817             height = Math.ceil(this.minHeight * width / this.minWidth);
27818         }
27819         
27820         this.thumbEl.setStyle({
27821             width : width + 'px',
27822             height : height + 'px'
27823         });
27824
27825         return;
27826             
27827     },
27828     
27829     setThumbBoxPosition : function()
27830     {
27831         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27832         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27833         
27834         this.thumbEl.setLeft(x);
27835         this.thumbEl.setTop(y);
27836         
27837     },
27838     
27839     baseRotateLevel : function()
27840     {
27841         this.baseRotate = 1;
27842         
27843         if(
27844                 typeof(this.exif) != 'undefined' &&
27845                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27846                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27847         ){
27848             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27849         }
27850         
27851         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27852         
27853     },
27854     
27855     baseScaleLevel : function()
27856     {
27857         var width, height;
27858         
27859         if(this.isDocument){
27860             
27861             if(this.baseRotate == 6 || this.baseRotate == 8){
27862             
27863                 height = this.thumbEl.getHeight();
27864                 this.baseScale = height / this.imageEl.OriginWidth;
27865
27866                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27867                     width = this.thumbEl.getWidth();
27868                     this.baseScale = width / this.imageEl.OriginHeight;
27869                 }
27870
27871                 return;
27872             }
27873
27874             height = this.thumbEl.getHeight();
27875             this.baseScale = height / this.imageEl.OriginHeight;
27876
27877             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27878                 width = this.thumbEl.getWidth();
27879                 this.baseScale = width / this.imageEl.OriginWidth;
27880             }
27881
27882             return;
27883         }
27884         
27885         if(this.baseRotate == 6 || this.baseRotate == 8){
27886             
27887             width = this.thumbEl.getHeight();
27888             this.baseScale = width / this.imageEl.OriginHeight;
27889             
27890             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27891                 height = this.thumbEl.getWidth();
27892                 this.baseScale = height / this.imageEl.OriginHeight;
27893             }
27894             
27895             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27896                 height = this.thumbEl.getWidth();
27897                 this.baseScale = height / this.imageEl.OriginHeight;
27898                 
27899                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27900                     width = this.thumbEl.getHeight();
27901                     this.baseScale = width / this.imageEl.OriginWidth;
27902                 }
27903             }
27904             
27905             return;
27906         }
27907         
27908         width = this.thumbEl.getWidth();
27909         this.baseScale = width / this.imageEl.OriginWidth;
27910         
27911         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27912             height = this.thumbEl.getHeight();
27913             this.baseScale = height / this.imageEl.OriginHeight;
27914         }
27915         
27916         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27917             
27918             height = this.thumbEl.getHeight();
27919             this.baseScale = height / this.imageEl.OriginHeight;
27920             
27921             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27922                 width = this.thumbEl.getWidth();
27923                 this.baseScale = width / this.imageEl.OriginWidth;
27924             }
27925             
27926         }
27927         
27928         return;
27929     },
27930     
27931     getScaleLevel : function()
27932     {
27933         return this.baseScale * Math.pow(1.1, this.scale);
27934     },
27935     
27936     onTouchStart : function(e)
27937     {
27938         if(!this.canvasLoaded){
27939             this.beforeSelectFile(e);
27940             return;
27941         }
27942         
27943         var touches = e.browserEvent.touches;
27944         
27945         if(!touches){
27946             return;
27947         }
27948         
27949         if(touches.length == 1){
27950             this.onMouseDown(e);
27951             return;
27952         }
27953         
27954         if(touches.length != 2){
27955             return;
27956         }
27957         
27958         var coords = [];
27959         
27960         for(var i = 0, finger; finger = touches[i]; i++){
27961             coords.push(finger.pageX, finger.pageY);
27962         }
27963         
27964         var x = Math.pow(coords[0] - coords[2], 2);
27965         var y = Math.pow(coords[1] - coords[3], 2);
27966         
27967         this.startDistance = Math.sqrt(x + y);
27968         
27969         this.startScale = this.scale;
27970         
27971         this.pinching = true;
27972         this.dragable = false;
27973         
27974     },
27975     
27976     onTouchMove : function(e)
27977     {
27978         if(!this.pinching && !this.dragable){
27979             return;
27980         }
27981         
27982         var touches = e.browserEvent.touches;
27983         
27984         if(!touches){
27985             return;
27986         }
27987         
27988         if(this.dragable){
27989             this.onMouseMove(e);
27990             return;
27991         }
27992         
27993         var coords = [];
27994         
27995         for(var i = 0, finger; finger = touches[i]; i++){
27996             coords.push(finger.pageX, finger.pageY);
27997         }
27998         
27999         var x = Math.pow(coords[0] - coords[2], 2);
28000         var y = Math.pow(coords[1] - coords[3], 2);
28001         
28002         this.endDistance = Math.sqrt(x + y);
28003         
28004         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28005         
28006         if(!this.zoomable()){
28007             this.scale = this.startScale;
28008             return;
28009         }
28010         
28011         this.draw();
28012         
28013     },
28014     
28015     onTouchEnd : function(e)
28016     {
28017         this.pinching = false;
28018         this.dragable = false;
28019         
28020     },
28021     
28022     process : function(file, crop)
28023     {
28024         if(this.loadMask){
28025             this.maskEl.mask(this.loadingText);
28026         }
28027         
28028         this.xhr = new XMLHttpRequest();
28029         
28030         file.xhr = this.xhr;
28031
28032         this.xhr.open(this.method, this.url, true);
28033         
28034         var headers = {
28035             "Accept": "application/json",
28036             "Cache-Control": "no-cache",
28037             "X-Requested-With": "XMLHttpRequest"
28038         };
28039         
28040         for (var headerName in headers) {
28041             var headerValue = headers[headerName];
28042             if (headerValue) {
28043                 this.xhr.setRequestHeader(headerName, headerValue);
28044             }
28045         }
28046         
28047         var _this = this;
28048         
28049         this.xhr.onload = function()
28050         {
28051             _this.xhrOnLoad(_this.xhr);
28052         }
28053         
28054         this.xhr.onerror = function()
28055         {
28056             _this.xhrOnError(_this.xhr);
28057         }
28058         
28059         var formData = new FormData();
28060
28061         formData.append('returnHTML', 'NO');
28062         
28063         if(crop){
28064             formData.append('crop', crop);
28065         }
28066         
28067         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28068             formData.append(this.paramName, file, file.name);
28069         }
28070         
28071         if(typeof(file.filename) != 'undefined'){
28072             formData.append('filename', file.filename);
28073         }
28074         
28075         if(typeof(file.mimetype) != 'undefined'){
28076             formData.append('mimetype', file.mimetype);
28077         }
28078         
28079         if(this.fireEvent('arrange', this, formData) != false){
28080             this.xhr.send(formData);
28081         };
28082     },
28083     
28084     xhrOnLoad : function(xhr)
28085     {
28086         if(this.loadMask){
28087             this.maskEl.unmask();
28088         }
28089         
28090         if (xhr.readyState !== 4) {
28091             this.fireEvent('exception', this, xhr);
28092             return;
28093         }
28094
28095         var response = Roo.decode(xhr.responseText);
28096         
28097         if(!response.success){
28098             this.fireEvent('exception', this, xhr);
28099             return;
28100         }
28101         
28102         var response = Roo.decode(xhr.responseText);
28103         
28104         this.fireEvent('upload', this, response);
28105         
28106     },
28107     
28108     xhrOnError : function()
28109     {
28110         if(this.loadMask){
28111             this.maskEl.unmask();
28112         }
28113         
28114         Roo.log('xhr on error');
28115         
28116         var response = Roo.decode(xhr.responseText);
28117           
28118         Roo.log(response);
28119         
28120     },
28121     
28122     prepare : function(file)
28123     {   
28124         if(this.loadMask){
28125             this.maskEl.mask(this.loadingText);
28126         }
28127         
28128         this.file = false;
28129         this.exif = {};
28130         
28131         if(typeof(file) === 'string'){
28132             this.loadCanvas(file);
28133             return;
28134         }
28135         
28136         if(!file || !this.urlAPI){
28137             return;
28138         }
28139         
28140         this.file = file;
28141         this.cropType = file.type;
28142         
28143         var _this = this;
28144         
28145         if(this.fireEvent('prepare', this, this.file) != false){
28146             
28147             var reader = new FileReader();
28148             
28149             reader.onload = function (e) {
28150                 if (e.target.error) {
28151                     Roo.log(e.target.error);
28152                     return;
28153                 }
28154                 
28155                 var buffer = e.target.result,
28156                     dataView = new DataView(buffer),
28157                     offset = 2,
28158                     maxOffset = dataView.byteLength - 4,
28159                     markerBytes,
28160                     markerLength;
28161                 
28162                 if (dataView.getUint16(0) === 0xffd8) {
28163                     while (offset < maxOffset) {
28164                         markerBytes = dataView.getUint16(offset);
28165                         
28166                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28167                             markerLength = dataView.getUint16(offset + 2) + 2;
28168                             if (offset + markerLength > dataView.byteLength) {
28169                                 Roo.log('Invalid meta data: Invalid segment size.');
28170                                 break;
28171                             }
28172                             
28173                             if(markerBytes == 0xffe1){
28174                                 _this.parseExifData(
28175                                     dataView,
28176                                     offset,
28177                                     markerLength
28178                                 );
28179                             }
28180                             
28181                             offset += markerLength;
28182                             
28183                             continue;
28184                         }
28185                         
28186                         break;
28187                     }
28188                     
28189                 }
28190                 
28191                 var url = _this.urlAPI.createObjectURL(_this.file);
28192                 
28193                 _this.loadCanvas(url);
28194                 
28195                 return;
28196             }
28197             
28198             reader.readAsArrayBuffer(this.file);
28199             
28200         }
28201         
28202     },
28203     
28204     parseExifData : function(dataView, offset, length)
28205     {
28206         var tiffOffset = offset + 10,
28207             littleEndian,
28208             dirOffset;
28209     
28210         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28211             // No Exif data, might be XMP data instead
28212             return;
28213         }
28214         
28215         // Check for the ASCII code for "Exif" (0x45786966):
28216         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28217             // No Exif data, might be XMP data instead
28218             return;
28219         }
28220         if (tiffOffset + 8 > dataView.byteLength) {
28221             Roo.log('Invalid Exif data: Invalid segment size.');
28222             return;
28223         }
28224         // Check for the two null bytes:
28225         if (dataView.getUint16(offset + 8) !== 0x0000) {
28226             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28227             return;
28228         }
28229         // Check the byte alignment:
28230         switch (dataView.getUint16(tiffOffset)) {
28231         case 0x4949:
28232             littleEndian = true;
28233             break;
28234         case 0x4D4D:
28235             littleEndian = false;
28236             break;
28237         default:
28238             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28239             return;
28240         }
28241         // Check for the TIFF tag marker (0x002A):
28242         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28243             Roo.log('Invalid Exif data: Missing TIFF marker.');
28244             return;
28245         }
28246         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28247         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28248         
28249         this.parseExifTags(
28250             dataView,
28251             tiffOffset,
28252             tiffOffset + dirOffset,
28253             littleEndian
28254         );
28255     },
28256     
28257     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28258     {
28259         var tagsNumber,
28260             dirEndOffset,
28261             i;
28262         if (dirOffset + 6 > dataView.byteLength) {
28263             Roo.log('Invalid Exif data: Invalid directory offset.');
28264             return;
28265         }
28266         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28267         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28268         if (dirEndOffset + 4 > dataView.byteLength) {
28269             Roo.log('Invalid Exif data: Invalid directory size.');
28270             return;
28271         }
28272         for (i = 0; i < tagsNumber; i += 1) {
28273             this.parseExifTag(
28274                 dataView,
28275                 tiffOffset,
28276                 dirOffset + 2 + 12 * i, // tag offset
28277                 littleEndian
28278             );
28279         }
28280         // Return the offset to the next directory:
28281         return dataView.getUint32(dirEndOffset, littleEndian);
28282     },
28283     
28284     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28285     {
28286         var tag = dataView.getUint16(offset, littleEndian);
28287         
28288         this.exif[tag] = this.getExifValue(
28289             dataView,
28290             tiffOffset,
28291             offset,
28292             dataView.getUint16(offset + 2, littleEndian), // tag type
28293             dataView.getUint32(offset + 4, littleEndian), // tag length
28294             littleEndian
28295         );
28296     },
28297     
28298     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28299     {
28300         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28301             tagSize,
28302             dataOffset,
28303             values,
28304             i,
28305             str,
28306             c;
28307     
28308         if (!tagType) {
28309             Roo.log('Invalid Exif data: Invalid tag type.');
28310             return;
28311         }
28312         
28313         tagSize = tagType.size * length;
28314         // Determine if the value is contained in the dataOffset bytes,
28315         // or if the value at the dataOffset is a pointer to the actual data:
28316         dataOffset = tagSize > 4 ?
28317                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28318         if (dataOffset + tagSize > dataView.byteLength) {
28319             Roo.log('Invalid Exif data: Invalid data offset.');
28320             return;
28321         }
28322         if (length === 1) {
28323             return tagType.getValue(dataView, dataOffset, littleEndian);
28324         }
28325         values = [];
28326         for (i = 0; i < length; i += 1) {
28327             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28328         }
28329         
28330         if (tagType.ascii) {
28331             str = '';
28332             // Concatenate the chars:
28333             for (i = 0; i < values.length; i += 1) {
28334                 c = values[i];
28335                 // Ignore the terminating NULL byte(s):
28336                 if (c === '\u0000') {
28337                     break;
28338                 }
28339                 str += c;
28340             }
28341             return str;
28342         }
28343         return values;
28344     }
28345     
28346 });
28347
28348 Roo.apply(Roo.bootstrap.UploadCropbox, {
28349     tags : {
28350         'Orientation': 0x0112
28351     },
28352     
28353     Orientation: {
28354             1: 0, //'top-left',
28355 //            2: 'top-right',
28356             3: 180, //'bottom-right',
28357 //            4: 'bottom-left',
28358 //            5: 'left-top',
28359             6: 90, //'right-top',
28360 //            7: 'right-bottom',
28361             8: 270 //'left-bottom'
28362     },
28363     
28364     exifTagTypes : {
28365         // byte, 8-bit unsigned int:
28366         1: {
28367             getValue: function (dataView, dataOffset) {
28368                 return dataView.getUint8(dataOffset);
28369             },
28370             size: 1
28371         },
28372         // ascii, 8-bit byte:
28373         2: {
28374             getValue: function (dataView, dataOffset) {
28375                 return String.fromCharCode(dataView.getUint8(dataOffset));
28376             },
28377             size: 1,
28378             ascii: true
28379         },
28380         // short, 16 bit int:
28381         3: {
28382             getValue: function (dataView, dataOffset, littleEndian) {
28383                 return dataView.getUint16(dataOffset, littleEndian);
28384             },
28385             size: 2
28386         },
28387         // long, 32 bit int:
28388         4: {
28389             getValue: function (dataView, dataOffset, littleEndian) {
28390                 return dataView.getUint32(dataOffset, littleEndian);
28391             },
28392             size: 4
28393         },
28394         // rational = two long values, first is numerator, second is denominator:
28395         5: {
28396             getValue: function (dataView, dataOffset, littleEndian) {
28397                 return dataView.getUint32(dataOffset, littleEndian) /
28398                     dataView.getUint32(dataOffset + 4, littleEndian);
28399             },
28400             size: 8
28401         },
28402         // slong, 32 bit signed int:
28403         9: {
28404             getValue: function (dataView, dataOffset, littleEndian) {
28405                 return dataView.getInt32(dataOffset, littleEndian);
28406             },
28407             size: 4
28408         },
28409         // srational, two slongs, first is numerator, second is denominator:
28410         10: {
28411             getValue: function (dataView, dataOffset, littleEndian) {
28412                 return dataView.getInt32(dataOffset, littleEndian) /
28413                     dataView.getInt32(dataOffset + 4, littleEndian);
28414             },
28415             size: 8
28416         }
28417     },
28418     
28419     footer : {
28420         STANDARD : [
28421             {
28422                 tag : 'div',
28423                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28424                 action : 'rotate-left',
28425                 cn : [
28426                     {
28427                         tag : 'button',
28428                         cls : 'btn btn-default',
28429                         html : '<i class="fa fa-undo"></i>'
28430                     }
28431                 ]
28432             },
28433             {
28434                 tag : 'div',
28435                 cls : 'btn-group roo-upload-cropbox-picture',
28436                 action : 'picture',
28437                 cn : [
28438                     {
28439                         tag : 'button',
28440                         cls : 'btn btn-default',
28441                         html : '<i class="fa fa-picture-o"></i>'
28442                     }
28443                 ]
28444             },
28445             {
28446                 tag : 'div',
28447                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28448                 action : 'rotate-right',
28449                 cn : [
28450                     {
28451                         tag : 'button',
28452                         cls : 'btn btn-default',
28453                         html : '<i class="fa fa-repeat"></i>'
28454                     }
28455                 ]
28456             }
28457         ],
28458         DOCUMENT : [
28459             {
28460                 tag : 'div',
28461                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28462                 action : 'rotate-left',
28463                 cn : [
28464                     {
28465                         tag : 'button',
28466                         cls : 'btn btn-default',
28467                         html : '<i class="fa fa-undo"></i>'
28468                     }
28469                 ]
28470             },
28471             {
28472                 tag : 'div',
28473                 cls : 'btn-group roo-upload-cropbox-download',
28474                 action : 'download',
28475                 cn : [
28476                     {
28477                         tag : 'button',
28478                         cls : 'btn btn-default',
28479                         html : '<i class="fa fa-download"></i>'
28480                     }
28481                 ]
28482             },
28483             {
28484                 tag : 'div',
28485                 cls : 'btn-group roo-upload-cropbox-crop',
28486                 action : 'crop',
28487                 cn : [
28488                     {
28489                         tag : 'button',
28490                         cls : 'btn btn-default',
28491                         html : '<i class="fa fa-crop"></i>'
28492                     }
28493                 ]
28494             },
28495             {
28496                 tag : 'div',
28497                 cls : 'btn-group roo-upload-cropbox-trash',
28498                 action : 'trash',
28499                 cn : [
28500                     {
28501                         tag : 'button',
28502                         cls : 'btn btn-default',
28503                         html : '<i class="fa fa-trash"></i>'
28504                     }
28505                 ]
28506             },
28507             {
28508                 tag : 'div',
28509                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28510                 action : 'rotate-right',
28511                 cn : [
28512                     {
28513                         tag : 'button',
28514                         cls : 'btn btn-default',
28515                         html : '<i class="fa fa-repeat"></i>'
28516                     }
28517                 ]
28518             }
28519         ],
28520         ROTATOR : [
28521             {
28522                 tag : 'div',
28523                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28524                 action : 'rotate-left',
28525                 cn : [
28526                     {
28527                         tag : 'button',
28528                         cls : 'btn btn-default',
28529                         html : '<i class="fa fa-undo"></i>'
28530                     }
28531                 ]
28532             },
28533             {
28534                 tag : 'div',
28535                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28536                 action : 'rotate-right',
28537                 cn : [
28538                     {
28539                         tag : 'button',
28540                         cls : 'btn btn-default',
28541                         html : '<i class="fa fa-repeat"></i>'
28542                     }
28543                 ]
28544             }
28545         ]
28546     }
28547 });
28548
28549 /*
28550 * Licence: LGPL
28551 */
28552
28553 /**
28554  * @class Roo.bootstrap.DocumentManager
28555  * @extends Roo.bootstrap.Component
28556  * Bootstrap DocumentManager class
28557  * @cfg {String} paramName default 'imageUpload'
28558  * @cfg {String} toolTipName default 'filename'
28559  * @cfg {String} method default POST
28560  * @cfg {String} url action url
28561  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28562  * @cfg {Boolean} multiple multiple upload default true
28563  * @cfg {Number} thumbSize default 300
28564  * @cfg {String} fieldLabel
28565  * @cfg {Number} labelWidth default 4
28566  * @cfg {String} labelAlign (left|top) default left
28567  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28568 * @cfg {Number} labellg set the width of label (1-12)
28569  * @cfg {Number} labelmd set the width of label (1-12)
28570  * @cfg {Number} labelsm set the width of label (1-12)
28571  * @cfg {Number} labelxs set the width of label (1-12)
28572  * 
28573  * @constructor
28574  * Create a new DocumentManager
28575  * @param {Object} config The config object
28576  */
28577
28578 Roo.bootstrap.DocumentManager = function(config){
28579     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28580     
28581     this.files = [];
28582     this.delegates = [];
28583     
28584     this.addEvents({
28585         /**
28586          * @event initial
28587          * Fire when initial the DocumentManager
28588          * @param {Roo.bootstrap.DocumentManager} this
28589          */
28590         "initial" : true,
28591         /**
28592          * @event inspect
28593          * inspect selected file
28594          * @param {Roo.bootstrap.DocumentManager} this
28595          * @param {File} file
28596          */
28597         "inspect" : true,
28598         /**
28599          * @event exception
28600          * Fire when xhr load exception
28601          * @param {Roo.bootstrap.DocumentManager} this
28602          * @param {XMLHttpRequest} xhr
28603          */
28604         "exception" : true,
28605         /**
28606          * @event afterupload
28607          * Fire when xhr load exception
28608          * @param {Roo.bootstrap.DocumentManager} this
28609          * @param {XMLHttpRequest} xhr
28610          */
28611         "afterupload" : true,
28612         /**
28613          * @event prepare
28614          * prepare the form data
28615          * @param {Roo.bootstrap.DocumentManager} this
28616          * @param {Object} formData
28617          */
28618         "prepare" : true,
28619         /**
28620          * @event remove
28621          * Fire when remove the file
28622          * @param {Roo.bootstrap.DocumentManager} this
28623          * @param {Object} file
28624          */
28625         "remove" : true,
28626         /**
28627          * @event refresh
28628          * Fire after refresh the file
28629          * @param {Roo.bootstrap.DocumentManager} this
28630          */
28631         "refresh" : true,
28632         /**
28633          * @event click
28634          * Fire after click the image
28635          * @param {Roo.bootstrap.DocumentManager} this
28636          * @param {Object} file
28637          */
28638         "click" : true,
28639         /**
28640          * @event edit
28641          * Fire when upload a image and editable set to true
28642          * @param {Roo.bootstrap.DocumentManager} this
28643          * @param {Object} file
28644          */
28645         "edit" : true,
28646         /**
28647          * @event beforeselectfile
28648          * Fire before select file
28649          * @param {Roo.bootstrap.DocumentManager} this
28650          */
28651         "beforeselectfile" : true,
28652         /**
28653          * @event process
28654          * Fire before process file
28655          * @param {Roo.bootstrap.DocumentManager} this
28656          * @param {Object} file
28657          */
28658         "process" : true,
28659         /**
28660          * @event previewrendered
28661          * Fire when preview rendered
28662          * @param {Roo.bootstrap.DocumentManager} this
28663          * @param {Object} file
28664          */
28665         "previewrendered" : true
28666         
28667     });
28668 };
28669
28670 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28671     
28672     boxes : 0,
28673     inputName : '',
28674     thumbSize : 300,
28675     multiple : true,
28676     files : false,
28677     method : 'POST',
28678     url : '',
28679     paramName : 'imageUpload',
28680     toolTipName : 'filename',
28681     fieldLabel : '',
28682     labelWidth : 4,
28683     labelAlign : 'left',
28684     editable : true,
28685     delegates : false,
28686     xhr : false, 
28687     
28688     labellg : 0,
28689     labelmd : 0,
28690     labelsm : 0,
28691     labelxs : 0,
28692     
28693     getAutoCreate : function()
28694     {   
28695         var managerWidget = {
28696             tag : 'div',
28697             cls : 'roo-document-manager',
28698             cn : [
28699                 {
28700                     tag : 'input',
28701                     cls : 'roo-document-manager-selector',
28702                     type : 'file'
28703                 },
28704                 {
28705                     tag : 'div',
28706                     cls : 'roo-document-manager-uploader',
28707                     cn : [
28708                         {
28709                             tag : 'div',
28710                             cls : 'roo-document-manager-upload-btn',
28711                             html : '<i class="fa fa-plus"></i>'
28712                         }
28713                     ]
28714                     
28715                 }
28716             ]
28717         };
28718         
28719         var content = [
28720             {
28721                 tag : 'div',
28722                 cls : 'column col-md-12',
28723                 cn : managerWidget
28724             }
28725         ];
28726         
28727         if(this.fieldLabel.length){
28728             
28729             content = [
28730                 {
28731                     tag : 'div',
28732                     cls : 'column col-md-12',
28733                     html : this.fieldLabel
28734                 },
28735                 {
28736                     tag : 'div',
28737                     cls : 'column col-md-12',
28738                     cn : managerWidget
28739                 }
28740             ];
28741
28742             if(this.labelAlign == 'left'){
28743                 content = [
28744                     {
28745                         tag : 'div',
28746                         cls : 'column',
28747                         html : this.fieldLabel
28748                     },
28749                     {
28750                         tag : 'div',
28751                         cls : 'column',
28752                         cn : managerWidget
28753                     }
28754                 ];
28755                 
28756                 if(this.labelWidth > 12){
28757                     content[0].style = "width: " + this.labelWidth + 'px';
28758                 }
28759
28760                 if(this.labelWidth < 13 && this.labelmd == 0){
28761                     this.labelmd = this.labelWidth;
28762                 }
28763
28764                 if(this.labellg > 0){
28765                     content[0].cls += ' col-lg-' + this.labellg;
28766                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28767                 }
28768
28769                 if(this.labelmd > 0){
28770                     content[0].cls += ' col-md-' + this.labelmd;
28771                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28772                 }
28773
28774                 if(this.labelsm > 0){
28775                     content[0].cls += ' col-sm-' + this.labelsm;
28776                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28777                 }
28778
28779                 if(this.labelxs > 0){
28780                     content[0].cls += ' col-xs-' + this.labelxs;
28781                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28782                 }
28783                 
28784             }
28785         }
28786         
28787         var cfg = {
28788             tag : 'div',
28789             cls : 'row clearfix',
28790             cn : content
28791         };
28792         
28793         return cfg;
28794         
28795     },
28796     
28797     initEvents : function()
28798     {
28799         this.managerEl = this.el.select('.roo-document-manager', true).first();
28800         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28801         
28802         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28803         this.selectorEl.hide();
28804         
28805         if(this.multiple){
28806             this.selectorEl.attr('multiple', 'multiple');
28807         }
28808         
28809         this.selectorEl.on('change', this.onFileSelected, this);
28810         
28811         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28812         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28813         
28814         this.uploader.on('click', this.onUploaderClick, this);
28815         
28816         this.renderProgressDialog();
28817         
28818         var _this = this;
28819         
28820         window.addEventListener("resize", function() { _this.refresh(); } );
28821         
28822         this.fireEvent('initial', this);
28823     },
28824     
28825     renderProgressDialog : function()
28826     {
28827         var _this = this;
28828         
28829         this.progressDialog = new Roo.bootstrap.Modal({
28830             cls : 'roo-document-manager-progress-dialog',
28831             allow_close : false,
28832             title : '',
28833             buttons : [
28834                 {
28835                     name  :'cancel',
28836                     weight : 'danger',
28837                     html : 'Cancel'
28838                 }
28839             ], 
28840             listeners : { 
28841                 btnclick : function() {
28842                     _this.uploadCancel();
28843                     this.hide();
28844                 }
28845             }
28846         });
28847          
28848         this.progressDialog.render(Roo.get(document.body));
28849          
28850         this.progress = new Roo.bootstrap.Progress({
28851             cls : 'roo-document-manager-progress',
28852             active : true,
28853             striped : true
28854         });
28855         
28856         this.progress.render(this.progressDialog.getChildContainer());
28857         
28858         this.progressBar = new Roo.bootstrap.ProgressBar({
28859             cls : 'roo-document-manager-progress-bar',
28860             aria_valuenow : 0,
28861             aria_valuemin : 0,
28862             aria_valuemax : 12,
28863             panel : 'success'
28864         });
28865         
28866         this.progressBar.render(this.progress.getChildContainer());
28867     },
28868     
28869     onUploaderClick : function(e)
28870     {
28871         e.preventDefault();
28872      
28873         if(this.fireEvent('beforeselectfile', this) != false){
28874             this.selectorEl.dom.click();
28875         }
28876         
28877     },
28878     
28879     onFileSelected : function(e)
28880     {
28881         e.preventDefault();
28882         
28883         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28884             return;
28885         }
28886         
28887         Roo.each(this.selectorEl.dom.files, function(file){
28888             if(this.fireEvent('inspect', this, file) != false){
28889                 this.files.push(file);
28890             }
28891         }, this);
28892         
28893         this.queue();
28894         
28895     },
28896     
28897     queue : function()
28898     {
28899         this.selectorEl.dom.value = '';
28900         
28901         if(!this.files || !this.files.length){
28902             return;
28903         }
28904         
28905         if(this.boxes > 0 && this.files.length > this.boxes){
28906             this.files = this.files.slice(0, this.boxes);
28907         }
28908         
28909         this.uploader.show();
28910         
28911         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28912             this.uploader.hide();
28913         }
28914         
28915         var _this = this;
28916         
28917         var files = [];
28918         
28919         var docs = [];
28920         
28921         Roo.each(this.files, function(file){
28922             
28923             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28924                 var f = this.renderPreview(file);
28925                 files.push(f);
28926                 return;
28927             }
28928             
28929             if(file.type.indexOf('image') != -1){
28930                 this.delegates.push(
28931                     (function(){
28932                         _this.process(file);
28933                     }).createDelegate(this)
28934                 );
28935         
28936                 return;
28937             }
28938             
28939             docs.push(
28940                 (function(){
28941                     _this.process(file);
28942                 }).createDelegate(this)
28943             );
28944             
28945         }, this);
28946         
28947         this.files = files;
28948         
28949         this.delegates = this.delegates.concat(docs);
28950         
28951         if(!this.delegates.length){
28952             this.refresh();
28953             return;
28954         }
28955         
28956         this.progressBar.aria_valuemax = this.delegates.length;
28957         
28958         this.arrange();
28959         
28960         return;
28961     },
28962     
28963     arrange : function()
28964     {
28965         if(!this.delegates.length){
28966             this.progressDialog.hide();
28967             this.refresh();
28968             return;
28969         }
28970         
28971         var delegate = this.delegates.shift();
28972         
28973         this.progressDialog.show();
28974         
28975         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28976         
28977         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28978         
28979         delegate();
28980     },
28981     
28982     refresh : function()
28983     {
28984         this.uploader.show();
28985         
28986         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28987             this.uploader.hide();
28988         }
28989         
28990         Roo.isTouch ? this.closable(false) : this.closable(true);
28991         
28992         this.fireEvent('refresh', this);
28993     },
28994     
28995     onRemove : function(e, el, o)
28996     {
28997         e.preventDefault();
28998         
28999         this.fireEvent('remove', this, o);
29000         
29001     },
29002     
29003     remove : function(o)
29004     {
29005         var files = [];
29006         
29007         Roo.each(this.files, function(file){
29008             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29009                 files.push(file);
29010                 return;
29011             }
29012
29013             o.target.remove();
29014
29015         }, this);
29016         
29017         this.files = files;
29018         
29019         this.refresh();
29020     },
29021     
29022     clear : function()
29023     {
29024         Roo.each(this.files, function(file){
29025             if(!file.target){
29026                 return;
29027             }
29028             
29029             file.target.remove();
29030
29031         }, this);
29032         
29033         this.files = [];
29034         
29035         this.refresh();
29036     },
29037     
29038     onClick : function(e, el, o)
29039     {
29040         e.preventDefault();
29041         
29042         this.fireEvent('click', this, o);
29043         
29044     },
29045     
29046     closable : function(closable)
29047     {
29048         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29049             
29050             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29051             
29052             if(closable){
29053                 el.show();
29054                 return;
29055             }
29056             
29057             el.hide();
29058             
29059         }, this);
29060     },
29061     
29062     xhrOnLoad : function(xhr)
29063     {
29064         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29065             el.remove();
29066         }, this);
29067         
29068         if (xhr.readyState !== 4) {
29069             this.arrange();
29070             this.fireEvent('exception', this, xhr);
29071             return;
29072         }
29073
29074         var response = Roo.decode(xhr.responseText);
29075         
29076         if(!response.success){
29077             this.arrange();
29078             this.fireEvent('exception', this, xhr);
29079             return;
29080         }
29081         
29082         var file = this.renderPreview(response.data);
29083         
29084         this.files.push(file);
29085         
29086         this.arrange();
29087         
29088         this.fireEvent('afterupload', this, xhr);
29089         
29090     },
29091     
29092     xhrOnError : function(xhr)
29093     {
29094         Roo.log('xhr on error');
29095         
29096         var response = Roo.decode(xhr.responseText);
29097           
29098         Roo.log(response);
29099         
29100         this.arrange();
29101     },
29102     
29103     process : function(file)
29104     {
29105         if(this.fireEvent('process', this, file) !== false){
29106             if(this.editable && file.type.indexOf('image') != -1){
29107                 this.fireEvent('edit', this, file);
29108                 return;
29109             }
29110
29111             this.uploadStart(file, false);
29112
29113             return;
29114         }
29115         
29116     },
29117     
29118     uploadStart : function(file, crop)
29119     {
29120         this.xhr = new XMLHttpRequest();
29121         
29122         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29123             this.arrange();
29124             return;
29125         }
29126         
29127         file.xhr = this.xhr;
29128             
29129         this.managerEl.createChild({
29130             tag : 'div',
29131             cls : 'roo-document-manager-loading',
29132             cn : [
29133                 {
29134                     tag : 'div',
29135                     tooltip : file.name,
29136                     cls : 'roo-document-manager-thumb',
29137                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29138                 }
29139             ]
29140
29141         });
29142
29143         this.xhr.open(this.method, this.url, true);
29144         
29145         var headers = {
29146             "Accept": "application/json",
29147             "Cache-Control": "no-cache",
29148             "X-Requested-With": "XMLHttpRequest"
29149         };
29150         
29151         for (var headerName in headers) {
29152             var headerValue = headers[headerName];
29153             if (headerValue) {
29154                 this.xhr.setRequestHeader(headerName, headerValue);
29155             }
29156         }
29157         
29158         var _this = this;
29159         
29160         this.xhr.onload = function()
29161         {
29162             _this.xhrOnLoad(_this.xhr);
29163         }
29164         
29165         this.xhr.onerror = function()
29166         {
29167             _this.xhrOnError(_this.xhr);
29168         }
29169         
29170         var formData = new FormData();
29171
29172         formData.append('returnHTML', 'NO');
29173         
29174         if(crop){
29175             formData.append('crop', crop);
29176         }
29177         
29178         formData.append(this.paramName, file, file.name);
29179         
29180         var options = {
29181             file : file, 
29182             manually : false
29183         };
29184         
29185         if(this.fireEvent('prepare', this, formData, options) != false){
29186             
29187             if(options.manually){
29188                 return;
29189             }
29190             
29191             this.xhr.send(formData);
29192             return;
29193         };
29194         
29195         this.uploadCancel();
29196     },
29197     
29198     uploadCancel : function()
29199     {
29200         if (this.xhr) {
29201             this.xhr.abort();
29202         }
29203         
29204         this.delegates = [];
29205         
29206         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29207             el.remove();
29208         }, this);
29209         
29210         this.arrange();
29211     },
29212     
29213     renderPreview : function(file)
29214     {
29215         if(typeof(file.target) != 'undefined' && file.target){
29216             return file;
29217         }
29218         
29219         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29220         
29221         var previewEl = this.managerEl.createChild({
29222             tag : 'div',
29223             cls : 'roo-document-manager-preview',
29224             cn : [
29225                 {
29226                     tag : 'div',
29227                     tooltip : file[this.toolTipName],
29228                     cls : 'roo-document-manager-thumb',
29229                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29230                 },
29231                 {
29232                     tag : 'button',
29233                     cls : 'close',
29234                     html : '<i class="fa fa-times-circle"></i>'
29235                 }
29236             ]
29237         });
29238
29239         var close = previewEl.select('button.close', true).first();
29240
29241         close.on('click', this.onRemove, this, file);
29242
29243         file.target = previewEl;
29244
29245         var image = previewEl.select('img', true).first();
29246         
29247         var _this = this;
29248         
29249         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29250         
29251         image.on('click', this.onClick, this, file);
29252         
29253         this.fireEvent('previewrendered', this, file);
29254         
29255         return file;
29256         
29257     },
29258     
29259     onPreviewLoad : function(file, image)
29260     {
29261         if(typeof(file.target) == 'undefined' || !file.target){
29262             return;
29263         }
29264         
29265         var width = image.dom.naturalWidth || image.dom.width;
29266         var height = image.dom.naturalHeight || image.dom.height;
29267         
29268         if(width > height){
29269             file.target.addClass('wide');
29270             return;
29271         }
29272         
29273         file.target.addClass('tall');
29274         return;
29275         
29276     },
29277     
29278     uploadFromSource : function(file, crop)
29279     {
29280         this.xhr = new XMLHttpRequest();
29281         
29282         this.managerEl.createChild({
29283             tag : 'div',
29284             cls : 'roo-document-manager-loading',
29285             cn : [
29286                 {
29287                     tag : 'div',
29288                     tooltip : file.name,
29289                     cls : 'roo-document-manager-thumb',
29290                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29291                 }
29292             ]
29293
29294         });
29295
29296         this.xhr.open(this.method, this.url, true);
29297         
29298         var headers = {
29299             "Accept": "application/json",
29300             "Cache-Control": "no-cache",
29301             "X-Requested-With": "XMLHttpRequest"
29302         };
29303         
29304         for (var headerName in headers) {
29305             var headerValue = headers[headerName];
29306             if (headerValue) {
29307                 this.xhr.setRequestHeader(headerName, headerValue);
29308             }
29309         }
29310         
29311         var _this = this;
29312         
29313         this.xhr.onload = function()
29314         {
29315             _this.xhrOnLoad(_this.xhr);
29316         }
29317         
29318         this.xhr.onerror = function()
29319         {
29320             _this.xhrOnError(_this.xhr);
29321         }
29322         
29323         var formData = new FormData();
29324
29325         formData.append('returnHTML', 'NO');
29326         
29327         formData.append('crop', crop);
29328         
29329         if(typeof(file.filename) != 'undefined'){
29330             formData.append('filename', file.filename);
29331         }
29332         
29333         if(typeof(file.mimetype) != 'undefined'){
29334             formData.append('mimetype', file.mimetype);
29335         }
29336         
29337         Roo.log(formData);
29338         
29339         if(this.fireEvent('prepare', this, formData) != false){
29340             this.xhr.send(formData);
29341         };
29342     }
29343 });
29344
29345 /*
29346 * Licence: LGPL
29347 */
29348
29349 /**
29350  * @class Roo.bootstrap.DocumentViewer
29351  * @extends Roo.bootstrap.Component
29352  * Bootstrap DocumentViewer class
29353  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29354  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29355  * 
29356  * @constructor
29357  * Create a new DocumentViewer
29358  * @param {Object} config The config object
29359  */
29360
29361 Roo.bootstrap.DocumentViewer = function(config){
29362     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29363     
29364     this.addEvents({
29365         /**
29366          * @event initial
29367          * Fire after initEvent
29368          * @param {Roo.bootstrap.DocumentViewer} this
29369          */
29370         "initial" : true,
29371         /**
29372          * @event click
29373          * Fire after click
29374          * @param {Roo.bootstrap.DocumentViewer} this
29375          */
29376         "click" : true,
29377         /**
29378          * @event download
29379          * Fire after download button
29380          * @param {Roo.bootstrap.DocumentViewer} this
29381          */
29382         "download" : true,
29383         /**
29384          * @event trash
29385          * Fire after trash button
29386          * @param {Roo.bootstrap.DocumentViewer} this
29387          */
29388         "trash" : true
29389         
29390     });
29391 };
29392
29393 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29394     
29395     showDownload : true,
29396     
29397     showTrash : true,
29398     
29399     getAutoCreate : function()
29400     {
29401         var cfg = {
29402             tag : 'div',
29403             cls : 'roo-document-viewer',
29404             cn : [
29405                 {
29406                     tag : 'div',
29407                     cls : 'roo-document-viewer-body',
29408                     cn : [
29409                         {
29410                             tag : 'div',
29411                             cls : 'roo-document-viewer-thumb',
29412                             cn : [
29413                                 {
29414                                     tag : 'img',
29415                                     cls : 'roo-document-viewer-image'
29416                                 }
29417                             ]
29418                         }
29419                     ]
29420                 },
29421                 {
29422                     tag : 'div',
29423                     cls : 'roo-document-viewer-footer',
29424                     cn : {
29425                         tag : 'div',
29426                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29427                         cn : [
29428                             {
29429                                 tag : 'div',
29430                                 cls : 'btn-group roo-document-viewer-download',
29431                                 cn : [
29432                                     {
29433                                         tag : 'button',
29434                                         cls : 'btn btn-default',
29435                                         html : '<i class="fa fa-download"></i>'
29436                                     }
29437                                 ]
29438                             },
29439                             {
29440                                 tag : 'div',
29441                                 cls : 'btn-group roo-document-viewer-trash',
29442                                 cn : [
29443                                     {
29444                                         tag : 'button',
29445                                         cls : 'btn btn-default',
29446                                         html : '<i class="fa fa-trash"></i>'
29447                                     }
29448                                 ]
29449                             }
29450                         ]
29451                     }
29452                 }
29453             ]
29454         };
29455         
29456         return cfg;
29457     },
29458     
29459     initEvents : function()
29460     {
29461         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29462         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29463         
29464         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29465         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29466         
29467         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29468         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29469         
29470         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29471         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29472         
29473         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29474         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29475         
29476         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29477         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29478         
29479         this.bodyEl.on('click', this.onClick, this);
29480         this.downloadBtn.on('click', this.onDownload, this);
29481         this.trashBtn.on('click', this.onTrash, this);
29482         
29483         this.downloadBtn.hide();
29484         this.trashBtn.hide();
29485         
29486         if(this.showDownload){
29487             this.downloadBtn.show();
29488         }
29489         
29490         if(this.showTrash){
29491             this.trashBtn.show();
29492         }
29493         
29494         if(!this.showDownload && !this.showTrash) {
29495             this.footerEl.hide();
29496         }
29497         
29498     },
29499     
29500     initial : function()
29501     {
29502         this.fireEvent('initial', this);
29503         
29504     },
29505     
29506     onClick : function(e)
29507     {
29508         e.preventDefault();
29509         
29510         this.fireEvent('click', this);
29511     },
29512     
29513     onDownload : function(e)
29514     {
29515         e.preventDefault();
29516         
29517         this.fireEvent('download', this);
29518     },
29519     
29520     onTrash : function(e)
29521     {
29522         e.preventDefault();
29523         
29524         this.fireEvent('trash', this);
29525     }
29526     
29527 });
29528 /*
29529  * - LGPL
29530  *
29531  * nav progress bar
29532  * 
29533  */
29534
29535 /**
29536  * @class Roo.bootstrap.NavProgressBar
29537  * @extends Roo.bootstrap.Component
29538  * Bootstrap NavProgressBar class
29539  * 
29540  * @constructor
29541  * Create a new nav progress bar
29542  * @param {Object} config The config object
29543  */
29544
29545 Roo.bootstrap.NavProgressBar = function(config){
29546     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29547
29548     this.bullets = this.bullets || [];
29549    
29550 //    Roo.bootstrap.NavProgressBar.register(this);
29551      this.addEvents({
29552         /**
29553              * @event changed
29554              * Fires when the active item changes
29555              * @param {Roo.bootstrap.NavProgressBar} this
29556              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29557              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29558          */
29559         'changed': true
29560      });
29561     
29562 };
29563
29564 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29565     
29566     bullets : [],
29567     barItems : [],
29568     
29569     getAutoCreate : function()
29570     {
29571         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29572         
29573         cfg = {
29574             tag : 'div',
29575             cls : 'roo-navigation-bar-group',
29576             cn : [
29577                 {
29578                     tag : 'div',
29579                     cls : 'roo-navigation-top-bar'
29580                 },
29581                 {
29582                     tag : 'div',
29583                     cls : 'roo-navigation-bullets-bar',
29584                     cn : [
29585                         {
29586                             tag : 'ul',
29587                             cls : 'roo-navigation-bar'
29588                         }
29589                     ]
29590                 },
29591                 
29592                 {
29593                     tag : 'div',
29594                     cls : 'roo-navigation-bottom-bar'
29595                 }
29596             ]
29597             
29598         };
29599         
29600         return cfg;
29601         
29602     },
29603     
29604     initEvents: function() 
29605     {
29606         
29607     },
29608     
29609     onRender : function(ct, position) 
29610     {
29611         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29612         
29613         if(this.bullets.length){
29614             Roo.each(this.bullets, function(b){
29615                this.addItem(b);
29616             }, this);
29617         }
29618         
29619         this.format();
29620         
29621     },
29622     
29623     addItem : function(cfg)
29624     {
29625         var item = new Roo.bootstrap.NavProgressItem(cfg);
29626         
29627         item.parentId = this.id;
29628         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29629         
29630         if(cfg.html){
29631             var top = new Roo.bootstrap.Element({
29632                 tag : 'div',
29633                 cls : 'roo-navigation-bar-text'
29634             });
29635             
29636             var bottom = new Roo.bootstrap.Element({
29637                 tag : 'div',
29638                 cls : 'roo-navigation-bar-text'
29639             });
29640             
29641             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29642             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29643             
29644             var topText = new Roo.bootstrap.Element({
29645                 tag : 'span',
29646                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29647             });
29648             
29649             var bottomText = new Roo.bootstrap.Element({
29650                 tag : 'span',
29651                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29652             });
29653             
29654             topText.onRender(top.el, null);
29655             bottomText.onRender(bottom.el, null);
29656             
29657             item.topEl = top;
29658             item.bottomEl = bottom;
29659         }
29660         
29661         this.barItems.push(item);
29662         
29663         return item;
29664     },
29665     
29666     getActive : function()
29667     {
29668         var active = false;
29669         
29670         Roo.each(this.barItems, function(v){
29671             
29672             if (!v.isActive()) {
29673                 return;
29674             }
29675             
29676             active = v;
29677             return false;
29678             
29679         });
29680         
29681         return active;
29682     },
29683     
29684     setActiveItem : function(item)
29685     {
29686         var prev = false;
29687         
29688         Roo.each(this.barItems, function(v){
29689             if (v.rid == item.rid) {
29690                 return ;
29691             }
29692             
29693             if (v.isActive()) {
29694                 v.setActive(false);
29695                 prev = v;
29696             }
29697         });
29698
29699         item.setActive(true);
29700         
29701         this.fireEvent('changed', this, item, prev);
29702     },
29703     
29704     getBarItem: function(rid)
29705     {
29706         var ret = false;
29707         
29708         Roo.each(this.barItems, function(e) {
29709             if (e.rid != rid) {
29710                 return;
29711             }
29712             
29713             ret =  e;
29714             return false;
29715         });
29716         
29717         return ret;
29718     },
29719     
29720     indexOfItem : function(item)
29721     {
29722         var index = false;
29723         
29724         Roo.each(this.barItems, function(v, i){
29725             
29726             if (v.rid != item.rid) {
29727                 return;
29728             }
29729             
29730             index = i;
29731             return false
29732         });
29733         
29734         return index;
29735     },
29736     
29737     setActiveNext : function()
29738     {
29739         var i = this.indexOfItem(this.getActive());
29740         
29741         if (i > this.barItems.length) {
29742             return;
29743         }
29744         
29745         this.setActiveItem(this.barItems[i+1]);
29746     },
29747     
29748     setActivePrev : function()
29749     {
29750         var i = this.indexOfItem(this.getActive());
29751         
29752         if (i  < 1) {
29753             return;
29754         }
29755         
29756         this.setActiveItem(this.barItems[i-1]);
29757     },
29758     
29759     format : function()
29760     {
29761         if(!this.barItems.length){
29762             return;
29763         }
29764      
29765         var width = 100 / this.barItems.length;
29766         
29767         Roo.each(this.barItems, function(i){
29768             i.el.setStyle('width', width + '%');
29769             i.topEl.el.setStyle('width', width + '%');
29770             i.bottomEl.el.setStyle('width', width + '%');
29771         }, this);
29772         
29773     }
29774     
29775 });
29776 /*
29777  * - LGPL
29778  *
29779  * Nav Progress Item
29780  * 
29781  */
29782
29783 /**
29784  * @class Roo.bootstrap.NavProgressItem
29785  * @extends Roo.bootstrap.Component
29786  * Bootstrap NavProgressItem class
29787  * @cfg {String} rid the reference id
29788  * @cfg {Boolean} active (true|false) Is item active default false
29789  * @cfg {Boolean} disabled (true|false) Is item active default false
29790  * @cfg {String} html
29791  * @cfg {String} position (top|bottom) text position default bottom
29792  * @cfg {String} icon show icon instead of number
29793  * 
29794  * @constructor
29795  * Create a new NavProgressItem
29796  * @param {Object} config The config object
29797  */
29798 Roo.bootstrap.NavProgressItem = function(config){
29799     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29800     this.addEvents({
29801         // raw events
29802         /**
29803          * @event click
29804          * The raw click event for the entire grid.
29805          * @param {Roo.bootstrap.NavProgressItem} this
29806          * @param {Roo.EventObject} e
29807          */
29808         "click" : true
29809     });
29810    
29811 };
29812
29813 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29814     
29815     rid : '',
29816     active : false,
29817     disabled : false,
29818     html : '',
29819     position : 'bottom',
29820     icon : false,
29821     
29822     getAutoCreate : function()
29823     {
29824         var iconCls = 'roo-navigation-bar-item-icon';
29825         
29826         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29827         
29828         var cfg = {
29829             tag: 'li',
29830             cls: 'roo-navigation-bar-item',
29831             cn : [
29832                 {
29833                     tag : 'i',
29834                     cls : iconCls
29835                 }
29836             ]
29837         };
29838         
29839         if(this.active){
29840             cfg.cls += ' active';
29841         }
29842         if(this.disabled){
29843             cfg.cls += ' disabled';
29844         }
29845         
29846         return cfg;
29847     },
29848     
29849     disable : function()
29850     {
29851         this.setDisabled(true);
29852     },
29853     
29854     enable : function()
29855     {
29856         this.setDisabled(false);
29857     },
29858     
29859     initEvents: function() 
29860     {
29861         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29862         
29863         this.iconEl.on('click', this.onClick, this);
29864     },
29865     
29866     onClick : function(e)
29867     {
29868         e.preventDefault();
29869         
29870         if(this.disabled){
29871             return;
29872         }
29873         
29874         if(this.fireEvent('click', this, e) === false){
29875             return;
29876         };
29877         
29878         this.parent().setActiveItem(this);
29879     },
29880     
29881     isActive: function () 
29882     {
29883         return this.active;
29884     },
29885     
29886     setActive : function(state)
29887     {
29888         if(this.active == state){
29889             return;
29890         }
29891         
29892         this.active = state;
29893         
29894         if (state) {
29895             this.el.addClass('active');
29896             return;
29897         }
29898         
29899         this.el.removeClass('active');
29900         
29901         return;
29902     },
29903     
29904     setDisabled : function(state)
29905     {
29906         if(this.disabled == state){
29907             return;
29908         }
29909         
29910         this.disabled = state;
29911         
29912         if (state) {
29913             this.el.addClass('disabled');
29914             return;
29915         }
29916         
29917         this.el.removeClass('disabled');
29918     },
29919     
29920     tooltipEl : function()
29921     {
29922         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29923     }
29924 });
29925  
29926
29927  /*
29928  * - LGPL
29929  *
29930  * FieldLabel
29931  * 
29932  */
29933
29934 /**
29935  * @class Roo.bootstrap.FieldLabel
29936  * @extends Roo.bootstrap.Component
29937  * Bootstrap FieldLabel class
29938  * @cfg {String} html contents of the element
29939  * @cfg {String} tag tag of the element default label
29940  * @cfg {String} cls class of the element
29941  * @cfg {String} target label target 
29942  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29943  * @cfg {String} invalidClass default "text-warning"
29944  * @cfg {String} validClass default "text-success"
29945  * @cfg {String} iconTooltip default "This field is required"
29946  * @cfg {String} indicatorpos (left|right) default left
29947  * 
29948  * @constructor
29949  * Create a new FieldLabel
29950  * @param {Object} config The config object
29951  */
29952
29953 Roo.bootstrap.FieldLabel = function(config){
29954     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29955     
29956     this.addEvents({
29957             /**
29958              * @event invalid
29959              * Fires after the field has been marked as invalid.
29960              * @param {Roo.form.FieldLabel} this
29961              * @param {String} msg The validation message
29962              */
29963             invalid : true,
29964             /**
29965              * @event valid
29966              * Fires after the field has been validated with no errors.
29967              * @param {Roo.form.FieldLabel} this
29968              */
29969             valid : true
29970         });
29971 };
29972
29973 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29974     
29975     tag: 'label',
29976     cls: '',
29977     html: '',
29978     target: '',
29979     allowBlank : true,
29980     invalidClass : 'has-warning',
29981     validClass : 'has-success',
29982     iconTooltip : 'This field is required',
29983     indicatorpos : 'left',
29984     
29985     getAutoCreate : function(){
29986         
29987         var cfg = {
29988             tag : this.tag,
29989             cls : 'roo-bootstrap-field-label ' + this.cls,
29990             for : this.target,
29991             cn : [
29992                 {
29993                     tag : 'i',
29994                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29995                     tooltip : this.iconTooltip
29996                 },
29997                 {
29998                     tag : 'span',
29999                     html : this.html
30000                 }
30001             ] 
30002         };
30003         
30004         if(this.indicatorpos == 'right'){
30005             var cfg = {
30006                 tag : this.tag,
30007                 cls : 'roo-bootstrap-field-label ' + this.cls,
30008                 for : this.target,
30009                 cn : [
30010                     {
30011                         tag : 'span',
30012                         html : this.html
30013                     },
30014                     {
30015                         tag : 'i',
30016                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
30017                         tooltip : this.iconTooltip
30018                     }
30019                 ] 
30020             };
30021         }
30022         
30023         return cfg;
30024     },
30025     
30026     initEvents: function() 
30027     {
30028         Roo.bootstrap.Element.superclass.initEvents.call(this);
30029         
30030         this.indicator = this.indicatorEl();
30031         
30032         if(this.indicator){
30033             this.indicator.removeClass('visible');
30034             this.indicator.addClass('invisible');
30035         }
30036         
30037         Roo.bootstrap.FieldLabel.register(this);
30038     },
30039     
30040     indicatorEl : function()
30041     {
30042         var indicator = this.el.select('i.roo-required-indicator',true).first();
30043         
30044         if(!indicator){
30045             return false;
30046         }
30047         
30048         return indicator;
30049         
30050     },
30051     
30052     /**
30053      * Mark this field as valid
30054      */
30055     markValid : function()
30056     {
30057         if(this.indicator){
30058             this.indicator.removeClass('visible');
30059             this.indicator.addClass('invisible');
30060         }
30061         
30062         this.el.removeClass(this.invalidClass);
30063         
30064         this.el.addClass(this.validClass);
30065         
30066         this.fireEvent('valid', this);
30067     },
30068     
30069     /**
30070      * Mark this field as invalid
30071      * @param {String} msg The validation message
30072      */
30073     markInvalid : function(msg)
30074     {
30075         if(this.indicator){
30076             this.indicator.removeClass('invisible');
30077             this.indicator.addClass('visible');
30078         }
30079         
30080         this.el.removeClass(this.validClass);
30081         
30082         this.el.addClass(this.invalidClass);
30083         
30084         this.fireEvent('invalid', this, msg);
30085     }
30086     
30087    
30088 });
30089
30090 Roo.apply(Roo.bootstrap.FieldLabel, {
30091     
30092     groups: {},
30093     
30094      /**
30095     * register a FieldLabel Group
30096     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30097     */
30098     register : function(label)
30099     {
30100         if(this.groups.hasOwnProperty(label.target)){
30101             return;
30102         }
30103      
30104         this.groups[label.target] = label;
30105         
30106     },
30107     /**
30108     * fetch a FieldLabel Group based on the target
30109     * @param {string} target
30110     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30111     */
30112     get: function(target) {
30113         if (typeof(this.groups[target]) == 'undefined') {
30114             return false;
30115         }
30116         
30117         return this.groups[target] ;
30118     }
30119 });
30120
30121  
30122
30123  /*
30124  * - LGPL
30125  *
30126  * page DateSplitField.
30127  * 
30128  */
30129
30130
30131 /**
30132  * @class Roo.bootstrap.DateSplitField
30133  * @extends Roo.bootstrap.Component
30134  * Bootstrap DateSplitField class
30135  * @cfg {string} fieldLabel - the label associated
30136  * @cfg {Number} labelWidth set the width of label (0-12)
30137  * @cfg {String} labelAlign (top|left)
30138  * @cfg {Boolean} dayAllowBlank (true|false) default false
30139  * @cfg {Boolean} monthAllowBlank (true|false) default false
30140  * @cfg {Boolean} yearAllowBlank (true|false) default false
30141  * @cfg {string} dayPlaceholder 
30142  * @cfg {string} monthPlaceholder
30143  * @cfg {string} yearPlaceholder
30144  * @cfg {string} dayFormat default 'd'
30145  * @cfg {string} monthFormat default 'm'
30146  * @cfg {string} yearFormat default 'Y'
30147  * @cfg {Number} labellg set the width of label (1-12)
30148  * @cfg {Number} labelmd set the width of label (1-12)
30149  * @cfg {Number} labelsm set the width of label (1-12)
30150  * @cfg {Number} labelxs set the width of label (1-12)
30151
30152  *     
30153  * @constructor
30154  * Create a new DateSplitField
30155  * @param {Object} config The config object
30156  */
30157
30158 Roo.bootstrap.DateSplitField = function(config){
30159     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30160     
30161     this.addEvents({
30162         // raw events
30163          /**
30164          * @event years
30165          * getting the data of years
30166          * @param {Roo.bootstrap.DateSplitField} this
30167          * @param {Object} years
30168          */
30169         "years" : true,
30170         /**
30171          * @event days
30172          * getting the data of days
30173          * @param {Roo.bootstrap.DateSplitField} this
30174          * @param {Object} days
30175          */
30176         "days" : true,
30177         /**
30178          * @event invalid
30179          * Fires after the field has been marked as invalid.
30180          * @param {Roo.form.Field} this
30181          * @param {String} msg The validation message
30182          */
30183         invalid : true,
30184        /**
30185          * @event valid
30186          * Fires after the field has been validated with no errors.
30187          * @param {Roo.form.Field} this
30188          */
30189         valid : true
30190     });
30191 };
30192
30193 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30194     
30195     fieldLabel : '',
30196     labelAlign : 'top',
30197     labelWidth : 3,
30198     dayAllowBlank : false,
30199     monthAllowBlank : false,
30200     yearAllowBlank : false,
30201     dayPlaceholder : '',
30202     monthPlaceholder : '',
30203     yearPlaceholder : '',
30204     dayFormat : 'd',
30205     monthFormat : 'm',
30206     yearFormat : 'Y',
30207     isFormField : true,
30208     labellg : 0,
30209     labelmd : 0,
30210     labelsm : 0,
30211     labelxs : 0,
30212     
30213     getAutoCreate : function()
30214     {
30215         var cfg = {
30216             tag : 'div',
30217             cls : 'row roo-date-split-field-group',
30218             cn : [
30219                 {
30220                     tag : 'input',
30221                     type : 'hidden',
30222                     cls : 'form-hidden-field roo-date-split-field-group-value',
30223                     name : this.name
30224                 }
30225             ]
30226         };
30227         
30228         var labelCls = 'col-md-12';
30229         var contentCls = 'col-md-4';
30230         
30231         if(this.fieldLabel){
30232             
30233             var label = {
30234                 tag : 'div',
30235                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30236                 cn : [
30237                     {
30238                         tag : 'label',
30239                         html : this.fieldLabel
30240                     }
30241                 ]
30242             };
30243             
30244             if(this.labelAlign == 'left'){
30245             
30246                 if(this.labelWidth > 12){
30247                     label.style = "width: " + this.labelWidth + 'px';
30248                 }
30249
30250                 if(this.labelWidth < 13 && this.labelmd == 0){
30251                     this.labelmd = this.labelWidth;
30252                 }
30253
30254                 if(this.labellg > 0){
30255                     labelCls = ' col-lg-' + this.labellg;
30256                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30257                 }
30258
30259                 if(this.labelmd > 0){
30260                     labelCls = ' col-md-' + this.labelmd;
30261                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30262                 }
30263
30264                 if(this.labelsm > 0){
30265                     labelCls = ' col-sm-' + this.labelsm;
30266                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30267                 }
30268
30269                 if(this.labelxs > 0){
30270                     labelCls = ' col-xs-' + this.labelxs;
30271                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30272                 }
30273             }
30274             
30275             label.cls += ' ' + labelCls;
30276             
30277             cfg.cn.push(label);
30278         }
30279         
30280         Roo.each(['day', 'month', 'year'], function(t){
30281             cfg.cn.push({
30282                 tag : 'div',
30283                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30284             });
30285         }, this);
30286         
30287         return cfg;
30288     },
30289     
30290     inputEl: function ()
30291     {
30292         return this.el.select('.roo-date-split-field-group-value', true).first();
30293     },
30294     
30295     onRender : function(ct, position) 
30296     {
30297         var _this = this;
30298         
30299         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30300         
30301         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30302         
30303         this.dayField = new Roo.bootstrap.ComboBox({
30304             allowBlank : this.dayAllowBlank,
30305             alwaysQuery : true,
30306             displayField : 'value',
30307             editable : false,
30308             fieldLabel : '',
30309             forceSelection : true,
30310             mode : 'local',
30311             placeholder : this.dayPlaceholder,
30312             selectOnFocus : true,
30313             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30314             triggerAction : 'all',
30315             typeAhead : true,
30316             valueField : 'value',
30317             store : new Roo.data.SimpleStore({
30318                 data : (function() {    
30319                     var days = [];
30320                     _this.fireEvent('days', _this, days);
30321                     return days;
30322                 })(),
30323                 fields : [ 'value' ]
30324             }),
30325             listeners : {
30326                 select : function (_self, record, index)
30327                 {
30328                     _this.setValue(_this.getValue());
30329                 }
30330             }
30331         });
30332
30333         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30334         
30335         this.monthField = new Roo.bootstrap.MonthField({
30336             after : '<i class=\"fa fa-calendar\"></i>',
30337             allowBlank : this.monthAllowBlank,
30338             placeholder : this.monthPlaceholder,
30339             readOnly : true,
30340             listeners : {
30341                 render : function (_self)
30342                 {
30343                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30344                         e.preventDefault();
30345                         _self.focus();
30346                     });
30347                 },
30348                 select : function (_self, oldvalue, newvalue)
30349                 {
30350                     _this.setValue(_this.getValue());
30351                 }
30352             }
30353         });
30354         
30355         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30356         
30357         this.yearField = new Roo.bootstrap.ComboBox({
30358             allowBlank : this.yearAllowBlank,
30359             alwaysQuery : true,
30360             displayField : 'value',
30361             editable : false,
30362             fieldLabel : '',
30363             forceSelection : true,
30364             mode : 'local',
30365             placeholder : this.yearPlaceholder,
30366             selectOnFocus : true,
30367             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30368             triggerAction : 'all',
30369             typeAhead : true,
30370             valueField : 'value',
30371             store : new Roo.data.SimpleStore({
30372                 data : (function() {
30373                     var years = [];
30374                     _this.fireEvent('years', _this, years);
30375                     return years;
30376                 })(),
30377                 fields : [ 'value' ]
30378             }),
30379             listeners : {
30380                 select : function (_self, record, index)
30381                 {
30382                     _this.setValue(_this.getValue());
30383                 }
30384             }
30385         });
30386
30387         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30388     },
30389     
30390     setValue : function(v, format)
30391     {
30392         this.inputEl.dom.value = v;
30393         
30394         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30395         
30396         var d = Date.parseDate(v, f);
30397         
30398         if(!d){
30399             this.validate();
30400             return;
30401         }
30402         
30403         this.setDay(d.format(this.dayFormat));
30404         this.setMonth(d.format(this.monthFormat));
30405         this.setYear(d.format(this.yearFormat));
30406         
30407         this.validate();
30408         
30409         return;
30410     },
30411     
30412     setDay : function(v)
30413     {
30414         this.dayField.setValue(v);
30415         this.inputEl.dom.value = this.getValue();
30416         this.validate();
30417         return;
30418     },
30419     
30420     setMonth : function(v)
30421     {
30422         this.monthField.setValue(v, true);
30423         this.inputEl.dom.value = this.getValue();
30424         this.validate();
30425         return;
30426     },
30427     
30428     setYear : function(v)
30429     {
30430         this.yearField.setValue(v);
30431         this.inputEl.dom.value = this.getValue();
30432         this.validate();
30433         return;
30434     },
30435     
30436     getDay : function()
30437     {
30438         return this.dayField.getValue();
30439     },
30440     
30441     getMonth : function()
30442     {
30443         return this.monthField.getValue();
30444     },
30445     
30446     getYear : function()
30447     {
30448         return this.yearField.getValue();
30449     },
30450     
30451     getValue : function()
30452     {
30453         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30454         
30455         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30456         
30457         return date;
30458     },
30459     
30460     reset : function()
30461     {
30462         this.setDay('');
30463         this.setMonth('');
30464         this.setYear('');
30465         this.inputEl.dom.value = '';
30466         this.validate();
30467         return;
30468     },
30469     
30470     validate : function()
30471     {
30472         var d = this.dayField.validate();
30473         var m = this.monthField.validate();
30474         var y = this.yearField.validate();
30475         
30476         var valid = true;
30477         
30478         if(
30479                 (!this.dayAllowBlank && !d) ||
30480                 (!this.monthAllowBlank && !m) ||
30481                 (!this.yearAllowBlank && !y)
30482         ){
30483             valid = false;
30484         }
30485         
30486         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30487             return valid;
30488         }
30489         
30490         if(valid){
30491             this.markValid();
30492             return valid;
30493         }
30494         
30495         this.markInvalid();
30496         
30497         return valid;
30498     },
30499     
30500     markValid : function()
30501     {
30502         
30503         var label = this.el.select('label', true).first();
30504         var icon = this.el.select('i.fa-star', true).first();
30505
30506         if(label && icon){
30507             icon.remove();
30508         }
30509         
30510         this.fireEvent('valid', this);
30511     },
30512     
30513      /**
30514      * Mark this field as invalid
30515      * @param {String} msg The validation message
30516      */
30517     markInvalid : function(msg)
30518     {
30519         
30520         var label = this.el.select('label', true).first();
30521         var icon = this.el.select('i.fa-star', true).first();
30522
30523         if(label && !icon){
30524             this.el.select('.roo-date-split-field-label', true).createChild({
30525                 tag : 'i',
30526                 cls : 'text-danger fa fa-lg fa-star',
30527                 tooltip : 'This field is required',
30528                 style : 'margin-right:5px;'
30529             }, label, true);
30530         }
30531         
30532         this.fireEvent('invalid', this, msg);
30533     },
30534     
30535     clearInvalid : function()
30536     {
30537         var label = this.el.select('label', true).first();
30538         var icon = this.el.select('i.fa-star', true).first();
30539
30540         if(label && icon){
30541             icon.remove();
30542         }
30543         
30544         this.fireEvent('valid', this);
30545     },
30546     
30547     getName: function()
30548     {
30549         return this.name;
30550     }
30551     
30552 });
30553
30554  /**
30555  *
30556  * This is based on 
30557  * http://masonry.desandro.com
30558  *
30559  * The idea is to render all the bricks based on vertical width...
30560  *
30561  * The original code extends 'outlayer' - we might need to use that....
30562  * 
30563  */
30564
30565
30566 /**
30567  * @class Roo.bootstrap.LayoutMasonry
30568  * @extends Roo.bootstrap.Component
30569  * Bootstrap Layout Masonry class
30570  * 
30571  * @constructor
30572  * Create a new Element
30573  * @param {Object} config The config object
30574  */
30575
30576 Roo.bootstrap.LayoutMasonry = function(config){
30577     
30578     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30579     
30580     this.bricks = [];
30581     
30582     Roo.bootstrap.LayoutMasonry.register(this);
30583     
30584     this.addEvents({
30585         // raw events
30586         /**
30587          * @event layout
30588          * Fire after layout the items
30589          * @param {Roo.bootstrap.LayoutMasonry} this
30590          * @param {Roo.EventObject} e
30591          */
30592         "layout" : true
30593     });
30594     
30595 };
30596
30597 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30598     
30599     /**
30600      * @cfg {Boolean} isLayoutInstant = no animation?
30601      */   
30602     isLayoutInstant : false, // needed?
30603    
30604     /**
30605      * @cfg {Number} boxWidth  width of the columns
30606      */   
30607     boxWidth : 450,
30608     
30609       /**
30610      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30611      */   
30612     boxHeight : 0,
30613     
30614     /**
30615      * @cfg {Number} padWidth padding below box..
30616      */   
30617     padWidth : 10, 
30618     
30619     /**
30620      * @cfg {Number} gutter gutter width..
30621      */   
30622     gutter : 10,
30623     
30624      /**
30625      * @cfg {Number} maxCols maximum number of columns
30626      */   
30627     
30628     maxCols: 0,
30629     
30630     /**
30631      * @cfg {Boolean} isAutoInitial defalut true
30632      */   
30633     isAutoInitial : true, 
30634     
30635     containerWidth: 0,
30636     
30637     /**
30638      * @cfg {Boolean} isHorizontal defalut false
30639      */   
30640     isHorizontal : false, 
30641
30642     currentSize : null,
30643     
30644     tag: 'div',
30645     
30646     cls: '',
30647     
30648     bricks: null, //CompositeElement
30649     
30650     cols : 1,
30651     
30652     _isLayoutInited : false,
30653     
30654 //    isAlternative : false, // only use for vertical layout...
30655     
30656     /**
30657      * @cfg {Number} alternativePadWidth padding below box..
30658      */   
30659     alternativePadWidth : 50,
30660     
30661     selectedBrick : [],
30662     
30663     getAutoCreate : function(){
30664         
30665         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30666         
30667         var cfg = {
30668             tag: this.tag,
30669             cls: 'blog-masonary-wrapper ' + this.cls,
30670             cn : {
30671                 cls : 'mas-boxes masonary'
30672             }
30673         };
30674         
30675         return cfg;
30676     },
30677     
30678     getChildContainer: function( )
30679     {
30680         if (this.boxesEl) {
30681             return this.boxesEl;
30682         }
30683         
30684         this.boxesEl = this.el.select('.mas-boxes').first();
30685         
30686         return this.boxesEl;
30687     },
30688     
30689     
30690     initEvents : function()
30691     {
30692         var _this = this;
30693         
30694         if(this.isAutoInitial){
30695             Roo.log('hook children rendered');
30696             this.on('childrenrendered', function() {
30697                 Roo.log('children rendered');
30698                 _this.initial();
30699             } ,this);
30700         }
30701     },
30702     
30703     initial : function()
30704     {
30705         this.selectedBrick = [];
30706         
30707         this.currentSize = this.el.getBox(true);
30708         
30709         Roo.EventManager.onWindowResize(this.resize, this); 
30710
30711         if(!this.isAutoInitial){
30712             this.layout();
30713             return;
30714         }
30715         
30716         this.layout();
30717         
30718         return;
30719         //this.layout.defer(500,this);
30720         
30721     },
30722     
30723     resize : function()
30724     {
30725         var cs = this.el.getBox(true);
30726         
30727         if (
30728                 this.currentSize.width == cs.width && 
30729                 this.currentSize.x == cs.x && 
30730                 this.currentSize.height == cs.height && 
30731                 this.currentSize.y == cs.y 
30732         ) {
30733             Roo.log("no change in with or X or Y");
30734             return;
30735         }
30736         
30737         this.currentSize = cs;
30738         
30739         this.layout();
30740         
30741     },
30742     
30743     layout : function()
30744     {   
30745         this._resetLayout();
30746         
30747         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30748         
30749         this.layoutItems( isInstant );
30750       
30751         this._isLayoutInited = true;
30752         
30753         this.fireEvent('layout', this);
30754         
30755     },
30756     
30757     _resetLayout : function()
30758     {
30759         if(this.isHorizontal){
30760             this.horizontalMeasureColumns();
30761             return;
30762         }
30763         
30764         this.verticalMeasureColumns();
30765         
30766     },
30767     
30768     verticalMeasureColumns : function()
30769     {
30770         this.getContainerWidth();
30771         
30772 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30773 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30774 //            return;
30775 //        }
30776         
30777         var boxWidth = this.boxWidth + this.padWidth;
30778         
30779         if(this.containerWidth < this.boxWidth){
30780             boxWidth = this.containerWidth
30781         }
30782         
30783         var containerWidth = this.containerWidth;
30784         
30785         var cols = Math.floor(containerWidth / boxWidth);
30786         
30787         this.cols = Math.max( cols, 1 );
30788         
30789         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30790         
30791         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30792         
30793         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30794         
30795         this.colWidth = boxWidth + avail - this.padWidth;
30796         
30797         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30798         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30799     },
30800     
30801     horizontalMeasureColumns : function()
30802     {
30803         this.getContainerWidth();
30804         
30805         var boxWidth = this.boxWidth;
30806         
30807         if(this.containerWidth < boxWidth){
30808             boxWidth = this.containerWidth;
30809         }
30810         
30811         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30812         
30813         this.el.setHeight(boxWidth);
30814         
30815     },
30816     
30817     getContainerWidth : function()
30818     {
30819         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30820     },
30821     
30822     layoutItems : function( isInstant )
30823     {
30824         Roo.log(this.bricks);
30825         
30826         var items = Roo.apply([], this.bricks);
30827         
30828         if(this.isHorizontal){
30829             this._horizontalLayoutItems( items , isInstant );
30830             return;
30831         }
30832         
30833 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30834 //            this._verticalAlternativeLayoutItems( items , isInstant );
30835 //            return;
30836 //        }
30837         
30838         this._verticalLayoutItems( items , isInstant );
30839         
30840     },
30841     
30842     _verticalLayoutItems : function ( items , isInstant)
30843     {
30844         if ( !items || !items.length ) {
30845             return;
30846         }
30847         
30848         var standard = [
30849             ['xs', 'xs', 'xs', 'tall'],
30850             ['xs', 'xs', 'tall'],
30851             ['xs', 'xs', 'sm'],
30852             ['xs', 'xs', 'xs'],
30853             ['xs', 'tall'],
30854             ['xs', 'sm'],
30855             ['xs', 'xs'],
30856             ['xs'],
30857             
30858             ['sm', 'xs', 'xs'],
30859             ['sm', 'xs'],
30860             ['sm'],
30861             
30862             ['tall', 'xs', 'xs', 'xs'],
30863             ['tall', 'xs', 'xs'],
30864             ['tall', 'xs'],
30865             ['tall']
30866             
30867         ];
30868         
30869         var queue = [];
30870         
30871         var boxes = [];
30872         
30873         var box = [];
30874         
30875         Roo.each(items, function(item, k){
30876             
30877             switch (item.size) {
30878                 // these layouts take up a full box,
30879                 case 'md' :
30880                 case 'md-left' :
30881                 case 'md-right' :
30882                 case 'wide' :
30883                     
30884                     if(box.length){
30885                         boxes.push(box);
30886                         box = [];
30887                     }
30888                     
30889                     boxes.push([item]);
30890                     
30891                     break;
30892                     
30893                 case 'xs' :
30894                 case 'sm' :
30895                 case 'tall' :
30896                     
30897                     box.push(item);
30898                     
30899                     break;
30900                 default :
30901                     break;
30902                     
30903             }
30904             
30905         }, this);
30906         
30907         if(box.length){
30908             boxes.push(box);
30909             box = [];
30910         }
30911         
30912         var filterPattern = function(box, length)
30913         {
30914             if(!box.length){
30915                 return;
30916             }
30917             
30918             var match = false;
30919             
30920             var pattern = box.slice(0, length);
30921             
30922             var format = [];
30923             
30924             Roo.each(pattern, function(i){
30925                 format.push(i.size);
30926             }, this);
30927             
30928             Roo.each(standard, function(s){
30929                 
30930                 if(String(s) != String(format)){
30931                     return;
30932                 }
30933                 
30934                 match = true;
30935                 return false;
30936                 
30937             }, this);
30938             
30939             if(!match && length == 1){
30940                 return;
30941             }
30942             
30943             if(!match){
30944                 filterPattern(box, length - 1);
30945                 return;
30946             }
30947                 
30948             queue.push(pattern);
30949
30950             box = box.slice(length, box.length);
30951
30952             filterPattern(box, 4);
30953
30954             return;
30955             
30956         }
30957         
30958         Roo.each(boxes, function(box, k){
30959             
30960             if(!box.length){
30961                 return;
30962             }
30963             
30964             if(box.length == 1){
30965                 queue.push(box);
30966                 return;
30967             }
30968             
30969             filterPattern(box, 4);
30970             
30971         }, this);
30972         
30973         this._processVerticalLayoutQueue( queue, isInstant );
30974         
30975     },
30976     
30977 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30978 //    {
30979 //        if ( !items || !items.length ) {
30980 //            return;
30981 //        }
30982 //
30983 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30984 //        
30985 //    },
30986     
30987     _horizontalLayoutItems : function ( items , isInstant)
30988     {
30989         if ( !items || !items.length || items.length < 3) {
30990             return;
30991         }
30992         
30993         items.reverse();
30994         
30995         var eItems = items.slice(0, 3);
30996         
30997         items = items.slice(3, items.length);
30998         
30999         var standard = [
31000             ['xs', 'xs', 'xs', 'wide'],
31001             ['xs', 'xs', 'wide'],
31002             ['xs', 'xs', 'sm'],
31003             ['xs', 'xs', 'xs'],
31004             ['xs', 'wide'],
31005             ['xs', 'sm'],
31006             ['xs', 'xs'],
31007             ['xs'],
31008             
31009             ['sm', 'xs', 'xs'],
31010             ['sm', 'xs'],
31011             ['sm'],
31012             
31013             ['wide', 'xs', 'xs', 'xs'],
31014             ['wide', 'xs', 'xs'],
31015             ['wide', 'xs'],
31016             ['wide'],
31017             
31018             ['wide-thin']
31019         ];
31020         
31021         var queue = [];
31022         
31023         var boxes = [];
31024         
31025         var box = [];
31026         
31027         Roo.each(items, function(item, k){
31028             
31029             switch (item.size) {
31030                 case 'md' :
31031                 case 'md-left' :
31032                 case 'md-right' :
31033                 case 'tall' :
31034                     
31035                     if(box.length){
31036                         boxes.push(box);
31037                         box = [];
31038                     }
31039                     
31040                     boxes.push([item]);
31041                     
31042                     break;
31043                     
31044                 case 'xs' :
31045                 case 'sm' :
31046                 case 'wide' :
31047                 case 'wide-thin' :
31048                     
31049                     box.push(item);
31050                     
31051                     break;
31052                 default :
31053                     break;
31054                     
31055             }
31056             
31057         }, this);
31058         
31059         if(box.length){
31060             boxes.push(box);
31061             box = [];
31062         }
31063         
31064         var filterPattern = function(box, length)
31065         {
31066             if(!box.length){
31067                 return;
31068             }
31069             
31070             var match = false;
31071             
31072             var pattern = box.slice(0, length);
31073             
31074             var format = [];
31075             
31076             Roo.each(pattern, function(i){
31077                 format.push(i.size);
31078             }, this);
31079             
31080             Roo.each(standard, function(s){
31081                 
31082                 if(String(s) != String(format)){
31083                     return;
31084                 }
31085                 
31086                 match = true;
31087                 return false;
31088                 
31089             }, this);
31090             
31091             if(!match && length == 1){
31092                 return;
31093             }
31094             
31095             if(!match){
31096                 filterPattern(box, length - 1);
31097                 return;
31098             }
31099                 
31100             queue.push(pattern);
31101
31102             box = box.slice(length, box.length);
31103
31104             filterPattern(box, 4);
31105
31106             return;
31107             
31108         }
31109         
31110         Roo.each(boxes, function(box, k){
31111             
31112             if(!box.length){
31113                 return;
31114             }
31115             
31116             if(box.length == 1){
31117                 queue.push(box);
31118                 return;
31119             }
31120             
31121             filterPattern(box, 4);
31122             
31123         }, this);
31124         
31125         
31126         var prune = [];
31127         
31128         var pos = this.el.getBox(true);
31129         
31130         var minX = pos.x;
31131         
31132         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31133         
31134         var hit_end = false;
31135         
31136         Roo.each(queue, function(box){
31137             
31138             if(hit_end){
31139                 
31140                 Roo.each(box, function(b){
31141                 
31142                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31143                     b.el.hide();
31144
31145                 }, this);
31146
31147                 return;
31148             }
31149             
31150             var mx = 0;
31151             
31152             Roo.each(box, function(b){
31153                 
31154                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31155                 b.el.show();
31156
31157                 mx = Math.max(mx, b.x);
31158                 
31159             }, this);
31160             
31161             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31162             
31163             if(maxX < minX){
31164                 
31165                 Roo.each(box, function(b){
31166                 
31167                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31168                     b.el.hide();
31169                     
31170                 }, this);
31171                 
31172                 hit_end = true;
31173                 
31174                 return;
31175             }
31176             
31177             prune.push(box);
31178             
31179         }, this);
31180         
31181         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31182     },
31183     
31184     /** Sets position of item in DOM
31185     * @param {Element} item
31186     * @param {Number} x - horizontal position
31187     * @param {Number} y - vertical position
31188     * @param {Boolean} isInstant - disables transitions
31189     */
31190     _processVerticalLayoutQueue : function( queue, isInstant )
31191     {
31192         var pos = this.el.getBox(true);
31193         var x = pos.x;
31194         var y = pos.y;
31195         var maxY = [];
31196         
31197         for (var i = 0; i < this.cols; i++){
31198             maxY[i] = pos.y;
31199         }
31200         
31201         Roo.each(queue, function(box, k){
31202             
31203             var col = k % this.cols;
31204             
31205             Roo.each(box, function(b,kk){
31206                 
31207                 b.el.position('absolute');
31208                 
31209                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31210                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31211                 
31212                 if(b.size == 'md-left' || b.size == 'md-right'){
31213                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31214                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31215                 }
31216                 
31217                 b.el.setWidth(width);
31218                 b.el.setHeight(height);
31219                 // iframe?
31220                 b.el.select('iframe',true).setSize(width,height);
31221                 
31222             }, this);
31223             
31224             for (var i = 0; i < this.cols; i++){
31225                 
31226                 if(maxY[i] < maxY[col]){
31227                     col = i;
31228                     continue;
31229                 }
31230                 
31231                 col = Math.min(col, i);
31232                 
31233             }
31234             
31235             x = pos.x + col * (this.colWidth + this.padWidth);
31236             
31237             y = maxY[col];
31238             
31239             var positions = [];
31240             
31241             switch (box.length){
31242                 case 1 :
31243                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31244                     break;
31245                 case 2 :
31246                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31247                     break;
31248                 case 3 :
31249                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31250                     break;
31251                 case 4 :
31252                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31253                     break;
31254                 default :
31255                     break;
31256             }
31257             
31258             Roo.each(box, function(b,kk){
31259                 
31260                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31261                 
31262                 var sz = b.el.getSize();
31263                 
31264                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31265                 
31266             }, this);
31267             
31268         }, this);
31269         
31270         var mY = 0;
31271         
31272         for (var i = 0; i < this.cols; i++){
31273             mY = Math.max(mY, maxY[i]);
31274         }
31275         
31276         this.el.setHeight(mY - pos.y);
31277         
31278     },
31279     
31280 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31281 //    {
31282 //        var pos = this.el.getBox(true);
31283 //        var x = pos.x;
31284 //        var y = pos.y;
31285 //        var maxX = pos.right;
31286 //        
31287 //        var maxHeight = 0;
31288 //        
31289 //        Roo.each(items, function(item, k){
31290 //            
31291 //            var c = k % 2;
31292 //            
31293 //            item.el.position('absolute');
31294 //                
31295 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31296 //
31297 //            item.el.setWidth(width);
31298 //
31299 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31300 //
31301 //            item.el.setHeight(height);
31302 //            
31303 //            if(c == 0){
31304 //                item.el.setXY([x, y], isInstant ? false : true);
31305 //            } else {
31306 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31307 //            }
31308 //            
31309 //            y = y + height + this.alternativePadWidth;
31310 //            
31311 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31312 //            
31313 //        }, this);
31314 //        
31315 //        this.el.setHeight(maxHeight);
31316 //        
31317 //    },
31318     
31319     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31320     {
31321         var pos = this.el.getBox(true);
31322         
31323         var minX = pos.x;
31324         var minY = pos.y;
31325         
31326         var maxX = pos.right;
31327         
31328         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31329         
31330         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31331         
31332         Roo.each(queue, function(box, k){
31333             
31334             Roo.each(box, function(b, kk){
31335                 
31336                 b.el.position('absolute');
31337                 
31338                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31339                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31340                 
31341                 if(b.size == 'md-left' || b.size == 'md-right'){
31342                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31343                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31344                 }
31345                 
31346                 b.el.setWidth(width);
31347                 b.el.setHeight(height);
31348                 
31349             }, this);
31350             
31351             if(!box.length){
31352                 return;
31353             }
31354             
31355             var positions = [];
31356             
31357             switch (box.length){
31358                 case 1 :
31359                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31360                     break;
31361                 case 2 :
31362                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31363                     break;
31364                 case 3 :
31365                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31366                     break;
31367                 case 4 :
31368                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31369                     break;
31370                 default :
31371                     break;
31372             }
31373             
31374             Roo.each(box, function(b,kk){
31375                 
31376                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31377                 
31378                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31379                 
31380             }, this);
31381             
31382         }, this);
31383         
31384     },
31385     
31386     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31387     {
31388         Roo.each(eItems, function(b,k){
31389             
31390             b.size = (k == 0) ? 'sm' : 'xs';
31391             b.x = (k == 0) ? 2 : 1;
31392             b.y = (k == 0) ? 2 : 1;
31393             
31394             b.el.position('absolute');
31395             
31396             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31397                 
31398             b.el.setWidth(width);
31399             
31400             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31401             
31402             b.el.setHeight(height);
31403             
31404         }, this);
31405
31406         var positions = [];
31407         
31408         positions.push({
31409             x : maxX - this.unitWidth * 2 - this.gutter,
31410             y : minY
31411         });
31412         
31413         positions.push({
31414             x : maxX - this.unitWidth,
31415             y : minY + (this.unitWidth + this.gutter) * 2
31416         });
31417         
31418         positions.push({
31419             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31420             y : minY
31421         });
31422         
31423         Roo.each(eItems, function(b,k){
31424             
31425             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31426
31427         }, this);
31428         
31429     },
31430     
31431     getVerticalOneBoxColPositions : function(x, y, box)
31432     {
31433         var pos = [];
31434         
31435         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31436         
31437         if(box[0].size == 'md-left'){
31438             rand = 0;
31439         }
31440         
31441         if(box[0].size == 'md-right'){
31442             rand = 1;
31443         }
31444         
31445         pos.push({
31446             x : x + (this.unitWidth + this.gutter) * rand,
31447             y : y
31448         });
31449         
31450         return pos;
31451     },
31452     
31453     getVerticalTwoBoxColPositions : function(x, y, box)
31454     {
31455         var pos = [];
31456         
31457         if(box[0].size == 'xs'){
31458             
31459             pos.push({
31460                 x : x,
31461                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31462             });
31463
31464             pos.push({
31465                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31466                 y : y
31467             });
31468             
31469             return pos;
31470             
31471         }
31472         
31473         pos.push({
31474             x : x,
31475             y : y
31476         });
31477
31478         pos.push({
31479             x : x + (this.unitWidth + this.gutter) * 2,
31480             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31481         });
31482         
31483         return pos;
31484         
31485     },
31486     
31487     getVerticalThreeBoxColPositions : function(x, y, box)
31488     {
31489         var pos = [];
31490         
31491         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31492             
31493             pos.push({
31494                 x : x,
31495                 y : y
31496             });
31497
31498             pos.push({
31499                 x : x + (this.unitWidth + this.gutter) * 1,
31500                 y : y
31501             });
31502             
31503             pos.push({
31504                 x : x + (this.unitWidth + this.gutter) * 2,
31505                 y : y
31506             });
31507             
31508             return pos;
31509             
31510         }
31511         
31512         if(box[0].size == 'xs' && box[1].size == 'xs'){
31513             
31514             pos.push({
31515                 x : x,
31516                 y : y
31517             });
31518
31519             pos.push({
31520                 x : x,
31521                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31522             });
31523             
31524             pos.push({
31525                 x : x + (this.unitWidth + this.gutter) * 1,
31526                 y : y
31527             });
31528             
31529             return pos;
31530             
31531         }
31532         
31533         pos.push({
31534             x : x,
31535             y : y
31536         });
31537
31538         pos.push({
31539             x : x + (this.unitWidth + this.gutter) * 2,
31540             y : y
31541         });
31542
31543         pos.push({
31544             x : x + (this.unitWidth + this.gutter) * 2,
31545             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31546         });
31547             
31548         return pos;
31549         
31550     },
31551     
31552     getVerticalFourBoxColPositions : function(x, y, box)
31553     {
31554         var pos = [];
31555         
31556         if(box[0].size == 'xs'){
31557             
31558             pos.push({
31559                 x : x,
31560                 y : y
31561             });
31562
31563             pos.push({
31564                 x : x,
31565                 y : y + (this.unitHeight + this.gutter) * 1
31566             });
31567             
31568             pos.push({
31569                 x : x,
31570                 y : y + (this.unitHeight + this.gutter) * 2
31571             });
31572             
31573             pos.push({
31574                 x : x + (this.unitWidth + this.gutter) * 1,
31575                 y : y
31576             });
31577             
31578             return pos;
31579             
31580         }
31581         
31582         pos.push({
31583             x : x,
31584             y : y
31585         });
31586
31587         pos.push({
31588             x : x + (this.unitWidth + this.gutter) * 2,
31589             y : y
31590         });
31591
31592         pos.push({
31593             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31594             y : y + (this.unitHeight + this.gutter) * 1
31595         });
31596
31597         pos.push({
31598             x : x + (this.unitWidth + this.gutter) * 2,
31599             y : y + (this.unitWidth + this.gutter) * 2
31600         });
31601
31602         return pos;
31603         
31604     },
31605     
31606     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31607     {
31608         var pos = [];
31609         
31610         if(box[0].size == 'md-left'){
31611             pos.push({
31612                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31613                 y : minY
31614             });
31615             
31616             return pos;
31617         }
31618         
31619         if(box[0].size == 'md-right'){
31620             pos.push({
31621                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31622                 y : minY + (this.unitWidth + this.gutter) * 1
31623             });
31624             
31625             return pos;
31626         }
31627         
31628         var rand = Math.floor(Math.random() * (4 - box[0].y));
31629         
31630         pos.push({
31631             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31632             y : minY + (this.unitWidth + this.gutter) * rand
31633         });
31634         
31635         return pos;
31636         
31637     },
31638     
31639     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31640     {
31641         var pos = [];
31642         
31643         if(box[0].size == 'xs'){
31644             
31645             pos.push({
31646                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31647                 y : minY
31648             });
31649
31650             pos.push({
31651                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31652                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31653             });
31654             
31655             return pos;
31656             
31657         }
31658         
31659         pos.push({
31660             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31661             y : minY
31662         });
31663
31664         pos.push({
31665             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31666             y : minY + (this.unitWidth + this.gutter) * 2
31667         });
31668         
31669         return pos;
31670         
31671     },
31672     
31673     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31674     {
31675         var pos = [];
31676         
31677         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31678             
31679             pos.push({
31680                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31681                 y : minY
31682             });
31683
31684             pos.push({
31685                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31686                 y : minY + (this.unitWidth + this.gutter) * 1
31687             });
31688             
31689             pos.push({
31690                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31691                 y : minY + (this.unitWidth + this.gutter) * 2
31692             });
31693             
31694             return pos;
31695             
31696         }
31697         
31698         if(box[0].size == 'xs' && box[1].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[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31707                 y : minY
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) * 1
31713             });
31714             
31715             return pos;
31716             
31717         }
31718         
31719         pos.push({
31720             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31721             y : minY
31722         });
31723
31724         pos.push({
31725             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31726             y : minY + (this.unitWidth + this.gutter) * 2
31727         });
31728
31729         pos.push({
31730             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31731             y : minY + (this.unitWidth + this.gutter) * 2
31732         });
31733             
31734         return pos;
31735         
31736     },
31737     
31738     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31739     {
31740         var pos = [];
31741         
31742         if(box[0].size == 'xs'){
31743             
31744             pos.push({
31745                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31746                 y : minY
31747             });
31748
31749             pos.push({
31750                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31751                 y : minY
31752             });
31753             
31754             pos.push({
31755                 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),
31756                 y : minY
31757             });
31758             
31759             pos.push({
31760                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31761                 y : minY + (this.unitWidth + this.gutter) * 1
31762             });
31763             
31764             return pos;
31765             
31766         }
31767         
31768         pos.push({
31769             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31770             y : minY
31771         });
31772         
31773         pos.push({
31774             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31775             y : minY + (this.unitWidth + this.gutter) * 2
31776         });
31777         
31778         pos.push({
31779             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31780             y : minY + (this.unitWidth + this.gutter) * 2
31781         });
31782         
31783         pos.push({
31784             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),
31785             y : minY + (this.unitWidth + this.gutter) * 2
31786         });
31787
31788         return pos;
31789         
31790     },
31791     
31792     /**
31793     * remove a Masonry Brick
31794     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31795     */
31796     removeBrick : function(brick_id)
31797     {
31798         if (!brick_id) {
31799             return;
31800         }
31801         
31802         for (var i = 0; i<this.bricks.length; i++) {
31803             if (this.bricks[i].id == brick_id) {
31804                 this.bricks.splice(i,1);
31805                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31806                 this.initial();
31807             }
31808         }
31809     },
31810     
31811     /**
31812     * adds a Masonry Brick
31813     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31814     */
31815     addBrick : function(cfg)
31816     {
31817         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31818         //this.register(cn);
31819         cn.parentId = this.id;
31820         cn.onRender(this.el, null);
31821         return cn;
31822     },
31823     
31824     /**
31825     * register a Masonry Brick
31826     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31827     */
31828     
31829     register : function(brick)
31830     {
31831         this.bricks.push(brick);
31832         brick.masonryId = this.id;
31833     },
31834     
31835     /**
31836     * clear all the Masonry Brick
31837     */
31838     clearAll : function()
31839     {
31840         this.bricks = [];
31841         //this.getChildContainer().dom.innerHTML = "";
31842         this.el.dom.innerHTML = '';
31843     },
31844     
31845     getSelected : function()
31846     {
31847         if (!this.selectedBrick) {
31848             return false;
31849         }
31850         
31851         return this.selectedBrick;
31852     }
31853 });
31854
31855 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31856     
31857     groups: {},
31858      /**
31859     * register a Masonry Layout
31860     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31861     */
31862     
31863     register : function(layout)
31864     {
31865         this.groups[layout.id] = layout;
31866     },
31867     /**
31868     * fetch a  Masonry Layout based on the masonry layout ID
31869     * @param {string} the masonry layout to add
31870     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31871     */
31872     
31873     get: function(layout_id) {
31874         if (typeof(this.groups[layout_id]) == 'undefined') {
31875             return false;
31876         }
31877         return this.groups[layout_id] ;
31878     }
31879     
31880     
31881     
31882 });
31883
31884  
31885
31886  /**
31887  *
31888  * This is based on 
31889  * http://masonry.desandro.com
31890  *
31891  * The idea is to render all the bricks based on vertical width...
31892  *
31893  * The original code extends 'outlayer' - we might need to use that....
31894  * 
31895  */
31896
31897
31898 /**
31899  * @class Roo.bootstrap.LayoutMasonryAuto
31900  * @extends Roo.bootstrap.Component
31901  * Bootstrap Layout Masonry class
31902  * 
31903  * @constructor
31904  * Create a new Element
31905  * @param {Object} config The config object
31906  */
31907
31908 Roo.bootstrap.LayoutMasonryAuto = function(config){
31909     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31910 };
31911
31912 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31913     
31914       /**
31915      * @cfg {Boolean} isFitWidth  - resize the width..
31916      */   
31917     isFitWidth : false,  // options..
31918     /**
31919      * @cfg {Boolean} isOriginLeft = left align?
31920      */   
31921     isOriginLeft : true,
31922     /**
31923      * @cfg {Boolean} isOriginTop = top align?
31924      */   
31925     isOriginTop : false,
31926     /**
31927      * @cfg {Boolean} isLayoutInstant = no animation?
31928      */   
31929     isLayoutInstant : false, // needed?
31930     /**
31931      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31932      */   
31933     isResizingContainer : true,
31934     /**
31935      * @cfg {Number} columnWidth  width of the columns 
31936      */   
31937     
31938     columnWidth : 0,
31939     
31940     /**
31941      * @cfg {Number} maxCols maximum number of columns
31942      */   
31943     
31944     maxCols: 0,
31945     /**
31946      * @cfg {Number} padHeight padding below box..
31947      */   
31948     
31949     padHeight : 10, 
31950     
31951     /**
31952      * @cfg {Boolean} isAutoInitial defalut true
31953      */   
31954     
31955     isAutoInitial : true, 
31956     
31957     // private?
31958     gutter : 0,
31959     
31960     containerWidth: 0,
31961     initialColumnWidth : 0,
31962     currentSize : null,
31963     
31964     colYs : null, // array.
31965     maxY : 0,
31966     padWidth: 10,
31967     
31968     
31969     tag: 'div',
31970     cls: '',
31971     bricks: null, //CompositeElement
31972     cols : 0, // array?
31973     // element : null, // wrapped now this.el
31974     _isLayoutInited : null, 
31975     
31976     
31977     getAutoCreate : function(){
31978         
31979         var cfg = {
31980             tag: this.tag,
31981             cls: 'blog-masonary-wrapper ' + this.cls,
31982             cn : {
31983                 cls : 'mas-boxes masonary'
31984             }
31985         };
31986         
31987         return cfg;
31988     },
31989     
31990     getChildContainer: function( )
31991     {
31992         if (this.boxesEl) {
31993             return this.boxesEl;
31994         }
31995         
31996         this.boxesEl = this.el.select('.mas-boxes').first();
31997         
31998         return this.boxesEl;
31999     },
32000     
32001     
32002     initEvents : function()
32003     {
32004         var _this = this;
32005         
32006         if(this.isAutoInitial){
32007             Roo.log('hook children rendered');
32008             this.on('childrenrendered', function() {
32009                 Roo.log('children rendered');
32010                 _this.initial();
32011             } ,this);
32012         }
32013         
32014     },
32015     
32016     initial : function()
32017     {
32018         this.reloadItems();
32019
32020         this.currentSize = this.el.getBox(true);
32021
32022         /// was window resize... - let's see if this works..
32023         Roo.EventManager.onWindowResize(this.resize, this); 
32024
32025         if(!this.isAutoInitial){
32026             this.layout();
32027             return;
32028         }
32029         
32030         this.layout.defer(500,this);
32031     },
32032     
32033     reloadItems: function()
32034     {
32035         this.bricks = this.el.select('.masonry-brick', true);
32036         
32037         this.bricks.each(function(b) {
32038             //Roo.log(b.getSize());
32039             if (!b.attr('originalwidth')) {
32040                 b.attr('originalwidth',  b.getSize().width);
32041             }
32042             
32043         });
32044         
32045         Roo.log(this.bricks.elements.length);
32046     },
32047     
32048     resize : function()
32049     {
32050         Roo.log('resize');
32051         var cs = this.el.getBox(true);
32052         
32053         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32054             Roo.log("no change in with or X");
32055             return;
32056         }
32057         this.currentSize = cs;
32058         this.layout();
32059     },
32060     
32061     layout : function()
32062     {
32063          Roo.log('layout');
32064         this._resetLayout();
32065         //this._manageStamps();
32066       
32067         // don't animate first layout
32068         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32069         this.layoutItems( isInstant );
32070       
32071         // flag for initalized
32072         this._isLayoutInited = true;
32073     },
32074     
32075     layoutItems : function( isInstant )
32076     {
32077         //var items = this._getItemsForLayout( this.items );
32078         // original code supports filtering layout items.. we just ignore it..
32079         
32080         this._layoutItems( this.bricks , isInstant );
32081       
32082         this._postLayout();
32083     },
32084     _layoutItems : function ( items , isInstant)
32085     {
32086        //this.fireEvent( 'layout', this, items );
32087     
32088
32089         if ( !items || !items.elements.length ) {
32090           // no items, emit event with empty array
32091             return;
32092         }
32093
32094         var queue = [];
32095         items.each(function(item) {
32096             Roo.log("layout item");
32097             Roo.log(item);
32098             // get x/y object from method
32099             var position = this._getItemLayoutPosition( item );
32100             // enqueue
32101             position.item = item;
32102             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32103             queue.push( position );
32104         }, this);
32105       
32106         this._processLayoutQueue( queue );
32107     },
32108     /** Sets position of item in DOM
32109     * @param {Element} item
32110     * @param {Number} x - horizontal position
32111     * @param {Number} y - vertical position
32112     * @param {Boolean} isInstant - disables transitions
32113     */
32114     _processLayoutQueue : function( queue )
32115     {
32116         for ( var i=0, len = queue.length; i < len; i++ ) {
32117             var obj = queue[i];
32118             obj.item.position('absolute');
32119             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32120         }
32121     },
32122       
32123     
32124     /**
32125     * Any logic you want to do after each layout,
32126     * i.e. size the container
32127     */
32128     _postLayout : function()
32129     {
32130         this.resizeContainer();
32131     },
32132     
32133     resizeContainer : function()
32134     {
32135         if ( !this.isResizingContainer ) {
32136             return;
32137         }
32138         var size = this._getContainerSize();
32139         if ( size ) {
32140             this.el.setSize(size.width,size.height);
32141             this.boxesEl.setSize(size.width,size.height);
32142         }
32143     },
32144     
32145     
32146     
32147     _resetLayout : function()
32148     {
32149         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32150         this.colWidth = this.el.getWidth();
32151         //this.gutter = this.el.getWidth(); 
32152         
32153         this.measureColumns();
32154
32155         // reset column Y
32156         var i = this.cols;
32157         this.colYs = [];
32158         while (i--) {
32159             this.colYs.push( 0 );
32160         }
32161     
32162         this.maxY = 0;
32163     },
32164
32165     measureColumns : function()
32166     {
32167         this.getContainerWidth();
32168       // if columnWidth is 0, default to outerWidth of first item
32169         if ( !this.columnWidth ) {
32170             var firstItem = this.bricks.first();
32171             Roo.log(firstItem);
32172             this.columnWidth  = this.containerWidth;
32173             if (firstItem && firstItem.attr('originalwidth') ) {
32174                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32175             }
32176             // columnWidth fall back to item of first element
32177             Roo.log("set column width?");
32178                         this.initialColumnWidth = this.columnWidth  ;
32179
32180             // if first elem has no width, default to size of container
32181             
32182         }
32183         
32184         
32185         if (this.initialColumnWidth) {
32186             this.columnWidth = this.initialColumnWidth;
32187         }
32188         
32189         
32190             
32191         // column width is fixed at the top - however if container width get's smaller we should
32192         // reduce it...
32193         
32194         // this bit calcs how man columns..
32195             
32196         var columnWidth = this.columnWidth += this.gutter;
32197       
32198         // calculate columns
32199         var containerWidth = this.containerWidth + this.gutter;
32200         
32201         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32202         // fix rounding errors, typically with gutters
32203         var excess = columnWidth - containerWidth % columnWidth;
32204         
32205         
32206         // if overshoot is less than a pixel, round up, otherwise floor it
32207         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32208         cols = Math[ mathMethod ]( cols );
32209         this.cols = Math.max( cols, 1 );
32210         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32211         
32212          // padding positioning..
32213         var totalColWidth = this.cols * this.columnWidth;
32214         var padavail = this.containerWidth - totalColWidth;
32215         // so for 2 columns - we need 3 'pads'
32216         
32217         var padNeeded = (1+this.cols) * this.padWidth;
32218         
32219         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32220         
32221         this.columnWidth += padExtra
32222         //this.padWidth = Math.floor(padavail /  ( this.cols));
32223         
32224         // adjust colum width so that padding is fixed??
32225         
32226         // we have 3 columns ... total = width * 3
32227         // we have X left over... that should be used by 
32228         
32229         //if (this.expandC) {
32230             
32231         //}
32232         
32233         
32234         
32235     },
32236     
32237     getContainerWidth : function()
32238     {
32239        /* // container is parent if fit width
32240         var container = this.isFitWidth ? this.element.parentNode : this.element;
32241         // check that this.size and size are there
32242         // IE8 triggers resize on body size change, so they might not be
32243         
32244         var size = getSize( container );  //FIXME
32245         this.containerWidth = size && size.innerWidth; //FIXME
32246         */
32247          
32248         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32249         
32250     },
32251     
32252     _getItemLayoutPosition : function( item )  // what is item?
32253     {
32254         // we resize the item to our columnWidth..
32255       
32256         item.setWidth(this.columnWidth);
32257         item.autoBoxAdjust  = false;
32258         
32259         var sz = item.getSize();
32260  
32261         // how many columns does this brick span
32262         var remainder = this.containerWidth % this.columnWidth;
32263         
32264         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32265         // round if off by 1 pixel, otherwise use ceil
32266         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32267         colSpan = Math.min( colSpan, this.cols );
32268         
32269         // normally this should be '1' as we dont' currently allow multi width columns..
32270         
32271         var colGroup = this._getColGroup( colSpan );
32272         // get the minimum Y value from the columns
32273         var minimumY = Math.min.apply( Math, colGroup );
32274         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32275         
32276         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32277          
32278         // position the brick
32279         var position = {
32280             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32281             y: this.currentSize.y + minimumY + this.padHeight
32282         };
32283         
32284         Roo.log(position);
32285         // apply setHeight to necessary columns
32286         var setHeight = minimumY + sz.height + this.padHeight;
32287         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32288         
32289         var setSpan = this.cols + 1 - colGroup.length;
32290         for ( var i = 0; i < setSpan; i++ ) {
32291           this.colYs[ shortColIndex + i ] = setHeight ;
32292         }
32293       
32294         return position;
32295     },
32296     
32297     /**
32298      * @param {Number} colSpan - number of columns the element spans
32299      * @returns {Array} colGroup
32300      */
32301     _getColGroup : function( colSpan )
32302     {
32303         if ( colSpan < 2 ) {
32304           // if brick spans only one column, use all the column Ys
32305           return this.colYs;
32306         }
32307       
32308         var colGroup = [];
32309         // how many different places could this brick fit horizontally
32310         var groupCount = this.cols + 1 - colSpan;
32311         // for each group potential horizontal position
32312         for ( var i = 0; i < groupCount; i++ ) {
32313           // make an array of colY values for that one group
32314           var groupColYs = this.colYs.slice( i, i + colSpan );
32315           // and get the max value of the array
32316           colGroup[i] = Math.max.apply( Math, groupColYs );
32317         }
32318         return colGroup;
32319     },
32320     /*
32321     _manageStamp : function( stamp )
32322     {
32323         var stampSize =  stamp.getSize();
32324         var offset = stamp.getBox();
32325         // get the columns that this stamp affects
32326         var firstX = this.isOriginLeft ? offset.x : offset.right;
32327         var lastX = firstX + stampSize.width;
32328         var firstCol = Math.floor( firstX / this.columnWidth );
32329         firstCol = Math.max( 0, firstCol );
32330         
32331         var lastCol = Math.floor( lastX / this.columnWidth );
32332         // lastCol should not go over if multiple of columnWidth #425
32333         lastCol -= lastX % this.columnWidth ? 0 : 1;
32334         lastCol = Math.min( this.cols - 1, lastCol );
32335         
32336         // set colYs to bottom of the stamp
32337         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32338             stampSize.height;
32339             
32340         for ( var i = firstCol; i <= lastCol; i++ ) {
32341           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32342         }
32343     },
32344     */
32345     
32346     _getContainerSize : function()
32347     {
32348         this.maxY = Math.max.apply( Math, this.colYs );
32349         var size = {
32350             height: this.maxY
32351         };
32352       
32353         if ( this.isFitWidth ) {
32354             size.width = this._getContainerFitWidth();
32355         }
32356       
32357         return size;
32358     },
32359     
32360     _getContainerFitWidth : function()
32361     {
32362         var unusedCols = 0;
32363         // count unused columns
32364         var i = this.cols;
32365         while ( --i ) {
32366           if ( this.colYs[i] !== 0 ) {
32367             break;
32368           }
32369           unusedCols++;
32370         }
32371         // fit container to columns that have been used
32372         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32373     },
32374     
32375     needsResizeLayout : function()
32376     {
32377         var previousWidth = this.containerWidth;
32378         this.getContainerWidth();
32379         return previousWidth !== this.containerWidth;
32380     }
32381  
32382 });
32383
32384  
32385
32386  /*
32387  * - LGPL
32388  *
32389  * element
32390  * 
32391  */
32392
32393 /**
32394  * @class Roo.bootstrap.MasonryBrick
32395  * @extends Roo.bootstrap.Component
32396  * Bootstrap MasonryBrick class
32397  * 
32398  * @constructor
32399  * Create a new MasonryBrick
32400  * @param {Object} config The config object
32401  */
32402
32403 Roo.bootstrap.MasonryBrick = function(config){
32404     
32405     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32406     
32407     Roo.bootstrap.MasonryBrick.register(this);
32408     
32409     this.addEvents({
32410         // raw events
32411         /**
32412          * @event click
32413          * When a MasonryBrick is clcik
32414          * @param {Roo.bootstrap.MasonryBrick} this
32415          * @param {Roo.EventObject} e
32416          */
32417         "click" : true
32418     });
32419 };
32420
32421 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32422     
32423     /**
32424      * @cfg {String} title
32425      */   
32426     title : '',
32427     /**
32428      * @cfg {String} html
32429      */   
32430     html : '',
32431     /**
32432      * @cfg {String} bgimage
32433      */   
32434     bgimage : '',
32435     /**
32436      * @cfg {String} videourl
32437      */   
32438     videourl : '',
32439     /**
32440      * @cfg {String} cls
32441      */   
32442     cls : '',
32443     /**
32444      * @cfg {String} href
32445      */   
32446     href : '',
32447     /**
32448      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32449      */   
32450     size : 'xs',
32451     
32452     /**
32453      * @cfg {String} placetitle (center|bottom)
32454      */   
32455     placetitle : '',
32456     
32457     /**
32458      * @cfg {Boolean} isFitContainer defalut true
32459      */   
32460     isFitContainer : true, 
32461     
32462     /**
32463      * @cfg {Boolean} preventDefault defalut false
32464      */   
32465     preventDefault : false, 
32466     
32467     /**
32468      * @cfg {Boolean} inverse defalut false
32469      */   
32470     maskInverse : false, 
32471     
32472     getAutoCreate : function()
32473     {
32474         if(!this.isFitContainer){
32475             return this.getSplitAutoCreate();
32476         }
32477         
32478         var cls = 'masonry-brick masonry-brick-full';
32479         
32480         if(this.href.length){
32481             cls += ' masonry-brick-link';
32482         }
32483         
32484         if(this.bgimage.length){
32485             cls += ' masonry-brick-image';
32486         }
32487         
32488         if(this.maskInverse){
32489             cls += ' mask-inverse';
32490         }
32491         
32492         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32493             cls += ' enable-mask';
32494         }
32495         
32496         if(this.size){
32497             cls += ' masonry-' + this.size + '-brick';
32498         }
32499         
32500         if(this.placetitle.length){
32501             
32502             switch (this.placetitle) {
32503                 case 'center' :
32504                     cls += ' masonry-center-title';
32505                     break;
32506                 case 'bottom' :
32507                     cls += ' masonry-bottom-title';
32508                     break;
32509                 default:
32510                     break;
32511             }
32512             
32513         } else {
32514             if(!this.html.length && !this.bgimage.length){
32515                 cls += ' masonry-center-title';
32516             }
32517
32518             if(!this.html.length && this.bgimage.length){
32519                 cls += ' masonry-bottom-title';
32520             }
32521         }
32522         
32523         if(this.cls){
32524             cls += ' ' + this.cls;
32525         }
32526         
32527         var cfg = {
32528             tag: (this.href.length) ? 'a' : 'div',
32529             cls: cls,
32530             cn: [
32531                 {
32532                     tag: 'div',
32533                     cls: 'masonry-brick-mask'
32534                 },
32535                 {
32536                     tag: 'div',
32537                     cls: 'masonry-brick-paragraph',
32538                     cn: []
32539                 }
32540             ]
32541         };
32542         
32543         if(this.href.length){
32544             cfg.href = this.href;
32545         }
32546         
32547         var cn = cfg.cn[1].cn;
32548         
32549         if(this.title.length){
32550             cn.push({
32551                 tag: 'h4',
32552                 cls: 'masonry-brick-title',
32553                 html: this.title
32554             });
32555         }
32556         
32557         if(this.html.length){
32558             cn.push({
32559                 tag: 'p',
32560                 cls: 'masonry-brick-text',
32561                 html: this.html
32562             });
32563         }
32564         
32565         if (!this.title.length && !this.html.length) {
32566             cfg.cn[1].cls += ' hide';
32567         }
32568         
32569         if(this.bgimage.length){
32570             cfg.cn.push({
32571                 tag: 'img',
32572                 cls: 'masonry-brick-image-view',
32573                 src: this.bgimage
32574             });
32575         }
32576         
32577         if(this.videourl.length){
32578             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32579             // youtube support only?
32580             cfg.cn.push({
32581                 tag: 'iframe',
32582                 cls: 'masonry-brick-image-view',
32583                 src: vurl,
32584                 frameborder : 0,
32585                 allowfullscreen : true
32586             });
32587         }
32588         
32589         return cfg;
32590         
32591     },
32592     
32593     getSplitAutoCreate : function()
32594     {
32595         var cls = 'masonry-brick masonry-brick-split';
32596         
32597         if(this.href.length){
32598             cls += ' masonry-brick-link';
32599         }
32600         
32601         if(this.bgimage.length){
32602             cls += ' masonry-brick-image';
32603         }
32604         
32605         if(this.size){
32606             cls += ' masonry-' + this.size + '-brick';
32607         }
32608         
32609         switch (this.placetitle) {
32610             case 'center' :
32611                 cls += ' masonry-center-title';
32612                 break;
32613             case 'bottom' :
32614                 cls += ' masonry-bottom-title';
32615                 break;
32616             default:
32617                 if(!this.bgimage.length){
32618                     cls += ' masonry-center-title';
32619                 }
32620
32621                 if(this.bgimage.length){
32622                     cls += ' masonry-bottom-title';
32623                 }
32624                 break;
32625         }
32626         
32627         if(this.cls){
32628             cls += ' ' + this.cls;
32629         }
32630         
32631         var cfg = {
32632             tag: (this.href.length) ? 'a' : 'div',
32633             cls: cls,
32634             cn: [
32635                 {
32636                     tag: 'div',
32637                     cls: 'masonry-brick-split-head',
32638                     cn: [
32639                         {
32640                             tag: 'div',
32641                             cls: 'masonry-brick-paragraph',
32642                             cn: []
32643                         }
32644                     ]
32645                 },
32646                 {
32647                     tag: 'div',
32648                     cls: 'masonry-brick-split-body',
32649                     cn: []
32650                 }
32651             ]
32652         };
32653         
32654         if(this.href.length){
32655             cfg.href = this.href;
32656         }
32657         
32658         if(this.title.length){
32659             cfg.cn[0].cn[0].cn.push({
32660                 tag: 'h4',
32661                 cls: 'masonry-brick-title',
32662                 html: this.title
32663             });
32664         }
32665         
32666         if(this.html.length){
32667             cfg.cn[1].cn.push({
32668                 tag: 'p',
32669                 cls: 'masonry-brick-text',
32670                 html: this.html
32671             });
32672         }
32673
32674         if(this.bgimage.length){
32675             cfg.cn[0].cn.push({
32676                 tag: 'img',
32677                 cls: 'masonry-brick-image-view',
32678                 src: this.bgimage
32679             });
32680         }
32681         
32682         if(this.videourl.length){
32683             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32684             // youtube support only?
32685             cfg.cn[0].cn.cn.push({
32686                 tag: 'iframe',
32687                 cls: 'masonry-brick-image-view',
32688                 src: vurl,
32689                 frameborder : 0,
32690                 allowfullscreen : true
32691             });
32692         }
32693         
32694         return cfg;
32695     },
32696     
32697     initEvents: function() 
32698     {
32699         switch (this.size) {
32700             case 'xs' :
32701                 this.x = 1;
32702                 this.y = 1;
32703                 break;
32704             case 'sm' :
32705                 this.x = 2;
32706                 this.y = 2;
32707                 break;
32708             case 'md' :
32709             case 'md-left' :
32710             case 'md-right' :
32711                 this.x = 3;
32712                 this.y = 3;
32713                 break;
32714             case 'tall' :
32715                 this.x = 2;
32716                 this.y = 3;
32717                 break;
32718             case 'wide' :
32719                 this.x = 3;
32720                 this.y = 2;
32721                 break;
32722             case 'wide-thin' :
32723                 this.x = 3;
32724                 this.y = 1;
32725                 break;
32726                         
32727             default :
32728                 break;
32729         }
32730         
32731         if(Roo.isTouch){
32732             this.el.on('touchstart', this.onTouchStart, this);
32733             this.el.on('touchmove', this.onTouchMove, this);
32734             this.el.on('touchend', this.onTouchEnd, this);
32735             this.el.on('contextmenu', this.onContextMenu, this);
32736         } else {
32737             this.el.on('mouseenter'  ,this.enter, this);
32738             this.el.on('mouseleave', this.leave, this);
32739             this.el.on('click', this.onClick, this);
32740         }
32741         
32742         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32743             this.parent().bricks.push(this);   
32744         }
32745         
32746     },
32747     
32748     onClick: function(e, el)
32749     {
32750         var time = this.endTimer - this.startTimer;
32751         // Roo.log(e.preventDefault());
32752         if(Roo.isTouch){
32753             if(time > 1000){
32754                 e.preventDefault();
32755                 return;
32756             }
32757         }
32758         
32759         if(!this.preventDefault){
32760             return;
32761         }
32762         
32763         e.preventDefault();
32764         
32765         if (this.activeClass != '') {
32766             this.selectBrick();
32767         }
32768         
32769         this.fireEvent('click', this, e);
32770     },
32771     
32772     enter: function(e, el)
32773     {
32774         e.preventDefault();
32775         
32776         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32777             return;
32778         }
32779         
32780         if(this.bgimage.length && this.html.length){
32781             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32782         }
32783     },
32784     
32785     leave: function(e, el)
32786     {
32787         e.preventDefault();
32788         
32789         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32790             return;
32791         }
32792         
32793         if(this.bgimage.length && this.html.length){
32794             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32795         }
32796     },
32797     
32798     onTouchStart: function(e, el)
32799     {
32800 //        e.preventDefault();
32801         
32802         this.touchmoved = false;
32803         
32804         if(!this.isFitContainer){
32805             return;
32806         }
32807         
32808         if(!this.bgimage.length || !this.html.length){
32809             return;
32810         }
32811         
32812         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32813         
32814         this.timer = new Date().getTime();
32815         
32816     },
32817     
32818     onTouchMove: function(e, el)
32819     {
32820         this.touchmoved = true;
32821     },
32822     
32823     onContextMenu : function(e,el)
32824     {
32825         e.preventDefault();
32826         e.stopPropagation();
32827         return false;
32828     },
32829     
32830     onTouchEnd: function(e, el)
32831     {
32832 //        e.preventDefault();
32833         
32834         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32835         
32836             this.leave(e,el);
32837             
32838             return;
32839         }
32840         
32841         if(!this.bgimage.length || !this.html.length){
32842             
32843             if(this.href.length){
32844                 window.location.href = this.href;
32845             }
32846             
32847             return;
32848         }
32849         
32850         if(!this.isFitContainer){
32851             return;
32852         }
32853         
32854         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32855         
32856         window.location.href = this.href;
32857     },
32858     
32859     //selection on single brick only
32860     selectBrick : function() {
32861         
32862         if (!this.parentId) {
32863             return;
32864         }
32865         
32866         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32867         var index = m.selectedBrick.indexOf(this.id);
32868         
32869         if ( index > -1) {
32870             m.selectedBrick.splice(index,1);
32871             this.el.removeClass(this.activeClass);
32872             return;
32873         }
32874         
32875         for(var i = 0; i < m.selectedBrick.length; i++) {
32876             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32877             b.el.removeClass(b.activeClass);
32878         }
32879         
32880         m.selectedBrick = [];
32881         
32882         m.selectedBrick.push(this.id);
32883         this.el.addClass(this.activeClass);
32884         return;
32885     },
32886     
32887     isSelected : function(){
32888         return this.el.hasClass(this.activeClass);
32889         
32890     }
32891 });
32892
32893 Roo.apply(Roo.bootstrap.MasonryBrick, {
32894     
32895     //groups: {},
32896     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32897      /**
32898     * register a Masonry Brick
32899     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32900     */
32901     
32902     register : function(brick)
32903     {
32904         //this.groups[brick.id] = brick;
32905         this.groups.add(brick.id, brick);
32906     },
32907     /**
32908     * fetch a  masonry brick based on the masonry brick ID
32909     * @param {string} the masonry brick to add
32910     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32911     */
32912     
32913     get: function(brick_id) 
32914     {
32915         // if (typeof(this.groups[brick_id]) == 'undefined') {
32916         //     return false;
32917         // }
32918         // return this.groups[brick_id] ;
32919         
32920         if(this.groups.key(brick_id)) {
32921             return this.groups.key(brick_id);
32922         }
32923         
32924         return false;
32925     }
32926     
32927     
32928     
32929 });
32930
32931  /*
32932  * - LGPL
32933  *
32934  * element
32935  * 
32936  */
32937
32938 /**
32939  * @class Roo.bootstrap.Brick
32940  * @extends Roo.bootstrap.Component
32941  * Bootstrap Brick class
32942  * 
32943  * @constructor
32944  * Create a new Brick
32945  * @param {Object} config The config object
32946  */
32947
32948 Roo.bootstrap.Brick = function(config){
32949     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32950     
32951     this.addEvents({
32952         // raw events
32953         /**
32954          * @event click
32955          * When a Brick is click
32956          * @param {Roo.bootstrap.Brick} this
32957          * @param {Roo.EventObject} e
32958          */
32959         "click" : true
32960     });
32961 };
32962
32963 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32964     
32965     /**
32966      * @cfg {String} title
32967      */   
32968     title : '',
32969     /**
32970      * @cfg {String} html
32971      */   
32972     html : '',
32973     /**
32974      * @cfg {String} bgimage
32975      */   
32976     bgimage : '',
32977     /**
32978      * @cfg {String} cls
32979      */   
32980     cls : '',
32981     /**
32982      * @cfg {String} href
32983      */   
32984     href : '',
32985     /**
32986      * @cfg {String} video
32987      */   
32988     video : '',
32989     /**
32990      * @cfg {Boolean} square
32991      */   
32992     square : true,
32993     
32994     getAutoCreate : function()
32995     {
32996         var cls = 'roo-brick';
32997         
32998         if(this.href.length){
32999             cls += ' roo-brick-link';
33000         }
33001         
33002         if(this.bgimage.length){
33003             cls += ' roo-brick-image';
33004         }
33005         
33006         if(!this.html.length && !this.bgimage.length){
33007             cls += ' roo-brick-center-title';
33008         }
33009         
33010         if(!this.html.length && this.bgimage.length){
33011             cls += ' roo-brick-bottom-title';
33012         }
33013         
33014         if(this.cls){
33015             cls += ' ' + this.cls;
33016         }
33017         
33018         var cfg = {
33019             tag: (this.href.length) ? 'a' : 'div',
33020             cls: cls,
33021             cn: [
33022                 {
33023                     tag: 'div',
33024                     cls: 'roo-brick-paragraph',
33025                     cn: []
33026                 }
33027             ]
33028         };
33029         
33030         if(this.href.length){
33031             cfg.href = this.href;
33032         }
33033         
33034         var cn = cfg.cn[0].cn;
33035         
33036         if(this.title.length){
33037             cn.push({
33038                 tag: 'h4',
33039                 cls: 'roo-brick-title',
33040                 html: this.title
33041             });
33042         }
33043         
33044         if(this.html.length){
33045             cn.push({
33046                 tag: 'p',
33047                 cls: 'roo-brick-text',
33048                 html: this.html
33049             });
33050         } else {
33051             cn.cls += ' hide';
33052         }
33053         
33054         if(this.bgimage.length){
33055             cfg.cn.push({
33056                 tag: 'img',
33057                 cls: 'roo-brick-image-view',
33058                 src: this.bgimage
33059             });
33060         }
33061         
33062         return cfg;
33063     },
33064     
33065     initEvents: function() 
33066     {
33067         if(this.title.length || this.html.length){
33068             this.el.on('mouseenter'  ,this.enter, this);
33069             this.el.on('mouseleave', this.leave, this);
33070         }
33071         
33072         Roo.EventManager.onWindowResize(this.resize, this); 
33073         
33074         if(this.bgimage.length){
33075             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33076             this.imageEl.on('load', this.onImageLoad, this);
33077             return;
33078         }
33079         
33080         this.resize();
33081     },
33082     
33083     onImageLoad : function()
33084     {
33085         this.resize();
33086     },
33087     
33088     resize : function()
33089     {
33090         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33091         
33092         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33093         
33094         if(this.bgimage.length){
33095             var image = this.el.select('.roo-brick-image-view', true).first();
33096             
33097             image.setWidth(paragraph.getWidth());
33098             
33099             if(this.square){
33100                 image.setHeight(paragraph.getWidth());
33101             }
33102             
33103             this.el.setHeight(image.getHeight());
33104             paragraph.setHeight(image.getHeight());
33105             
33106         }
33107         
33108     },
33109     
33110     enter: function(e, el)
33111     {
33112         e.preventDefault();
33113         
33114         if(this.bgimage.length){
33115             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33116             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33117         }
33118     },
33119     
33120     leave: function(e, el)
33121     {
33122         e.preventDefault();
33123         
33124         if(this.bgimage.length){
33125             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33126             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33127         }
33128     }
33129     
33130 });
33131
33132  
33133
33134  /*
33135  * - LGPL
33136  *
33137  * Number field 
33138  */
33139
33140 /**
33141  * @class Roo.bootstrap.NumberField
33142  * @extends Roo.bootstrap.Input
33143  * Bootstrap NumberField class
33144  * 
33145  * 
33146  * 
33147  * 
33148  * @constructor
33149  * Create a new NumberField
33150  * @param {Object} config The config object
33151  */
33152
33153 Roo.bootstrap.NumberField = function(config){
33154     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33155 };
33156
33157 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33158     
33159     /**
33160      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33161      */
33162     allowDecimals : true,
33163     /**
33164      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33165      */
33166     decimalSeparator : ".",
33167     /**
33168      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33169      */
33170     decimalPrecision : 2,
33171     /**
33172      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33173      */
33174     allowNegative : true,
33175     
33176     /**
33177      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33178      */
33179     allowZero: true,
33180     /**
33181      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33182      */
33183     minValue : Number.NEGATIVE_INFINITY,
33184     /**
33185      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33186      */
33187     maxValue : Number.MAX_VALUE,
33188     /**
33189      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33190      */
33191     minText : "The minimum value for this field is {0}",
33192     /**
33193      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33194      */
33195     maxText : "The maximum value for this field is {0}",
33196     /**
33197      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33198      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33199      */
33200     nanText : "{0} is not a valid number",
33201     /**
33202      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33203      */
33204     castInt : true,
33205     /**
33206      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33207      */
33208     thousandsDelimiter : false,
33209     /**
33210      * @cfg {String} valueAlign alignment of value
33211      */
33212     valueAlign : "left",
33213
33214     getAutoCreate : function()
33215     {
33216         var hiddenInput = {
33217             tag: 'input',
33218             type: 'hidden',
33219             id: Roo.id(),
33220             cls: 'hidden-number-input'
33221         };
33222         
33223         if (this.name) {
33224             hiddenInput.name = this.name;
33225         }
33226         
33227         this.name = '';
33228         
33229         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33230         
33231         this.name = hiddenInput.name;
33232         
33233         if(cfg.cn.length > 0) {
33234             cfg.cn.push(hiddenInput);
33235         }
33236         
33237         return cfg;
33238     },
33239
33240     // private
33241     initEvents : function()
33242     {   
33243         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33244         
33245         var allowed = "0123456789";
33246         
33247         if(this.allowDecimals){
33248             allowed += this.decimalSeparator;
33249         }
33250         
33251         if(this.allowNegative){
33252             allowed += "-";
33253         }
33254         
33255         if(this.thousandsDelimiter) {
33256             allowed += ",";
33257         }
33258         
33259         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33260         
33261         var keyPress = function(e){
33262             
33263             var k = e.getKey();
33264             
33265             var c = e.getCharCode();
33266             
33267             if(
33268                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33269                     allowed.indexOf(String.fromCharCode(c)) === -1
33270             ){
33271                 e.stopEvent();
33272                 return;
33273             }
33274             
33275             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33276                 return;
33277             }
33278             
33279             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33280                 e.stopEvent();
33281             }
33282         };
33283         
33284         this.el.on("keypress", keyPress, this);
33285     },
33286     
33287     validateValue : function(value)
33288     {
33289         
33290         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33291             return false;
33292         }
33293         
33294         var num = this.parseValue(value);
33295         
33296         if(isNaN(num)){
33297             this.markInvalid(String.format(this.nanText, value));
33298             return false;
33299         }
33300         
33301         if(num < this.minValue){
33302             this.markInvalid(String.format(this.minText, this.minValue));
33303             return false;
33304         }
33305         
33306         if(num > this.maxValue){
33307             this.markInvalid(String.format(this.maxText, this.maxValue));
33308             return false;
33309         }
33310         
33311         return true;
33312     },
33313
33314     getValue : function()
33315     {
33316         var v = this.hiddenEl().getValue();
33317         
33318         return this.fixPrecision(this.parseValue(v));
33319     },
33320
33321     parseValue : function(value)
33322     {
33323         if(this.thousandsDelimiter) {
33324             value += "";
33325             r = new RegExp(",", "g");
33326             value = value.replace(r, "");
33327         }
33328         
33329         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33330         return isNaN(value) ? '' : value;
33331     },
33332
33333     fixPrecision : function(value)
33334     {
33335         if(this.thousandsDelimiter) {
33336             value += "";
33337             r = new RegExp(",", "g");
33338             value = value.replace(r, "");
33339         }
33340         
33341         var nan = isNaN(value);
33342         
33343         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33344             return nan ? '' : value;
33345         }
33346         return parseFloat(value).toFixed(this.decimalPrecision);
33347     },
33348
33349     setValue : function(v)
33350     {
33351         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33352         
33353         this.value = v;
33354         
33355         if(this.rendered){
33356             
33357             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33358             
33359             this.inputEl().dom.value = (v == '') ? '' :
33360                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33361             
33362             if(!this.allowZero && v === '0') {
33363                 this.hiddenEl().dom.value = '';
33364                 this.inputEl().dom.value = '';
33365             }
33366             
33367             this.validate();
33368         }
33369     },
33370
33371     decimalPrecisionFcn : function(v)
33372     {
33373         return Math.floor(v);
33374     },
33375
33376     beforeBlur : function()
33377     {
33378         if(!this.castInt){
33379             return;
33380         }
33381         
33382         var v = this.parseValue(this.getRawValue());
33383         
33384         if(v || v === 0){
33385             this.setValue(v);
33386         }
33387     },
33388     
33389     hiddenEl : function()
33390     {
33391         return this.el.select('input.hidden-number-input',true).first();
33392     }
33393     
33394 });
33395
33396  
33397
33398 /*
33399 * Licence: LGPL
33400 */
33401
33402 /**
33403  * @class Roo.bootstrap.DocumentSlider
33404  * @extends Roo.bootstrap.Component
33405  * Bootstrap DocumentSlider class
33406  * 
33407  * @constructor
33408  * Create a new DocumentViewer
33409  * @param {Object} config The config object
33410  */
33411
33412 Roo.bootstrap.DocumentSlider = function(config){
33413     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33414     
33415     this.files = [];
33416     
33417     this.addEvents({
33418         /**
33419          * @event initial
33420          * Fire after initEvent
33421          * @param {Roo.bootstrap.DocumentSlider} this
33422          */
33423         "initial" : true,
33424         /**
33425          * @event update
33426          * Fire after update
33427          * @param {Roo.bootstrap.DocumentSlider} this
33428          */
33429         "update" : true,
33430         /**
33431          * @event click
33432          * Fire after click
33433          * @param {Roo.bootstrap.DocumentSlider} this
33434          */
33435         "click" : true
33436     });
33437 };
33438
33439 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33440     
33441     files : false,
33442     
33443     indicator : 0,
33444     
33445     getAutoCreate : function()
33446     {
33447         var cfg = {
33448             tag : 'div',
33449             cls : 'roo-document-slider',
33450             cn : [
33451                 {
33452                     tag : 'div',
33453                     cls : 'roo-document-slider-header',
33454                     cn : [
33455                         {
33456                             tag : 'div',
33457                             cls : 'roo-document-slider-header-title'
33458                         }
33459                     ]
33460                 },
33461                 {
33462                     tag : 'div',
33463                     cls : 'roo-document-slider-body',
33464                     cn : [
33465                         {
33466                             tag : 'div',
33467                             cls : 'roo-document-slider-prev',
33468                             cn : [
33469                                 {
33470                                     tag : 'i',
33471                                     cls : 'fa fa-chevron-left'
33472                                 }
33473                             ]
33474                         },
33475                         {
33476                             tag : 'div',
33477                             cls : 'roo-document-slider-thumb',
33478                             cn : [
33479                                 {
33480                                     tag : 'img',
33481                                     cls : 'roo-document-slider-image'
33482                                 }
33483                             ]
33484                         },
33485                         {
33486                             tag : 'div',
33487                             cls : 'roo-document-slider-next',
33488                             cn : [
33489                                 {
33490                                     tag : 'i',
33491                                     cls : 'fa fa-chevron-right'
33492                                 }
33493                             ]
33494                         }
33495                     ]
33496                 }
33497             ]
33498         };
33499         
33500         return cfg;
33501     },
33502     
33503     initEvents : function()
33504     {
33505         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33506         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33507         
33508         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33509         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33510         
33511         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33512         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33513         
33514         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33515         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33516         
33517         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33518         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33519         
33520         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33521         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33522         
33523         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33524         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33525         
33526         this.thumbEl.on('click', this.onClick, this);
33527         
33528         this.prevIndicator.on('click', this.prev, this);
33529         
33530         this.nextIndicator.on('click', this.next, this);
33531         
33532     },
33533     
33534     initial : function()
33535     {
33536         if(this.files.length){
33537             this.indicator = 1;
33538             this.update()
33539         }
33540         
33541         this.fireEvent('initial', this);
33542     },
33543     
33544     update : function()
33545     {
33546         this.imageEl.attr('src', this.files[this.indicator - 1]);
33547         
33548         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33549         
33550         this.prevIndicator.show();
33551         
33552         if(this.indicator == 1){
33553             this.prevIndicator.hide();
33554         }
33555         
33556         this.nextIndicator.show();
33557         
33558         if(this.indicator == this.files.length){
33559             this.nextIndicator.hide();
33560         }
33561         
33562         this.thumbEl.scrollTo('top');
33563         
33564         this.fireEvent('update', this);
33565     },
33566     
33567     onClick : function(e)
33568     {
33569         e.preventDefault();
33570         
33571         this.fireEvent('click', this);
33572     },
33573     
33574     prev : function(e)
33575     {
33576         e.preventDefault();
33577         
33578         this.indicator = Math.max(1, this.indicator - 1);
33579         
33580         this.update();
33581     },
33582     
33583     next : function(e)
33584     {
33585         e.preventDefault();
33586         
33587         this.indicator = Math.min(this.files.length, this.indicator + 1);
33588         
33589         this.update();
33590     }
33591 });
33592 /*
33593  * - LGPL
33594  *
33595  * RadioSet
33596  *
33597  *
33598  */
33599
33600 /**
33601  * @class Roo.bootstrap.RadioSet
33602  * @extends Roo.bootstrap.Input
33603  * Bootstrap RadioSet class
33604  * @cfg {String} indicatorpos (left|right) default left
33605  * @cfg {Boolean} inline (true|false) inline the element (default true)
33606  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33607  * @constructor
33608  * Create a new RadioSet
33609  * @param {Object} config The config object
33610  */
33611
33612 Roo.bootstrap.RadioSet = function(config){
33613     
33614     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33615     
33616     this.radioes = [];
33617     
33618     Roo.bootstrap.RadioSet.register(this);
33619     
33620     this.addEvents({
33621         /**
33622         * @event check
33623         * Fires when the element is checked or unchecked.
33624         * @param {Roo.bootstrap.RadioSet} this This radio
33625         * @param {Roo.bootstrap.Radio} item The checked item
33626         */
33627        check : true,
33628        /**
33629         * @event click
33630         * Fires when the element is click.
33631         * @param {Roo.bootstrap.RadioSet} this This radio set
33632         * @param {Roo.bootstrap.Radio} item The checked item
33633         * @param {Roo.EventObject} e The event object
33634         */
33635        click : true
33636     });
33637     
33638 };
33639
33640 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33641
33642     radioes : false,
33643     
33644     inline : true,
33645     
33646     weight : '',
33647     
33648     indicatorpos : 'left',
33649     
33650     getAutoCreate : function()
33651     {
33652         var label = {
33653             tag : 'label',
33654             cls : 'roo-radio-set-label',
33655             cn : [
33656                 {
33657                     tag : 'span',
33658                     html : this.fieldLabel
33659                 }
33660             ]
33661         };
33662         
33663         if(this.indicatorpos == 'left'){
33664             label.cn.unshift({
33665                 tag : 'i',
33666                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33667                 tooltip : 'This field is required'
33668             });
33669         } else {
33670             label.cn.push({
33671                 tag : 'i',
33672                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33673                 tooltip : 'This field is required'
33674             });
33675         }
33676         
33677         var items = {
33678             tag : 'div',
33679             cls : 'roo-radio-set-items'
33680         };
33681         
33682         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33683         
33684         if (align === 'left' && this.fieldLabel.length) {
33685             
33686             items = {
33687                 cls : "roo-radio-set-right", 
33688                 cn: [
33689                     items
33690                 ]
33691             };
33692             
33693             if(this.labelWidth > 12){
33694                 label.style = "width: " + this.labelWidth + 'px';
33695             }
33696             
33697             if(this.labelWidth < 13 && this.labelmd == 0){
33698                 this.labelmd = this.labelWidth;
33699             }
33700             
33701             if(this.labellg > 0){
33702                 label.cls += ' col-lg-' + this.labellg;
33703                 items.cls += ' col-lg-' + (12 - this.labellg);
33704             }
33705             
33706             if(this.labelmd > 0){
33707                 label.cls += ' col-md-' + this.labelmd;
33708                 items.cls += ' col-md-' + (12 - this.labelmd);
33709             }
33710             
33711             if(this.labelsm > 0){
33712                 label.cls += ' col-sm-' + this.labelsm;
33713                 items.cls += ' col-sm-' + (12 - this.labelsm);
33714             }
33715             
33716             if(this.labelxs > 0){
33717                 label.cls += ' col-xs-' + this.labelxs;
33718                 items.cls += ' col-xs-' + (12 - this.labelxs);
33719             }
33720         }
33721         
33722         var cfg = {
33723             tag : 'div',
33724             cls : 'roo-radio-set',
33725             cn : [
33726                 {
33727                     tag : 'input',
33728                     cls : 'roo-radio-set-input',
33729                     type : 'hidden',
33730                     name : this.name,
33731                     value : this.value ? this.value :  ''
33732                 },
33733                 label,
33734                 items
33735             ]
33736         };
33737         
33738         if(this.weight.length){
33739             cfg.cls += ' roo-radio-' + this.weight;
33740         }
33741         
33742         if(this.inline) {
33743             cfg.cls += ' roo-radio-set-inline';
33744         }
33745         
33746         var settings=this;
33747         ['xs','sm','md','lg'].map(function(size){
33748             if (settings[size]) {
33749                 cfg.cls += ' col-' + size + '-' + settings[size];
33750             }
33751         });
33752         
33753         return cfg;
33754         
33755     },
33756
33757     initEvents : function()
33758     {
33759         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33760         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33761         
33762         if(!this.fieldLabel.length){
33763             this.labelEl.hide();
33764         }
33765         
33766         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33767         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33768         
33769         this.indicator = this.indicatorEl();
33770         
33771         if(this.indicator){
33772             this.indicator.addClass('invisible');
33773         }
33774         
33775         this.originalValue = this.getValue();
33776         
33777     },
33778     
33779     inputEl: function ()
33780     {
33781         return this.el.select('.roo-radio-set-input', true).first();
33782     },
33783     
33784     getChildContainer : function()
33785     {
33786         return this.itemsEl;
33787     },
33788     
33789     register : function(item)
33790     {
33791         this.radioes.push(item);
33792         
33793     },
33794     
33795     validate : function()
33796     {   
33797         if(this.getVisibilityEl().hasClass('hidden')){
33798             return true;
33799         }
33800         
33801         var valid = false;
33802         
33803         Roo.each(this.radioes, function(i){
33804             if(!i.checked){
33805                 return;
33806             }
33807             
33808             valid = true;
33809             return false;
33810         });
33811         
33812         if(this.allowBlank) {
33813             return true;
33814         }
33815         
33816         if(this.disabled || valid){
33817             this.markValid();
33818             return true;
33819         }
33820         
33821         this.markInvalid();
33822         return false;
33823         
33824     },
33825     
33826     markValid : function()
33827     {
33828         if(this.labelEl.isVisible(true)){
33829             this.indicatorEl().removeClass('visible');
33830             this.indicatorEl().addClass('invisible');
33831         }
33832         
33833         this.el.removeClass([this.invalidClass, this.validClass]);
33834         this.el.addClass(this.validClass);
33835         
33836         this.fireEvent('valid', this);
33837     },
33838     
33839     markInvalid : function(msg)
33840     {
33841         if(this.allowBlank || this.disabled){
33842             return;
33843         }
33844         
33845         if(this.labelEl.isVisible(true)){
33846             this.indicatorEl().removeClass('invisible');
33847             this.indicatorEl().addClass('visible');
33848         }
33849         
33850         this.el.removeClass([this.invalidClass, this.validClass]);
33851         this.el.addClass(this.invalidClass);
33852         
33853         this.fireEvent('invalid', this, msg);
33854         
33855     },
33856     
33857     setValue : function(v, suppressEvent)
33858     {   
33859         if(this.value === v){
33860             return;
33861         }
33862         
33863         this.value = v;
33864         
33865         if(this.rendered){
33866             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33867         }
33868         
33869         Roo.each(this.radioes, function(i){
33870             i.checked = false;
33871             i.el.removeClass('checked');
33872         });
33873         
33874         Roo.each(this.radioes, function(i){
33875             
33876             if(i.value === v || i.value.toString() === v.toString()){
33877                 i.checked = true;
33878                 i.el.addClass('checked');
33879                 
33880                 if(suppressEvent !== true){
33881                     this.fireEvent('check', this, i);
33882                 }
33883                 
33884                 return false;
33885             }
33886             
33887         }, this);
33888         
33889         this.validate();
33890     },
33891     
33892     clearInvalid : function(){
33893         
33894         if(!this.el || this.preventMark){
33895             return;
33896         }
33897         
33898         this.el.removeClass([this.invalidClass]);
33899         
33900         this.fireEvent('valid', this);
33901     }
33902     
33903 });
33904
33905 Roo.apply(Roo.bootstrap.RadioSet, {
33906     
33907     groups: {},
33908     
33909     register : function(set)
33910     {
33911         this.groups[set.name] = set;
33912     },
33913     
33914     get: function(name) 
33915     {
33916         if (typeof(this.groups[name]) == 'undefined') {
33917             return false;
33918         }
33919         
33920         return this.groups[name] ;
33921     }
33922     
33923 });
33924 /*
33925  * Based on:
33926  * Ext JS Library 1.1.1
33927  * Copyright(c) 2006-2007, Ext JS, LLC.
33928  *
33929  * Originally Released Under LGPL - original licence link has changed is not relivant.
33930  *
33931  * Fork - LGPL
33932  * <script type="text/javascript">
33933  */
33934
33935
33936 /**
33937  * @class Roo.bootstrap.SplitBar
33938  * @extends Roo.util.Observable
33939  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33940  * <br><br>
33941  * Usage:
33942  * <pre><code>
33943 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33944                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33945 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33946 split.minSize = 100;
33947 split.maxSize = 600;
33948 split.animate = true;
33949 split.on('moved', splitterMoved);
33950 </code></pre>
33951  * @constructor
33952  * Create a new SplitBar
33953  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33954  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33955  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33956  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33957                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33958                         position of the SplitBar).
33959  */
33960 Roo.bootstrap.SplitBar = function(cfg){
33961     
33962     /** @private */
33963     
33964     //{
33965     //  dragElement : elm
33966     //  resizingElement: el,
33967         // optional..
33968     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33969     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33970         // existingProxy ???
33971     //}
33972     
33973     this.el = Roo.get(cfg.dragElement, true);
33974     this.el.dom.unselectable = "on";
33975     /** @private */
33976     this.resizingEl = Roo.get(cfg.resizingElement, true);
33977
33978     /**
33979      * @private
33980      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33981      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33982      * @type Number
33983      */
33984     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33985     
33986     /**
33987      * The minimum size of the resizing element. (Defaults to 0)
33988      * @type Number
33989      */
33990     this.minSize = 0;
33991     
33992     /**
33993      * The maximum size of the resizing element. (Defaults to 2000)
33994      * @type Number
33995      */
33996     this.maxSize = 2000;
33997     
33998     /**
33999      * Whether to animate the transition to the new size
34000      * @type Boolean
34001      */
34002     this.animate = false;
34003     
34004     /**
34005      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34006      * @type Boolean
34007      */
34008     this.useShim = false;
34009     
34010     /** @private */
34011     this.shim = null;
34012     
34013     if(!cfg.existingProxy){
34014         /** @private */
34015         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34016     }else{
34017         this.proxy = Roo.get(cfg.existingProxy).dom;
34018     }
34019     /** @private */
34020     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34021     
34022     /** @private */
34023     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34024     
34025     /** @private */
34026     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34027     
34028     /** @private */
34029     this.dragSpecs = {};
34030     
34031     /**
34032      * @private The adapter to use to positon and resize elements
34033      */
34034     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34035     this.adapter.init(this);
34036     
34037     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34038         /** @private */
34039         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34040         this.el.addClass("roo-splitbar-h");
34041     }else{
34042         /** @private */
34043         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34044         this.el.addClass("roo-splitbar-v");
34045     }
34046     
34047     this.addEvents({
34048         /**
34049          * @event resize
34050          * Fires when the splitter is moved (alias for {@link #event-moved})
34051          * @param {Roo.bootstrap.SplitBar} this
34052          * @param {Number} newSize the new width or height
34053          */
34054         "resize" : true,
34055         /**
34056          * @event moved
34057          * Fires when the splitter is moved
34058          * @param {Roo.bootstrap.SplitBar} this
34059          * @param {Number} newSize the new width or height
34060          */
34061         "moved" : true,
34062         /**
34063          * @event beforeresize
34064          * Fires before the splitter is dragged
34065          * @param {Roo.bootstrap.SplitBar} this
34066          */
34067         "beforeresize" : true,
34068
34069         "beforeapply" : true
34070     });
34071
34072     Roo.util.Observable.call(this);
34073 };
34074
34075 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34076     onStartProxyDrag : function(x, y){
34077         this.fireEvent("beforeresize", this);
34078         if(!this.overlay){
34079             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34080             o.unselectable();
34081             o.enableDisplayMode("block");
34082             // all splitbars share the same overlay
34083             Roo.bootstrap.SplitBar.prototype.overlay = o;
34084         }
34085         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34086         this.overlay.show();
34087         Roo.get(this.proxy).setDisplayed("block");
34088         var size = this.adapter.getElementSize(this);
34089         this.activeMinSize = this.getMinimumSize();;
34090         this.activeMaxSize = this.getMaximumSize();;
34091         var c1 = size - this.activeMinSize;
34092         var c2 = Math.max(this.activeMaxSize - size, 0);
34093         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34094             this.dd.resetConstraints();
34095             this.dd.setXConstraint(
34096                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34097                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34098             );
34099             this.dd.setYConstraint(0, 0);
34100         }else{
34101             this.dd.resetConstraints();
34102             this.dd.setXConstraint(0, 0);
34103             this.dd.setYConstraint(
34104                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34105                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34106             );
34107          }
34108         this.dragSpecs.startSize = size;
34109         this.dragSpecs.startPoint = [x, y];
34110         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34111     },
34112     
34113     /** 
34114      * @private Called after the drag operation by the DDProxy
34115      */
34116     onEndProxyDrag : function(e){
34117         Roo.get(this.proxy).setDisplayed(false);
34118         var endPoint = Roo.lib.Event.getXY(e);
34119         if(this.overlay){
34120             this.overlay.hide();
34121         }
34122         var newSize;
34123         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34124             newSize = this.dragSpecs.startSize + 
34125                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34126                     endPoint[0] - this.dragSpecs.startPoint[0] :
34127                     this.dragSpecs.startPoint[0] - endPoint[0]
34128                 );
34129         }else{
34130             newSize = this.dragSpecs.startSize + 
34131                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34132                     endPoint[1] - this.dragSpecs.startPoint[1] :
34133                     this.dragSpecs.startPoint[1] - endPoint[1]
34134                 );
34135         }
34136         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34137         if(newSize != this.dragSpecs.startSize){
34138             if(this.fireEvent('beforeapply', this, newSize) !== false){
34139                 this.adapter.setElementSize(this, newSize);
34140                 this.fireEvent("moved", this, newSize);
34141                 this.fireEvent("resize", this, newSize);
34142             }
34143         }
34144     },
34145     
34146     /**
34147      * Get the adapter this SplitBar uses
34148      * @return The adapter object
34149      */
34150     getAdapter : function(){
34151         return this.adapter;
34152     },
34153     
34154     /**
34155      * Set the adapter this SplitBar uses
34156      * @param {Object} adapter A SplitBar adapter object
34157      */
34158     setAdapter : function(adapter){
34159         this.adapter = adapter;
34160         this.adapter.init(this);
34161     },
34162     
34163     /**
34164      * Gets the minimum size for the resizing element
34165      * @return {Number} The minimum size
34166      */
34167     getMinimumSize : function(){
34168         return this.minSize;
34169     },
34170     
34171     /**
34172      * Sets the minimum size for the resizing element
34173      * @param {Number} minSize The minimum size
34174      */
34175     setMinimumSize : function(minSize){
34176         this.minSize = minSize;
34177     },
34178     
34179     /**
34180      * Gets the maximum size for the resizing element
34181      * @return {Number} The maximum size
34182      */
34183     getMaximumSize : function(){
34184         return this.maxSize;
34185     },
34186     
34187     /**
34188      * Sets the maximum size for the resizing element
34189      * @param {Number} maxSize The maximum size
34190      */
34191     setMaximumSize : function(maxSize){
34192         this.maxSize = maxSize;
34193     },
34194     
34195     /**
34196      * Sets the initialize size for the resizing element
34197      * @param {Number} size The initial size
34198      */
34199     setCurrentSize : function(size){
34200         var oldAnimate = this.animate;
34201         this.animate = false;
34202         this.adapter.setElementSize(this, size);
34203         this.animate = oldAnimate;
34204     },
34205     
34206     /**
34207      * Destroy this splitbar. 
34208      * @param {Boolean} removeEl True to remove the element
34209      */
34210     destroy : function(removeEl){
34211         if(this.shim){
34212             this.shim.remove();
34213         }
34214         this.dd.unreg();
34215         this.proxy.parentNode.removeChild(this.proxy);
34216         if(removeEl){
34217             this.el.remove();
34218         }
34219     }
34220 });
34221
34222 /**
34223  * @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.
34224  */
34225 Roo.bootstrap.SplitBar.createProxy = function(dir){
34226     var proxy = new Roo.Element(document.createElement("div"));
34227     proxy.unselectable();
34228     var cls = 'roo-splitbar-proxy';
34229     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34230     document.body.appendChild(proxy.dom);
34231     return proxy.dom;
34232 };
34233
34234 /** 
34235  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34236  * Default Adapter. It assumes the splitter and resizing element are not positioned
34237  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34238  */
34239 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34240 };
34241
34242 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34243     // do nothing for now
34244     init : function(s){
34245     
34246     },
34247     /**
34248      * Called before drag operations to get the current size of the resizing element. 
34249      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34250      */
34251      getElementSize : function(s){
34252         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34253             return s.resizingEl.getWidth();
34254         }else{
34255             return s.resizingEl.getHeight();
34256         }
34257     },
34258     
34259     /**
34260      * Called after drag operations to set the size of the resizing element.
34261      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34262      * @param {Number} newSize The new size to set
34263      * @param {Function} onComplete A function to be invoked when resizing is complete
34264      */
34265     setElementSize : function(s, newSize, onComplete){
34266         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34267             if(!s.animate){
34268                 s.resizingEl.setWidth(newSize);
34269                 if(onComplete){
34270                     onComplete(s, newSize);
34271                 }
34272             }else{
34273                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34274             }
34275         }else{
34276             
34277             if(!s.animate){
34278                 s.resizingEl.setHeight(newSize);
34279                 if(onComplete){
34280                     onComplete(s, newSize);
34281                 }
34282             }else{
34283                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34284             }
34285         }
34286     }
34287 };
34288
34289 /** 
34290  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34291  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34292  * Adapter that  moves the splitter element to align with the resized sizing element. 
34293  * Used with an absolute positioned SplitBar.
34294  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34295  * document.body, make sure you assign an id to the body element.
34296  */
34297 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34298     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34299     this.container = Roo.get(container);
34300 };
34301
34302 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34303     init : function(s){
34304         this.basic.init(s);
34305     },
34306     
34307     getElementSize : function(s){
34308         return this.basic.getElementSize(s);
34309     },
34310     
34311     setElementSize : function(s, newSize, onComplete){
34312         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34313     },
34314     
34315     moveSplitter : function(s){
34316         var yes = Roo.bootstrap.SplitBar;
34317         switch(s.placement){
34318             case yes.LEFT:
34319                 s.el.setX(s.resizingEl.getRight());
34320                 break;
34321             case yes.RIGHT:
34322                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34323                 break;
34324             case yes.TOP:
34325                 s.el.setY(s.resizingEl.getBottom());
34326                 break;
34327             case yes.BOTTOM:
34328                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34329                 break;
34330         }
34331     }
34332 };
34333
34334 /**
34335  * Orientation constant - Create a vertical SplitBar
34336  * @static
34337  * @type Number
34338  */
34339 Roo.bootstrap.SplitBar.VERTICAL = 1;
34340
34341 /**
34342  * Orientation constant - Create a horizontal SplitBar
34343  * @static
34344  * @type Number
34345  */
34346 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34347
34348 /**
34349  * Placement constant - The resizing element is to the left of the splitter element
34350  * @static
34351  * @type Number
34352  */
34353 Roo.bootstrap.SplitBar.LEFT = 1;
34354
34355 /**
34356  * Placement constant - The resizing element is to the right of the splitter element
34357  * @static
34358  * @type Number
34359  */
34360 Roo.bootstrap.SplitBar.RIGHT = 2;
34361
34362 /**
34363  * Placement constant - The resizing element is positioned above the splitter element
34364  * @static
34365  * @type Number
34366  */
34367 Roo.bootstrap.SplitBar.TOP = 3;
34368
34369 /**
34370  * Placement constant - The resizing element is positioned under splitter element
34371  * @static
34372  * @type Number
34373  */
34374 Roo.bootstrap.SplitBar.BOTTOM = 4;
34375 Roo.namespace("Roo.bootstrap.layout");/*
34376  * Based on:
34377  * Ext JS Library 1.1.1
34378  * Copyright(c) 2006-2007, Ext JS, LLC.
34379  *
34380  * Originally Released Under LGPL - original licence link has changed is not relivant.
34381  *
34382  * Fork - LGPL
34383  * <script type="text/javascript">
34384  */
34385
34386 /**
34387  * @class Roo.bootstrap.layout.Manager
34388  * @extends Roo.bootstrap.Component
34389  * Base class for layout managers.
34390  */
34391 Roo.bootstrap.layout.Manager = function(config)
34392 {
34393     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34394
34395
34396
34397
34398
34399     /** false to disable window resize monitoring @type Boolean */
34400     this.monitorWindowResize = true;
34401     this.regions = {};
34402     this.addEvents({
34403         /**
34404          * @event layout
34405          * Fires when a layout is performed.
34406          * @param {Roo.LayoutManager} this
34407          */
34408         "layout" : true,
34409         /**
34410          * @event regionresized
34411          * Fires when the user resizes a region.
34412          * @param {Roo.LayoutRegion} region The resized region
34413          * @param {Number} newSize The new size (width for east/west, height for north/south)
34414          */
34415         "regionresized" : true,
34416         /**
34417          * @event regioncollapsed
34418          * Fires when a region is collapsed.
34419          * @param {Roo.LayoutRegion} region The collapsed region
34420          */
34421         "regioncollapsed" : true,
34422         /**
34423          * @event regionexpanded
34424          * Fires when a region is expanded.
34425          * @param {Roo.LayoutRegion} region The expanded region
34426          */
34427         "regionexpanded" : true
34428     });
34429     this.updating = false;
34430
34431     if (config.el) {
34432         this.el = Roo.get(config.el);
34433         this.initEvents();
34434     }
34435
34436 };
34437
34438 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34439
34440
34441     regions : null,
34442
34443     monitorWindowResize : true,
34444
34445
34446     updating : false,
34447
34448
34449     onRender : function(ct, position)
34450     {
34451         if(!this.el){
34452             this.el = Roo.get(ct);
34453             this.initEvents();
34454         }
34455         //this.fireEvent('render',this);
34456     },
34457
34458
34459     initEvents: function()
34460     {
34461
34462
34463         // ie scrollbar fix
34464         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34465             document.body.scroll = "no";
34466         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34467             this.el.position('relative');
34468         }
34469         this.id = this.el.id;
34470         this.el.addClass("roo-layout-container");
34471         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34472         if(this.el.dom != document.body ) {
34473             this.el.on('resize', this.layout,this);
34474             this.el.on('show', this.layout,this);
34475         }
34476
34477     },
34478
34479     /**
34480      * Returns true if this layout is currently being updated
34481      * @return {Boolean}
34482      */
34483     isUpdating : function(){
34484         return this.updating;
34485     },
34486
34487     /**
34488      * Suspend the LayoutManager from doing auto-layouts while
34489      * making multiple add or remove calls
34490      */
34491     beginUpdate : function(){
34492         this.updating = true;
34493     },
34494
34495     /**
34496      * Restore auto-layouts and optionally disable the manager from performing a layout
34497      * @param {Boolean} noLayout true to disable a layout update
34498      */
34499     endUpdate : function(noLayout){
34500         this.updating = false;
34501         if(!noLayout){
34502             this.layout();
34503         }
34504     },
34505
34506     layout: function(){
34507         // abstract...
34508     },
34509
34510     onRegionResized : function(region, newSize){
34511         this.fireEvent("regionresized", region, newSize);
34512         this.layout();
34513     },
34514
34515     onRegionCollapsed : function(region){
34516         this.fireEvent("regioncollapsed", region);
34517     },
34518
34519     onRegionExpanded : function(region){
34520         this.fireEvent("regionexpanded", region);
34521     },
34522
34523     /**
34524      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34525      * performs box-model adjustments.
34526      * @return {Object} The size as an object {width: (the width), height: (the height)}
34527      */
34528     getViewSize : function()
34529     {
34530         var size;
34531         if(this.el.dom != document.body){
34532             size = this.el.getSize();
34533         }else{
34534             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34535         }
34536         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34537         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34538         return size;
34539     },
34540
34541     /**
34542      * Returns the Element this layout is bound to.
34543      * @return {Roo.Element}
34544      */
34545     getEl : function(){
34546         return this.el;
34547     },
34548
34549     /**
34550      * Returns the specified region.
34551      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34552      * @return {Roo.LayoutRegion}
34553      */
34554     getRegion : function(target){
34555         return this.regions[target.toLowerCase()];
34556     },
34557
34558     onWindowResize : function(){
34559         if(this.monitorWindowResize){
34560             this.layout();
34561         }
34562     }
34563 });
34564 /*
34565  * Based on:
34566  * Ext JS Library 1.1.1
34567  * Copyright(c) 2006-2007, Ext JS, LLC.
34568  *
34569  * Originally Released Under LGPL - original licence link has changed is not relivant.
34570  *
34571  * Fork - LGPL
34572  * <script type="text/javascript">
34573  */
34574 /**
34575  * @class Roo.bootstrap.layout.Border
34576  * @extends Roo.bootstrap.layout.Manager
34577  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34578  * please see: examples/bootstrap/nested.html<br><br>
34579  
34580 <b>The container the layout is rendered into can be either the body element or any other element.
34581 If it is not the body element, the container needs to either be an absolute positioned element,
34582 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34583 the container size if it is not the body element.</b>
34584
34585 * @constructor
34586 * Create a new Border
34587 * @param {Object} config Configuration options
34588  */
34589 Roo.bootstrap.layout.Border = function(config){
34590     config = config || {};
34591     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34592     
34593     
34594     
34595     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34596         if(config[region]){
34597             config[region].region = region;
34598             this.addRegion(config[region]);
34599         }
34600     },this);
34601     
34602 };
34603
34604 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34605
34606 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34607     /**
34608      * Creates and adds a new region if it doesn't already exist.
34609      * @param {String} target The target region key (north, south, east, west or center).
34610      * @param {Object} config The regions config object
34611      * @return {BorderLayoutRegion} The new region
34612      */
34613     addRegion : function(config)
34614     {
34615         if(!this.regions[config.region]){
34616             var r = this.factory(config);
34617             this.bindRegion(r);
34618         }
34619         return this.regions[config.region];
34620     },
34621
34622     // private (kinda)
34623     bindRegion : function(r){
34624         this.regions[r.config.region] = r;
34625         
34626         r.on("visibilitychange",    this.layout, this);
34627         r.on("paneladded",          this.layout, this);
34628         r.on("panelremoved",        this.layout, this);
34629         r.on("invalidated",         this.layout, this);
34630         r.on("resized",             this.onRegionResized, this);
34631         r.on("collapsed",           this.onRegionCollapsed, this);
34632         r.on("expanded",            this.onRegionExpanded, this);
34633     },
34634
34635     /**
34636      * Performs a layout update.
34637      */
34638     layout : function()
34639     {
34640         if(this.updating) {
34641             return;
34642         }
34643         
34644         // render all the rebions if they have not been done alreayd?
34645         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34646             if(this.regions[region] && !this.regions[region].bodyEl){
34647                 this.regions[region].onRender(this.el)
34648             }
34649         },this);
34650         
34651         var size = this.getViewSize();
34652         var w = size.width;
34653         var h = size.height;
34654         var centerW = w;
34655         var centerH = h;
34656         var centerY = 0;
34657         var centerX = 0;
34658         //var x = 0, y = 0;
34659
34660         var rs = this.regions;
34661         var north = rs["north"];
34662         var south = rs["south"]; 
34663         var west = rs["west"];
34664         var east = rs["east"];
34665         var center = rs["center"];
34666         //if(this.hideOnLayout){ // not supported anymore
34667             //c.el.setStyle("display", "none");
34668         //}
34669         if(north && north.isVisible()){
34670             var b = north.getBox();
34671             var m = north.getMargins();
34672             b.width = w - (m.left+m.right);
34673             b.x = m.left;
34674             b.y = m.top;
34675             centerY = b.height + b.y + m.bottom;
34676             centerH -= centerY;
34677             north.updateBox(this.safeBox(b));
34678         }
34679         if(south && south.isVisible()){
34680             var b = south.getBox();
34681             var m = south.getMargins();
34682             b.width = w - (m.left+m.right);
34683             b.x = m.left;
34684             var totalHeight = (b.height + m.top + m.bottom);
34685             b.y = h - totalHeight + m.top;
34686             centerH -= totalHeight;
34687             south.updateBox(this.safeBox(b));
34688         }
34689         if(west && west.isVisible()){
34690             var b = west.getBox();
34691             var m = west.getMargins();
34692             b.height = centerH - (m.top+m.bottom);
34693             b.x = m.left;
34694             b.y = centerY + m.top;
34695             var totalWidth = (b.width + m.left + m.right);
34696             centerX += totalWidth;
34697             centerW -= totalWidth;
34698             west.updateBox(this.safeBox(b));
34699         }
34700         if(east && east.isVisible()){
34701             var b = east.getBox();
34702             var m = east.getMargins();
34703             b.height = centerH - (m.top+m.bottom);
34704             var totalWidth = (b.width + m.left + m.right);
34705             b.x = w - totalWidth + m.left;
34706             b.y = centerY + m.top;
34707             centerW -= totalWidth;
34708             east.updateBox(this.safeBox(b));
34709         }
34710         if(center){
34711             var m = center.getMargins();
34712             var centerBox = {
34713                 x: centerX + m.left,
34714                 y: centerY + m.top,
34715                 width: centerW - (m.left+m.right),
34716                 height: centerH - (m.top+m.bottom)
34717             };
34718             //if(this.hideOnLayout){
34719                 //center.el.setStyle("display", "block");
34720             //}
34721             center.updateBox(this.safeBox(centerBox));
34722         }
34723         this.el.repaint();
34724         this.fireEvent("layout", this);
34725     },
34726
34727     // private
34728     safeBox : function(box){
34729         box.width = Math.max(0, box.width);
34730         box.height = Math.max(0, box.height);
34731         return box;
34732     },
34733
34734     /**
34735      * Adds a ContentPanel (or subclass) to this layout.
34736      * @param {String} target The target region key (north, south, east, west or center).
34737      * @param {Roo.ContentPanel} panel The panel to add
34738      * @return {Roo.ContentPanel} The added panel
34739      */
34740     add : function(target, panel){
34741          
34742         target = target.toLowerCase();
34743         return this.regions[target].add(panel);
34744     },
34745
34746     /**
34747      * Remove a ContentPanel (or subclass) to this layout.
34748      * @param {String} target The target region key (north, south, east, west or center).
34749      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34750      * @return {Roo.ContentPanel} The removed panel
34751      */
34752     remove : function(target, panel){
34753         target = target.toLowerCase();
34754         return this.regions[target].remove(panel);
34755     },
34756
34757     /**
34758      * Searches all regions for a panel with the specified id
34759      * @param {String} panelId
34760      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34761      */
34762     findPanel : function(panelId){
34763         var rs = this.regions;
34764         for(var target in rs){
34765             if(typeof rs[target] != "function"){
34766                 var p = rs[target].getPanel(panelId);
34767                 if(p){
34768                     return p;
34769                 }
34770             }
34771         }
34772         return null;
34773     },
34774
34775     /**
34776      * Searches all regions for a panel with the specified id and activates (shows) it.
34777      * @param {String/ContentPanel} panelId The panels id or the panel itself
34778      * @return {Roo.ContentPanel} The shown panel or null
34779      */
34780     showPanel : function(panelId) {
34781       var rs = this.regions;
34782       for(var target in rs){
34783          var r = rs[target];
34784          if(typeof r != "function"){
34785             if(r.hasPanel(panelId)){
34786                return r.showPanel(panelId);
34787             }
34788          }
34789       }
34790       return null;
34791    },
34792
34793    /**
34794      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34795      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34796      */
34797    /*
34798     restoreState : function(provider){
34799         if(!provider){
34800             provider = Roo.state.Manager;
34801         }
34802         var sm = new Roo.LayoutStateManager();
34803         sm.init(this, provider);
34804     },
34805 */
34806  
34807  
34808     /**
34809      * Adds a xtype elements to the layout.
34810      * <pre><code>
34811
34812 layout.addxtype({
34813        xtype : 'ContentPanel',
34814        region: 'west',
34815        items: [ .... ]
34816    }
34817 );
34818
34819 layout.addxtype({
34820         xtype : 'NestedLayoutPanel',
34821         region: 'west',
34822         layout: {
34823            center: { },
34824            west: { }   
34825         },
34826         items : [ ... list of content panels or nested layout panels.. ]
34827    }
34828 );
34829 </code></pre>
34830      * @param {Object} cfg Xtype definition of item to add.
34831      */
34832     addxtype : function(cfg)
34833     {
34834         // basically accepts a pannel...
34835         // can accept a layout region..!?!?
34836         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34837         
34838         
34839         // theory?  children can only be panels??
34840         
34841         //if (!cfg.xtype.match(/Panel$/)) {
34842         //    return false;
34843         //}
34844         var ret = false;
34845         
34846         if (typeof(cfg.region) == 'undefined') {
34847             Roo.log("Failed to add Panel, region was not set");
34848             Roo.log(cfg);
34849             return false;
34850         }
34851         var region = cfg.region;
34852         delete cfg.region;
34853         
34854           
34855         var xitems = [];
34856         if (cfg.items) {
34857             xitems = cfg.items;
34858             delete cfg.items;
34859         }
34860         var nb = false;
34861         
34862         switch(cfg.xtype) 
34863         {
34864             case 'Content':  // ContentPanel (el, cfg)
34865             case 'Scroll':  // ContentPanel (el, cfg)
34866             case 'View': 
34867                 cfg.autoCreate = true;
34868                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34869                 //} else {
34870                 //    var el = this.el.createChild();
34871                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34872                 //}
34873                 
34874                 this.add(region, ret);
34875                 break;
34876             
34877             /*
34878             case 'TreePanel': // our new panel!
34879                 cfg.el = this.el.createChild();
34880                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34881                 this.add(region, ret);
34882                 break;
34883             */
34884             
34885             case 'Nest': 
34886                 // create a new Layout (which is  a Border Layout...
34887                 
34888                 var clayout = cfg.layout;
34889                 clayout.el  = this.el.createChild();
34890                 clayout.items   = clayout.items  || [];
34891                 
34892                 delete cfg.layout;
34893                 
34894                 // replace this exitems with the clayout ones..
34895                 xitems = clayout.items;
34896                  
34897                 // force background off if it's in center...
34898                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34899                     cfg.background = false;
34900                 }
34901                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34902                 
34903                 
34904                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34905                 //console.log('adding nested layout panel '  + cfg.toSource());
34906                 this.add(region, ret);
34907                 nb = {}; /// find first...
34908                 break;
34909             
34910             case 'Grid':
34911                 
34912                 // needs grid and region
34913                 
34914                 //var el = this.getRegion(region).el.createChild();
34915                 /*
34916                  *var el = this.el.createChild();
34917                 // create the grid first...
34918                 cfg.grid.container = el;
34919                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34920                 */
34921                 
34922                 if (region == 'center' && this.active ) {
34923                     cfg.background = false;
34924                 }
34925                 
34926                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34927                 
34928                 this.add(region, ret);
34929                 /*
34930                 if (cfg.background) {
34931                     // render grid on panel activation (if panel background)
34932                     ret.on('activate', function(gp) {
34933                         if (!gp.grid.rendered) {
34934                     //        gp.grid.render(el);
34935                         }
34936                     });
34937                 } else {
34938                   //  cfg.grid.render(el);
34939                 }
34940                 */
34941                 break;
34942            
34943            
34944             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34945                 // it was the old xcomponent building that caused this before.
34946                 // espeically if border is the top element in the tree.
34947                 ret = this;
34948                 break; 
34949                 
34950                     
34951                 
34952                 
34953                 
34954             default:
34955                 /*
34956                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34957                     
34958                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34959                     this.add(region, ret);
34960                 } else {
34961                 */
34962                     Roo.log(cfg);
34963                     throw "Can not add '" + cfg.xtype + "' to Border";
34964                     return null;
34965              
34966                                 
34967              
34968         }
34969         this.beginUpdate();
34970         // add children..
34971         var region = '';
34972         var abn = {};
34973         Roo.each(xitems, function(i)  {
34974             region = nb && i.region ? i.region : false;
34975             
34976             var add = ret.addxtype(i);
34977            
34978             if (region) {
34979                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34980                 if (!i.background) {
34981                     abn[region] = nb[region] ;
34982                 }
34983             }
34984             
34985         });
34986         this.endUpdate();
34987
34988         // make the last non-background panel active..
34989         //if (nb) { Roo.log(abn); }
34990         if (nb) {
34991             
34992             for(var r in abn) {
34993                 region = this.getRegion(r);
34994                 if (region) {
34995                     // tried using nb[r], but it does not work..
34996                      
34997                     region.showPanel(abn[r]);
34998                    
34999                 }
35000             }
35001         }
35002         return ret;
35003         
35004     },
35005     
35006     
35007 // private
35008     factory : function(cfg)
35009     {
35010         
35011         var validRegions = Roo.bootstrap.layout.Border.regions;
35012
35013         var target = cfg.region;
35014         cfg.mgr = this;
35015         
35016         var r = Roo.bootstrap.layout;
35017         Roo.log(target);
35018         switch(target){
35019             case "north":
35020                 return new r.North(cfg);
35021             case "south":
35022                 return new r.South(cfg);
35023             case "east":
35024                 return new r.East(cfg);
35025             case "west":
35026                 return new r.West(cfg);
35027             case "center":
35028                 return new r.Center(cfg);
35029         }
35030         throw 'Layout region "'+target+'" not supported.';
35031     }
35032     
35033     
35034 });
35035  /*
35036  * Based on:
35037  * Ext JS Library 1.1.1
35038  * Copyright(c) 2006-2007, Ext JS, LLC.
35039  *
35040  * Originally Released Under LGPL - original licence link has changed is not relivant.
35041  *
35042  * Fork - LGPL
35043  * <script type="text/javascript">
35044  */
35045  
35046 /**
35047  * @class Roo.bootstrap.layout.Basic
35048  * @extends Roo.util.Observable
35049  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35050  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35051  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35052  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35053  * @cfg {string}   region  the region that it inhabits..
35054  * @cfg {bool}   skipConfig skip config?
35055  * 
35056
35057  */
35058 Roo.bootstrap.layout.Basic = function(config){
35059     
35060     this.mgr = config.mgr;
35061     
35062     this.position = config.region;
35063     
35064     var skipConfig = config.skipConfig;
35065     
35066     this.events = {
35067         /**
35068          * @scope Roo.BasicLayoutRegion
35069          */
35070         
35071         /**
35072          * @event beforeremove
35073          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35074          * @param {Roo.LayoutRegion} this
35075          * @param {Roo.ContentPanel} panel The panel
35076          * @param {Object} e The cancel event object
35077          */
35078         "beforeremove" : true,
35079         /**
35080          * @event invalidated
35081          * Fires when the layout for this region is changed.
35082          * @param {Roo.LayoutRegion} this
35083          */
35084         "invalidated" : true,
35085         /**
35086          * @event visibilitychange
35087          * Fires when this region is shown or hidden 
35088          * @param {Roo.LayoutRegion} this
35089          * @param {Boolean} visibility true or false
35090          */
35091         "visibilitychange" : true,
35092         /**
35093          * @event paneladded
35094          * Fires when a panel is added. 
35095          * @param {Roo.LayoutRegion} this
35096          * @param {Roo.ContentPanel} panel The panel
35097          */
35098         "paneladded" : true,
35099         /**
35100          * @event panelremoved
35101          * Fires when a panel is removed. 
35102          * @param {Roo.LayoutRegion} this
35103          * @param {Roo.ContentPanel} panel The panel
35104          */
35105         "panelremoved" : true,
35106         /**
35107          * @event beforecollapse
35108          * Fires when this region before collapse.
35109          * @param {Roo.LayoutRegion} this
35110          */
35111         "beforecollapse" : true,
35112         /**
35113          * @event collapsed
35114          * Fires when this region is collapsed.
35115          * @param {Roo.LayoutRegion} this
35116          */
35117         "collapsed" : true,
35118         /**
35119          * @event expanded
35120          * Fires when this region is expanded.
35121          * @param {Roo.LayoutRegion} this
35122          */
35123         "expanded" : true,
35124         /**
35125          * @event slideshow
35126          * Fires when this region is slid into view.
35127          * @param {Roo.LayoutRegion} this
35128          */
35129         "slideshow" : true,
35130         /**
35131          * @event slidehide
35132          * Fires when this region slides out of view. 
35133          * @param {Roo.LayoutRegion} this
35134          */
35135         "slidehide" : true,
35136         /**
35137          * @event panelactivated
35138          * Fires when a panel is activated. 
35139          * @param {Roo.LayoutRegion} this
35140          * @param {Roo.ContentPanel} panel The activated panel
35141          */
35142         "panelactivated" : true,
35143         /**
35144          * @event resized
35145          * Fires when the user resizes this region. 
35146          * @param {Roo.LayoutRegion} this
35147          * @param {Number} newSize The new size (width for east/west, height for north/south)
35148          */
35149         "resized" : true
35150     };
35151     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35152     this.panels = new Roo.util.MixedCollection();
35153     this.panels.getKey = this.getPanelId.createDelegate(this);
35154     this.box = null;
35155     this.activePanel = null;
35156     // ensure listeners are added...
35157     
35158     if (config.listeners || config.events) {
35159         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35160             listeners : config.listeners || {},
35161             events : config.events || {}
35162         });
35163     }
35164     
35165     if(skipConfig !== true){
35166         this.applyConfig(config);
35167     }
35168 };
35169
35170 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35171 {
35172     getPanelId : function(p){
35173         return p.getId();
35174     },
35175     
35176     applyConfig : function(config){
35177         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35178         this.config = config;
35179         
35180     },
35181     
35182     /**
35183      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35184      * the width, for horizontal (north, south) the height.
35185      * @param {Number} newSize The new width or height
35186      */
35187     resizeTo : function(newSize){
35188         var el = this.el ? this.el :
35189                  (this.activePanel ? this.activePanel.getEl() : null);
35190         if(el){
35191             switch(this.position){
35192                 case "east":
35193                 case "west":
35194                     el.setWidth(newSize);
35195                     this.fireEvent("resized", this, newSize);
35196                 break;
35197                 case "north":
35198                 case "south":
35199                     el.setHeight(newSize);
35200                     this.fireEvent("resized", this, newSize);
35201                 break;                
35202             }
35203         }
35204     },
35205     
35206     getBox : function(){
35207         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35208     },
35209     
35210     getMargins : function(){
35211         return this.margins;
35212     },
35213     
35214     updateBox : function(box){
35215         this.box = box;
35216         var el = this.activePanel.getEl();
35217         el.dom.style.left = box.x + "px";
35218         el.dom.style.top = box.y + "px";
35219         this.activePanel.setSize(box.width, box.height);
35220     },
35221     
35222     /**
35223      * Returns the container element for this region.
35224      * @return {Roo.Element}
35225      */
35226     getEl : function(){
35227         return this.activePanel;
35228     },
35229     
35230     /**
35231      * Returns true if this region is currently visible.
35232      * @return {Boolean}
35233      */
35234     isVisible : function(){
35235         return this.activePanel ? true : false;
35236     },
35237     
35238     setActivePanel : function(panel){
35239         panel = this.getPanel(panel);
35240         if(this.activePanel && this.activePanel != panel){
35241             this.activePanel.setActiveState(false);
35242             this.activePanel.getEl().setLeftTop(-10000,-10000);
35243         }
35244         this.activePanel = panel;
35245         panel.setActiveState(true);
35246         if(this.box){
35247             panel.setSize(this.box.width, this.box.height);
35248         }
35249         this.fireEvent("panelactivated", this, panel);
35250         this.fireEvent("invalidated");
35251     },
35252     
35253     /**
35254      * Show the specified panel.
35255      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35256      * @return {Roo.ContentPanel} The shown panel or null
35257      */
35258     showPanel : function(panel){
35259         panel = this.getPanel(panel);
35260         if(panel){
35261             this.setActivePanel(panel);
35262         }
35263         return panel;
35264     },
35265     
35266     /**
35267      * Get the active panel for this region.
35268      * @return {Roo.ContentPanel} The active panel or null
35269      */
35270     getActivePanel : function(){
35271         return this.activePanel;
35272     },
35273     
35274     /**
35275      * Add the passed ContentPanel(s)
35276      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35277      * @return {Roo.ContentPanel} The panel added (if only one was added)
35278      */
35279     add : function(panel){
35280         if(arguments.length > 1){
35281             for(var i = 0, len = arguments.length; i < len; i++) {
35282                 this.add(arguments[i]);
35283             }
35284             return null;
35285         }
35286         if(this.hasPanel(panel)){
35287             this.showPanel(panel);
35288             return panel;
35289         }
35290         var el = panel.getEl();
35291         if(el.dom.parentNode != this.mgr.el.dom){
35292             this.mgr.el.dom.appendChild(el.dom);
35293         }
35294         if(panel.setRegion){
35295             panel.setRegion(this);
35296         }
35297         this.panels.add(panel);
35298         el.setStyle("position", "absolute");
35299         if(!panel.background){
35300             this.setActivePanel(panel);
35301             if(this.config.initialSize && this.panels.getCount()==1){
35302                 this.resizeTo(this.config.initialSize);
35303             }
35304         }
35305         this.fireEvent("paneladded", this, panel);
35306         return panel;
35307     },
35308     
35309     /**
35310      * Returns true if the panel is in this region.
35311      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35312      * @return {Boolean}
35313      */
35314     hasPanel : function(panel){
35315         if(typeof panel == "object"){ // must be panel obj
35316             panel = panel.getId();
35317         }
35318         return this.getPanel(panel) ? true : false;
35319     },
35320     
35321     /**
35322      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35323      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35324      * @param {Boolean} preservePanel Overrides the config preservePanel option
35325      * @return {Roo.ContentPanel} The panel that was removed
35326      */
35327     remove : function(panel, preservePanel){
35328         panel = this.getPanel(panel);
35329         if(!panel){
35330             return null;
35331         }
35332         var e = {};
35333         this.fireEvent("beforeremove", this, panel, e);
35334         if(e.cancel === true){
35335             return null;
35336         }
35337         var panelId = panel.getId();
35338         this.panels.removeKey(panelId);
35339         return panel;
35340     },
35341     
35342     /**
35343      * Returns the panel specified or null if it's not in this region.
35344      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35345      * @return {Roo.ContentPanel}
35346      */
35347     getPanel : function(id){
35348         if(typeof id == "object"){ // must be panel obj
35349             return id;
35350         }
35351         return this.panels.get(id);
35352     },
35353     
35354     /**
35355      * Returns this regions position (north/south/east/west/center).
35356      * @return {String} 
35357      */
35358     getPosition: function(){
35359         return this.position;    
35360     }
35361 });/*
35362  * Based on:
35363  * Ext JS Library 1.1.1
35364  * Copyright(c) 2006-2007, Ext JS, LLC.
35365  *
35366  * Originally Released Under LGPL - original licence link has changed is not relivant.
35367  *
35368  * Fork - LGPL
35369  * <script type="text/javascript">
35370  */
35371  
35372 /**
35373  * @class Roo.bootstrap.layout.Region
35374  * @extends Roo.bootstrap.layout.Basic
35375  * This class represents a region in a layout manager.
35376  
35377  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35378  * @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})
35379  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35380  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35381  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35382  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35383  * @cfg {String}    title           The title for the region (overrides panel titles)
35384  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35385  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35386  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35387  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35388  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35389  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35390  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35391  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35392  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35393  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35394
35395  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35396  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35397  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35398  * @cfg {Number}    width           For East/West panels
35399  * @cfg {Number}    height          For North/South panels
35400  * @cfg {Boolean}   split           To show the splitter
35401  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35402  * 
35403  * @cfg {string}   cls             Extra CSS classes to add to region
35404  * 
35405  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35406  * @cfg {string}   region  the region that it inhabits..
35407  *
35408
35409  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35410  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35411
35412  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35413  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35414  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35415  */
35416 Roo.bootstrap.layout.Region = function(config)
35417 {
35418     this.applyConfig(config);
35419
35420     var mgr = config.mgr;
35421     var pos = config.region;
35422     config.skipConfig = true;
35423     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35424     
35425     if (mgr.el) {
35426         this.onRender(mgr.el);   
35427     }
35428      
35429     this.visible = true;
35430     this.collapsed = false;
35431     this.unrendered_panels = [];
35432 };
35433
35434 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35435
35436     position: '', // set by wrapper (eg. north/south etc..)
35437     unrendered_panels : null,  // unrendered panels.
35438     createBody : function(){
35439         /** This region's body element 
35440         * @type Roo.Element */
35441         this.bodyEl = this.el.createChild({
35442                 tag: "div",
35443                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35444         });
35445     },
35446
35447     onRender: function(ctr, pos)
35448     {
35449         var dh = Roo.DomHelper;
35450         /** This region's container element 
35451         * @type Roo.Element */
35452         this.el = dh.append(ctr.dom, {
35453                 tag: "div",
35454                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35455             }, true);
35456         /** This region's title element 
35457         * @type Roo.Element */
35458     
35459         this.titleEl = dh.append(this.el.dom,
35460             {
35461                     tag: "div",
35462                     unselectable: "on",
35463                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35464                     children:[
35465                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35466                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35467                     ]}, true);
35468         
35469         this.titleEl.enableDisplayMode();
35470         /** This region's title text element 
35471         * @type HTMLElement */
35472         this.titleTextEl = this.titleEl.dom.firstChild;
35473         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35474         /*
35475         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35476         this.closeBtn.enableDisplayMode();
35477         this.closeBtn.on("click", this.closeClicked, this);
35478         this.closeBtn.hide();
35479     */
35480         this.createBody(this.config);
35481         if(this.config.hideWhenEmpty){
35482             this.hide();
35483             this.on("paneladded", this.validateVisibility, this);
35484             this.on("panelremoved", this.validateVisibility, this);
35485         }
35486         if(this.autoScroll){
35487             this.bodyEl.setStyle("overflow", "auto");
35488         }else{
35489             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35490         }
35491         //if(c.titlebar !== false){
35492             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35493                 this.titleEl.hide();
35494             }else{
35495                 this.titleEl.show();
35496                 if(this.config.title){
35497                     this.titleTextEl.innerHTML = this.config.title;
35498                 }
35499             }
35500         //}
35501         if(this.config.collapsed){
35502             this.collapse(true);
35503         }
35504         if(this.config.hidden){
35505             this.hide();
35506         }
35507         
35508         if (this.unrendered_panels && this.unrendered_panels.length) {
35509             for (var i =0;i< this.unrendered_panels.length; i++) {
35510                 this.add(this.unrendered_panels[i]);
35511             }
35512             this.unrendered_panels = null;
35513             
35514         }
35515         
35516     },
35517     
35518     applyConfig : function(c)
35519     {
35520         /*
35521          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35522             var dh = Roo.DomHelper;
35523             if(c.titlebar !== false){
35524                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35525                 this.collapseBtn.on("click", this.collapse, this);
35526                 this.collapseBtn.enableDisplayMode();
35527                 /*
35528                 if(c.showPin === true || this.showPin){
35529                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35530                     this.stickBtn.enableDisplayMode();
35531                     this.stickBtn.on("click", this.expand, this);
35532                     this.stickBtn.hide();
35533                 }
35534                 
35535             }
35536             */
35537             /** This region's collapsed element
35538             * @type Roo.Element */
35539             /*
35540              *
35541             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35542                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35543             ]}, true);
35544             
35545             if(c.floatable !== false){
35546                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35547                this.collapsedEl.on("click", this.collapseClick, this);
35548             }
35549
35550             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35551                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35552                    id: "message", unselectable: "on", style:{"float":"left"}});
35553                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35554              }
35555             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35556             this.expandBtn.on("click", this.expand, this);
35557             
35558         }
35559         
35560         if(this.collapseBtn){
35561             this.collapseBtn.setVisible(c.collapsible == true);
35562         }
35563         
35564         this.cmargins = c.cmargins || this.cmargins ||
35565                          (this.position == "west" || this.position == "east" ?
35566                              {top: 0, left: 2, right:2, bottom: 0} :
35567                              {top: 2, left: 0, right:0, bottom: 2});
35568         */
35569         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35570         
35571         
35572         this.bottomTabs = c.tabPosition != "top";
35573         
35574         this.autoScroll = c.autoScroll || false;
35575         
35576         
35577        
35578         
35579         this.duration = c.duration || .30;
35580         this.slideDuration = c.slideDuration || .45;
35581         this.config = c;
35582        
35583     },
35584     /**
35585      * Returns true if this region is currently visible.
35586      * @return {Boolean}
35587      */
35588     isVisible : function(){
35589         return this.visible;
35590     },
35591
35592     /**
35593      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35594      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35595      */
35596     //setCollapsedTitle : function(title){
35597     //    title = title || "&#160;";
35598      //   if(this.collapsedTitleTextEl){
35599       //      this.collapsedTitleTextEl.innerHTML = title;
35600        // }
35601     //},
35602
35603     getBox : function(){
35604         var b;
35605       //  if(!this.collapsed){
35606             b = this.el.getBox(false, true);
35607        // }else{
35608           //  b = this.collapsedEl.getBox(false, true);
35609         //}
35610         return b;
35611     },
35612
35613     getMargins : function(){
35614         return this.margins;
35615         //return this.collapsed ? this.cmargins : this.margins;
35616     },
35617 /*
35618     highlight : function(){
35619         this.el.addClass("x-layout-panel-dragover");
35620     },
35621
35622     unhighlight : function(){
35623         this.el.removeClass("x-layout-panel-dragover");
35624     },
35625 */
35626     updateBox : function(box)
35627     {
35628         if (!this.bodyEl) {
35629             return; // not rendered yet..
35630         }
35631         
35632         this.box = box;
35633         if(!this.collapsed){
35634             this.el.dom.style.left = box.x + "px";
35635             this.el.dom.style.top = box.y + "px";
35636             this.updateBody(box.width, box.height);
35637         }else{
35638             this.collapsedEl.dom.style.left = box.x + "px";
35639             this.collapsedEl.dom.style.top = box.y + "px";
35640             this.collapsedEl.setSize(box.width, box.height);
35641         }
35642         if(this.tabs){
35643             this.tabs.autoSizeTabs();
35644         }
35645     },
35646
35647     updateBody : function(w, h)
35648     {
35649         if(w !== null){
35650             this.el.setWidth(w);
35651             w -= this.el.getBorderWidth("rl");
35652             if(this.config.adjustments){
35653                 w += this.config.adjustments[0];
35654             }
35655         }
35656         if(h !== null && h > 0){
35657             this.el.setHeight(h);
35658             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35659             h -= this.el.getBorderWidth("tb");
35660             if(this.config.adjustments){
35661                 h += this.config.adjustments[1];
35662             }
35663             this.bodyEl.setHeight(h);
35664             if(this.tabs){
35665                 h = this.tabs.syncHeight(h);
35666             }
35667         }
35668         if(this.panelSize){
35669             w = w !== null ? w : this.panelSize.width;
35670             h = h !== null ? h : this.panelSize.height;
35671         }
35672         if(this.activePanel){
35673             var el = this.activePanel.getEl();
35674             w = w !== null ? w : el.getWidth();
35675             h = h !== null ? h : el.getHeight();
35676             this.panelSize = {width: w, height: h};
35677             this.activePanel.setSize(w, h);
35678         }
35679         if(Roo.isIE && this.tabs){
35680             this.tabs.el.repaint();
35681         }
35682     },
35683
35684     /**
35685      * Returns the container element for this region.
35686      * @return {Roo.Element}
35687      */
35688     getEl : function(){
35689         return this.el;
35690     },
35691
35692     /**
35693      * Hides this region.
35694      */
35695     hide : function(){
35696         //if(!this.collapsed){
35697             this.el.dom.style.left = "-2000px";
35698             this.el.hide();
35699         //}else{
35700          //   this.collapsedEl.dom.style.left = "-2000px";
35701          //   this.collapsedEl.hide();
35702        // }
35703         this.visible = false;
35704         this.fireEvent("visibilitychange", this, false);
35705     },
35706
35707     /**
35708      * Shows this region if it was previously hidden.
35709      */
35710     show : function(){
35711         //if(!this.collapsed){
35712             this.el.show();
35713         //}else{
35714         //    this.collapsedEl.show();
35715        // }
35716         this.visible = true;
35717         this.fireEvent("visibilitychange", this, true);
35718     },
35719 /*
35720     closeClicked : function(){
35721         if(this.activePanel){
35722             this.remove(this.activePanel);
35723         }
35724     },
35725
35726     collapseClick : function(e){
35727         if(this.isSlid){
35728            e.stopPropagation();
35729            this.slideIn();
35730         }else{
35731            e.stopPropagation();
35732            this.slideOut();
35733         }
35734     },
35735 */
35736     /**
35737      * Collapses this region.
35738      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35739      */
35740     /*
35741     collapse : function(skipAnim, skipCheck = false){
35742         if(this.collapsed) {
35743             return;
35744         }
35745         
35746         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35747             
35748             this.collapsed = true;
35749             if(this.split){
35750                 this.split.el.hide();
35751             }
35752             if(this.config.animate && skipAnim !== true){
35753                 this.fireEvent("invalidated", this);
35754                 this.animateCollapse();
35755             }else{
35756                 this.el.setLocation(-20000,-20000);
35757                 this.el.hide();
35758                 this.collapsedEl.show();
35759                 this.fireEvent("collapsed", this);
35760                 this.fireEvent("invalidated", this);
35761             }
35762         }
35763         
35764     },
35765 */
35766     animateCollapse : function(){
35767         // overridden
35768     },
35769
35770     /**
35771      * Expands this region if it was previously collapsed.
35772      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35773      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35774      */
35775     /*
35776     expand : function(e, skipAnim){
35777         if(e) {
35778             e.stopPropagation();
35779         }
35780         if(!this.collapsed || this.el.hasActiveFx()) {
35781             return;
35782         }
35783         if(this.isSlid){
35784             this.afterSlideIn();
35785             skipAnim = true;
35786         }
35787         this.collapsed = false;
35788         if(this.config.animate && skipAnim !== true){
35789             this.animateExpand();
35790         }else{
35791             this.el.show();
35792             if(this.split){
35793                 this.split.el.show();
35794             }
35795             this.collapsedEl.setLocation(-2000,-2000);
35796             this.collapsedEl.hide();
35797             this.fireEvent("invalidated", this);
35798             this.fireEvent("expanded", this);
35799         }
35800     },
35801 */
35802     animateExpand : function(){
35803         // overridden
35804     },
35805
35806     initTabs : function()
35807     {
35808         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35809         
35810         var ts = new Roo.bootstrap.panel.Tabs({
35811                 el: this.bodyEl.dom,
35812                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35813                 disableTooltips: this.config.disableTabTips,
35814                 toolbar : this.config.toolbar
35815             });
35816         
35817         if(this.config.hideTabs){
35818             ts.stripWrap.setDisplayed(false);
35819         }
35820         this.tabs = ts;
35821         ts.resizeTabs = this.config.resizeTabs === true;
35822         ts.minTabWidth = this.config.minTabWidth || 40;
35823         ts.maxTabWidth = this.config.maxTabWidth || 250;
35824         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35825         ts.monitorResize = false;
35826         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35827         ts.bodyEl.addClass('roo-layout-tabs-body');
35828         this.panels.each(this.initPanelAsTab, this);
35829     },
35830
35831     initPanelAsTab : function(panel){
35832         var ti = this.tabs.addTab(
35833             panel.getEl().id,
35834             panel.getTitle(),
35835             null,
35836             this.config.closeOnTab && panel.isClosable(),
35837             panel.tpl
35838         );
35839         if(panel.tabTip !== undefined){
35840             ti.setTooltip(panel.tabTip);
35841         }
35842         ti.on("activate", function(){
35843               this.setActivePanel(panel);
35844         }, this);
35845         
35846         if(this.config.closeOnTab){
35847             ti.on("beforeclose", function(t, e){
35848                 e.cancel = true;
35849                 this.remove(panel);
35850             }, this);
35851         }
35852         
35853         panel.tabItem = ti;
35854         
35855         return ti;
35856     },
35857
35858     updatePanelTitle : function(panel, title)
35859     {
35860         if(this.activePanel == panel){
35861             this.updateTitle(title);
35862         }
35863         if(this.tabs){
35864             var ti = this.tabs.getTab(panel.getEl().id);
35865             ti.setText(title);
35866             if(panel.tabTip !== undefined){
35867                 ti.setTooltip(panel.tabTip);
35868             }
35869         }
35870     },
35871
35872     updateTitle : function(title){
35873         if(this.titleTextEl && !this.config.title){
35874             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35875         }
35876     },
35877
35878     setActivePanel : function(panel)
35879     {
35880         panel = this.getPanel(panel);
35881         if(this.activePanel && this.activePanel != panel){
35882             if(this.activePanel.setActiveState(false) === false){
35883                 return;
35884             }
35885         }
35886         this.activePanel = panel;
35887         panel.setActiveState(true);
35888         if(this.panelSize){
35889             panel.setSize(this.panelSize.width, this.panelSize.height);
35890         }
35891         if(this.closeBtn){
35892             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35893         }
35894         this.updateTitle(panel.getTitle());
35895         if(this.tabs){
35896             this.fireEvent("invalidated", this);
35897         }
35898         this.fireEvent("panelactivated", this, panel);
35899     },
35900
35901     /**
35902      * Shows the specified panel.
35903      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35904      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35905      */
35906     showPanel : function(panel)
35907     {
35908         panel = this.getPanel(panel);
35909         if(panel){
35910             if(this.tabs){
35911                 var tab = this.tabs.getTab(panel.getEl().id);
35912                 if(tab.isHidden()){
35913                     this.tabs.unhideTab(tab.id);
35914                 }
35915                 tab.activate();
35916             }else{
35917                 this.setActivePanel(panel);
35918             }
35919         }
35920         return panel;
35921     },
35922
35923     /**
35924      * Get the active panel for this region.
35925      * @return {Roo.ContentPanel} The active panel or null
35926      */
35927     getActivePanel : function(){
35928         return this.activePanel;
35929     },
35930
35931     validateVisibility : function(){
35932         if(this.panels.getCount() < 1){
35933             this.updateTitle("&#160;");
35934             this.closeBtn.hide();
35935             this.hide();
35936         }else{
35937             if(!this.isVisible()){
35938                 this.show();
35939             }
35940         }
35941     },
35942
35943     /**
35944      * Adds the passed ContentPanel(s) to this region.
35945      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35946      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35947      */
35948     add : function(panel)
35949     {
35950         if(arguments.length > 1){
35951             for(var i = 0, len = arguments.length; i < len; i++) {
35952                 this.add(arguments[i]);
35953             }
35954             return null;
35955         }
35956         
35957         // if we have not been rendered yet, then we can not really do much of this..
35958         if (!this.bodyEl) {
35959             this.unrendered_panels.push(panel);
35960             return panel;
35961         }
35962         
35963         
35964         
35965         
35966         if(this.hasPanel(panel)){
35967             this.showPanel(panel);
35968             return panel;
35969         }
35970         panel.setRegion(this);
35971         this.panels.add(panel);
35972        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35973             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35974             // and hide them... ???
35975             this.bodyEl.dom.appendChild(panel.getEl().dom);
35976             if(panel.background !== true){
35977                 this.setActivePanel(panel);
35978             }
35979             this.fireEvent("paneladded", this, panel);
35980             return panel;
35981         }
35982         */
35983         if(!this.tabs){
35984             this.initTabs();
35985         }else{
35986             this.initPanelAsTab(panel);
35987         }
35988         
35989         
35990         if(panel.background !== true){
35991             this.tabs.activate(panel.getEl().id);
35992         }
35993         this.fireEvent("paneladded", this, panel);
35994         return panel;
35995     },
35996
35997     /**
35998      * Hides the tab for the specified panel.
35999      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36000      */
36001     hidePanel : function(panel){
36002         if(this.tabs && (panel = this.getPanel(panel))){
36003             this.tabs.hideTab(panel.getEl().id);
36004         }
36005     },
36006
36007     /**
36008      * Unhides the tab for a previously hidden panel.
36009      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36010      */
36011     unhidePanel : function(panel){
36012         if(this.tabs && (panel = this.getPanel(panel))){
36013             this.tabs.unhideTab(panel.getEl().id);
36014         }
36015     },
36016
36017     clearPanels : function(){
36018         while(this.panels.getCount() > 0){
36019              this.remove(this.panels.first());
36020         }
36021     },
36022
36023     /**
36024      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36025      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36026      * @param {Boolean} preservePanel Overrides the config preservePanel option
36027      * @return {Roo.ContentPanel} The panel that was removed
36028      */
36029     remove : function(panel, preservePanel)
36030     {
36031         panel = this.getPanel(panel);
36032         if(!panel){
36033             return null;
36034         }
36035         var e = {};
36036         this.fireEvent("beforeremove", this, panel, e);
36037         if(e.cancel === true){
36038             return null;
36039         }
36040         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36041         var panelId = panel.getId();
36042         this.panels.removeKey(panelId);
36043         if(preservePanel){
36044             document.body.appendChild(panel.getEl().dom);
36045         }
36046         if(this.tabs){
36047             this.tabs.removeTab(panel.getEl().id);
36048         }else if (!preservePanel){
36049             this.bodyEl.dom.removeChild(panel.getEl().dom);
36050         }
36051         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36052             var p = this.panels.first();
36053             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36054             tempEl.appendChild(p.getEl().dom);
36055             this.bodyEl.update("");
36056             this.bodyEl.dom.appendChild(p.getEl().dom);
36057             tempEl = null;
36058             this.updateTitle(p.getTitle());
36059             this.tabs = null;
36060             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36061             this.setActivePanel(p);
36062         }
36063         panel.setRegion(null);
36064         if(this.activePanel == panel){
36065             this.activePanel = null;
36066         }
36067         if(this.config.autoDestroy !== false && preservePanel !== true){
36068             try{panel.destroy();}catch(e){}
36069         }
36070         this.fireEvent("panelremoved", this, panel);
36071         return panel;
36072     },
36073
36074     /**
36075      * Returns the TabPanel component used by this region
36076      * @return {Roo.TabPanel}
36077      */
36078     getTabs : function(){
36079         return this.tabs;
36080     },
36081
36082     createTool : function(parentEl, className){
36083         var btn = Roo.DomHelper.append(parentEl, {
36084             tag: "div",
36085             cls: "x-layout-tools-button",
36086             children: [ {
36087                 tag: "div",
36088                 cls: "roo-layout-tools-button-inner " + className,
36089                 html: "&#160;"
36090             }]
36091         }, true);
36092         btn.addClassOnOver("roo-layout-tools-button-over");
36093         return btn;
36094     }
36095 });/*
36096  * Based on:
36097  * Ext JS Library 1.1.1
36098  * Copyright(c) 2006-2007, Ext JS, LLC.
36099  *
36100  * Originally Released Under LGPL - original licence link has changed is not relivant.
36101  *
36102  * Fork - LGPL
36103  * <script type="text/javascript">
36104  */
36105  
36106
36107
36108 /**
36109  * @class Roo.SplitLayoutRegion
36110  * @extends Roo.LayoutRegion
36111  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36112  */
36113 Roo.bootstrap.layout.Split = function(config){
36114     this.cursor = config.cursor;
36115     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36116 };
36117
36118 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36119 {
36120     splitTip : "Drag to resize.",
36121     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36122     useSplitTips : false,
36123
36124     applyConfig : function(config){
36125         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36126     },
36127     
36128     onRender : function(ctr,pos) {
36129         
36130         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36131         if(!this.config.split){
36132             return;
36133         }
36134         if(!this.split){
36135             
36136             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36137                             tag: "div",
36138                             id: this.el.id + "-split",
36139                             cls: "roo-layout-split roo-layout-split-"+this.position,
36140                             html: "&#160;"
36141             });
36142             /** The SplitBar for this region 
36143             * @type Roo.SplitBar */
36144             // does not exist yet...
36145             Roo.log([this.position, this.orientation]);
36146             
36147             this.split = new Roo.bootstrap.SplitBar({
36148                 dragElement : splitEl,
36149                 resizingElement: this.el,
36150                 orientation : this.orientation
36151             });
36152             
36153             this.split.on("moved", this.onSplitMove, this);
36154             this.split.useShim = this.config.useShim === true;
36155             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36156             if(this.useSplitTips){
36157                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36158             }
36159             //if(config.collapsible){
36160             //    this.split.el.on("dblclick", this.collapse,  this);
36161             //}
36162         }
36163         if(typeof this.config.minSize != "undefined"){
36164             this.split.minSize = this.config.minSize;
36165         }
36166         if(typeof this.config.maxSize != "undefined"){
36167             this.split.maxSize = this.config.maxSize;
36168         }
36169         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36170             this.hideSplitter();
36171         }
36172         
36173     },
36174
36175     getHMaxSize : function(){
36176          var cmax = this.config.maxSize || 10000;
36177          var center = this.mgr.getRegion("center");
36178          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36179     },
36180
36181     getVMaxSize : function(){
36182          var cmax = this.config.maxSize || 10000;
36183          var center = this.mgr.getRegion("center");
36184          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36185     },
36186
36187     onSplitMove : function(split, newSize){
36188         this.fireEvent("resized", this, newSize);
36189     },
36190     
36191     /** 
36192      * Returns the {@link Roo.SplitBar} for this region.
36193      * @return {Roo.SplitBar}
36194      */
36195     getSplitBar : function(){
36196         return this.split;
36197     },
36198     
36199     hide : function(){
36200         this.hideSplitter();
36201         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36202     },
36203
36204     hideSplitter : function(){
36205         if(this.split){
36206             this.split.el.setLocation(-2000,-2000);
36207             this.split.el.hide();
36208         }
36209     },
36210
36211     show : function(){
36212         if(this.split){
36213             this.split.el.show();
36214         }
36215         Roo.bootstrap.layout.Split.superclass.show.call(this);
36216     },
36217     
36218     beforeSlide: function(){
36219         if(Roo.isGecko){// firefox overflow auto bug workaround
36220             this.bodyEl.clip();
36221             if(this.tabs) {
36222                 this.tabs.bodyEl.clip();
36223             }
36224             if(this.activePanel){
36225                 this.activePanel.getEl().clip();
36226                 
36227                 if(this.activePanel.beforeSlide){
36228                     this.activePanel.beforeSlide();
36229                 }
36230             }
36231         }
36232     },
36233     
36234     afterSlide : function(){
36235         if(Roo.isGecko){// firefox overflow auto bug workaround
36236             this.bodyEl.unclip();
36237             if(this.tabs) {
36238                 this.tabs.bodyEl.unclip();
36239             }
36240             if(this.activePanel){
36241                 this.activePanel.getEl().unclip();
36242                 if(this.activePanel.afterSlide){
36243                     this.activePanel.afterSlide();
36244                 }
36245             }
36246         }
36247     },
36248
36249     initAutoHide : function(){
36250         if(this.autoHide !== false){
36251             if(!this.autoHideHd){
36252                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36253                 this.autoHideHd = {
36254                     "mouseout": function(e){
36255                         if(!e.within(this.el, true)){
36256                             st.delay(500);
36257                         }
36258                     },
36259                     "mouseover" : function(e){
36260                         st.cancel();
36261                     },
36262                     scope : this
36263                 };
36264             }
36265             this.el.on(this.autoHideHd);
36266         }
36267     },
36268
36269     clearAutoHide : function(){
36270         if(this.autoHide !== false){
36271             this.el.un("mouseout", this.autoHideHd.mouseout);
36272             this.el.un("mouseover", this.autoHideHd.mouseover);
36273         }
36274     },
36275
36276     clearMonitor : function(){
36277         Roo.get(document).un("click", this.slideInIf, this);
36278     },
36279
36280     // these names are backwards but not changed for compat
36281     slideOut : function(){
36282         if(this.isSlid || this.el.hasActiveFx()){
36283             return;
36284         }
36285         this.isSlid = true;
36286         if(this.collapseBtn){
36287             this.collapseBtn.hide();
36288         }
36289         this.closeBtnState = this.closeBtn.getStyle('display');
36290         this.closeBtn.hide();
36291         if(this.stickBtn){
36292             this.stickBtn.show();
36293         }
36294         this.el.show();
36295         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36296         this.beforeSlide();
36297         this.el.setStyle("z-index", 10001);
36298         this.el.slideIn(this.getSlideAnchor(), {
36299             callback: function(){
36300                 this.afterSlide();
36301                 this.initAutoHide();
36302                 Roo.get(document).on("click", this.slideInIf, this);
36303                 this.fireEvent("slideshow", this);
36304             },
36305             scope: this,
36306             block: true
36307         });
36308     },
36309
36310     afterSlideIn : function(){
36311         this.clearAutoHide();
36312         this.isSlid = false;
36313         this.clearMonitor();
36314         this.el.setStyle("z-index", "");
36315         if(this.collapseBtn){
36316             this.collapseBtn.show();
36317         }
36318         this.closeBtn.setStyle('display', this.closeBtnState);
36319         if(this.stickBtn){
36320             this.stickBtn.hide();
36321         }
36322         this.fireEvent("slidehide", this);
36323     },
36324
36325     slideIn : function(cb){
36326         if(!this.isSlid || this.el.hasActiveFx()){
36327             Roo.callback(cb);
36328             return;
36329         }
36330         this.isSlid = false;
36331         this.beforeSlide();
36332         this.el.slideOut(this.getSlideAnchor(), {
36333             callback: function(){
36334                 this.el.setLeftTop(-10000, -10000);
36335                 this.afterSlide();
36336                 this.afterSlideIn();
36337                 Roo.callback(cb);
36338             },
36339             scope: this,
36340             block: true
36341         });
36342     },
36343     
36344     slideInIf : function(e){
36345         if(!e.within(this.el)){
36346             this.slideIn();
36347         }
36348     },
36349
36350     animateCollapse : function(){
36351         this.beforeSlide();
36352         this.el.setStyle("z-index", 20000);
36353         var anchor = this.getSlideAnchor();
36354         this.el.slideOut(anchor, {
36355             callback : function(){
36356                 this.el.setStyle("z-index", "");
36357                 this.collapsedEl.slideIn(anchor, {duration:.3});
36358                 this.afterSlide();
36359                 this.el.setLocation(-10000,-10000);
36360                 this.el.hide();
36361                 this.fireEvent("collapsed", this);
36362             },
36363             scope: this,
36364             block: true
36365         });
36366     },
36367
36368     animateExpand : function(){
36369         this.beforeSlide();
36370         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36371         this.el.setStyle("z-index", 20000);
36372         this.collapsedEl.hide({
36373             duration:.1
36374         });
36375         this.el.slideIn(this.getSlideAnchor(), {
36376             callback : function(){
36377                 this.el.setStyle("z-index", "");
36378                 this.afterSlide();
36379                 if(this.split){
36380                     this.split.el.show();
36381                 }
36382                 this.fireEvent("invalidated", this);
36383                 this.fireEvent("expanded", this);
36384             },
36385             scope: this,
36386             block: true
36387         });
36388     },
36389
36390     anchors : {
36391         "west" : "left",
36392         "east" : "right",
36393         "north" : "top",
36394         "south" : "bottom"
36395     },
36396
36397     sanchors : {
36398         "west" : "l",
36399         "east" : "r",
36400         "north" : "t",
36401         "south" : "b"
36402     },
36403
36404     canchors : {
36405         "west" : "tl-tr",
36406         "east" : "tr-tl",
36407         "north" : "tl-bl",
36408         "south" : "bl-tl"
36409     },
36410
36411     getAnchor : function(){
36412         return this.anchors[this.position];
36413     },
36414
36415     getCollapseAnchor : function(){
36416         return this.canchors[this.position];
36417     },
36418
36419     getSlideAnchor : function(){
36420         return this.sanchors[this.position];
36421     },
36422
36423     getAlignAdj : function(){
36424         var cm = this.cmargins;
36425         switch(this.position){
36426             case "west":
36427                 return [0, 0];
36428             break;
36429             case "east":
36430                 return [0, 0];
36431             break;
36432             case "north":
36433                 return [0, 0];
36434             break;
36435             case "south":
36436                 return [0, 0];
36437             break;
36438         }
36439     },
36440
36441     getExpandAdj : function(){
36442         var c = this.collapsedEl, cm = this.cmargins;
36443         switch(this.position){
36444             case "west":
36445                 return [-(cm.right+c.getWidth()+cm.left), 0];
36446             break;
36447             case "east":
36448                 return [cm.right+c.getWidth()+cm.left, 0];
36449             break;
36450             case "north":
36451                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36452             break;
36453             case "south":
36454                 return [0, cm.top+cm.bottom+c.getHeight()];
36455             break;
36456         }
36457     }
36458 });/*
36459  * Based on:
36460  * Ext JS Library 1.1.1
36461  * Copyright(c) 2006-2007, Ext JS, LLC.
36462  *
36463  * Originally Released Under LGPL - original licence link has changed is not relivant.
36464  *
36465  * Fork - LGPL
36466  * <script type="text/javascript">
36467  */
36468 /*
36469  * These classes are private internal classes
36470  */
36471 Roo.bootstrap.layout.Center = function(config){
36472     config.region = "center";
36473     Roo.bootstrap.layout.Region.call(this, config);
36474     this.visible = true;
36475     this.minWidth = config.minWidth || 20;
36476     this.minHeight = config.minHeight || 20;
36477 };
36478
36479 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36480     hide : function(){
36481         // center panel can't be hidden
36482     },
36483     
36484     show : function(){
36485         // center panel can't be hidden
36486     },
36487     
36488     getMinWidth: function(){
36489         return this.minWidth;
36490     },
36491     
36492     getMinHeight: function(){
36493         return this.minHeight;
36494     }
36495 });
36496
36497
36498
36499
36500  
36501
36502
36503
36504
36505
36506 Roo.bootstrap.layout.North = function(config)
36507 {
36508     config.region = 'north';
36509     config.cursor = 'n-resize';
36510     
36511     Roo.bootstrap.layout.Split.call(this, config);
36512     
36513     
36514     if(this.split){
36515         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36516         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36517         this.split.el.addClass("roo-layout-split-v");
36518     }
36519     var size = config.initialSize || config.height;
36520     if(typeof size != "undefined"){
36521         this.el.setHeight(size);
36522     }
36523 };
36524 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36525 {
36526     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36527     
36528     
36529     
36530     getBox : function(){
36531         if(this.collapsed){
36532             return this.collapsedEl.getBox();
36533         }
36534         var box = this.el.getBox();
36535         if(this.split){
36536             box.height += this.split.el.getHeight();
36537         }
36538         return box;
36539     },
36540     
36541     updateBox : function(box){
36542         if(this.split && !this.collapsed){
36543             box.height -= this.split.el.getHeight();
36544             this.split.el.setLeft(box.x);
36545             this.split.el.setTop(box.y+box.height);
36546             this.split.el.setWidth(box.width);
36547         }
36548         if(this.collapsed){
36549             this.updateBody(box.width, null);
36550         }
36551         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36552     }
36553 });
36554
36555
36556
36557
36558
36559 Roo.bootstrap.layout.South = function(config){
36560     config.region = 'south';
36561     config.cursor = 's-resize';
36562     Roo.bootstrap.layout.Split.call(this, config);
36563     if(this.split){
36564         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36565         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36566         this.split.el.addClass("roo-layout-split-v");
36567     }
36568     var size = config.initialSize || config.height;
36569     if(typeof size != "undefined"){
36570         this.el.setHeight(size);
36571     }
36572 };
36573
36574 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36575     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36576     getBox : function(){
36577         if(this.collapsed){
36578             return this.collapsedEl.getBox();
36579         }
36580         var box = this.el.getBox();
36581         if(this.split){
36582             var sh = this.split.el.getHeight();
36583             box.height += sh;
36584             box.y -= sh;
36585         }
36586         return box;
36587     },
36588     
36589     updateBox : function(box){
36590         if(this.split && !this.collapsed){
36591             var sh = this.split.el.getHeight();
36592             box.height -= sh;
36593             box.y += sh;
36594             this.split.el.setLeft(box.x);
36595             this.split.el.setTop(box.y-sh);
36596             this.split.el.setWidth(box.width);
36597         }
36598         if(this.collapsed){
36599             this.updateBody(box.width, null);
36600         }
36601         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36602     }
36603 });
36604
36605 Roo.bootstrap.layout.East = function(config){
36606     config.region = "east";
36607     config.cursor = "e-resize";
36608     Roo.bootstrap.layout.Split.call(this, config);
36609     if(this.split){
36610         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36611         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36612         this.split.el.addClass("roo-layout-split-h");
36613     }
36614     var size = config.initialSize || config.width;
36615     if(typeof size != "undefined"){
36616         this.el.setWidth(size);
36617     }
36618 };
36619 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36620     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36621     getBox : function(){
36622         if(this.collapsed){
36623             return this.collapsedEl.getBox();
36624         }
36625         var box = this.el.getBox();
36626         if(this.split){
36627             var sw = this.split.el.getWidth();
36628             box.width += sw;
36629             box.x -= sw;
36630         }
36631         return box;
36632     },
36633
36634     updateBox : function(box){
36635         if(this.split && !this.collapsed){
36636             var sw = this.split.el.getWidth();
36637             box.width -= sw;
36638             this.split.el.setLeft(box.x);
36639             this.split.el.setTop(box.y);
36640             this.split.el.setHeight(box.height);
36641             box.x += sw;
36642         }
36643         if(this.collapsed){
36644             this.updateBody(null, box.height);
36645         }
36646         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36647     }
36648 });
36649
36650 Roo.bootstrap.layout.West = function(config){
36651     config.region = "west";
36652     config.cursor = "w-resize";
36653     
36654     Roo.bootstrap.layout.Split.call(this, config);
36655     if(this.split){
36656         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36657         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36658         this.split.el.addClass("roo-layout-split-h");
36659     }
36660     
36661 };
36662 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36663     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36664     
36665     onRender: function(ctr, pos)
36666     {
36667         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36668         var size = this.config.initialSize || this.config.width;
36669         if(typeof size != "undefined"){
36670             this.el.setWidth(size);
36671         }
36672     },
36673     
36674     getBox : function(){
36675         if(this.collapsed){
36676             return this.collapsedEl.getBox();
36677         }
36678         var box = this.el.getBox();
36679         if(this.split){
36680             box.width += this.split.el.getWidth();
36681         }
36682         return box;
36683     },
36684     
36685     updateBox : function(box){
36686         if(this.split && !this.collapsed){
36687             var sw = this.split.el.getWidth();
36688             box.width -= sw;
36689             this.split.el.setLeft(box.x+box.width);
36690             this.split.el.setTop(box.y);
36691             this.split.el.setHeight(box.height);
36692         }
36693         if(this.collapsed){
36694             this.updateBody(null, box.height);
36695         }
36696         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36697     }
36698 });
36699 Roo.namespace("Roo.bootstrap.panel");/*
36700  * Based on:
36701  * Ext JS Library 1.1.1
36702  * Copyright(c) 2006-2007, Ext JS, LLC.
36703  *
36704  * Originally Released Under LGPL - original licence link has changed is not relivant.
36705  *
36706  * Fork - LGPL
36707  * <script type="text/javascript">
36708  */
36709 /**
36710  * @class Roo.ContentPanel
36711  * @extends Roo.util.Observable
36712  * A basic ContentPanel element.
36713  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36714  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36715  * @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
36716  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36717  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36718  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36719  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36720  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36721  * @cfg {String} title          The title for this panel
36722  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36723  * @cfg {String} url            Calls {@link #setUrl} with this value
36724  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36725  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36726  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36727  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36728  * @cfg {Boolean} badges render the badges
36729
36730  * @constructor
36731  * Create a new ContentPanel.
36732  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36733  * @param {String/Object} config A string to set only the title or a config object
36734  * @param {String} content (optional) Set the HTML content for this panel
36735  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36736  */
36737 Roo.bootstrap.panel.Content = function( config){
36738     
36739     this.tpl = config.tpl || false;
36740     
36741     var el = config.el;
36742     var content = config.content;
36743
36744     if(config.autoCreate){ // xtype is available if this is called from factory
36745         el = Roo.id();
36746     }
36747     this.el = Roo.get(el);
36748     if(!this.el && config && config.autoCreate){
36749         if(typeof config.autoCreate == "object"){
36750             if(!config.autoCreate.id){
36751                 config.autoCreate.id = config.id||el;
36752             }
36753             this.el = Roo.DomHelper.append(document.body,
36754                         config.autoCreate, true);
36755         }else{
36756             var elcfg =  {   tag: "div",
36757                             cls: "roo-layout-inactive-content",
36758                             id: config.id||el
36759                             };
36760             if (config.html) {
36761                 elcfg.html = config.html;
36762                 
36763             }
36764                         
36765             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36766         }
36767     } 
36768     this.closable = false;
36769     this.loaded = false;
36770     this.active = false;
36771    
36772       
36773     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36774         
36775         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36776         
36777         this.wrapEl = this.el; //this.el.wrap();
36778         var ti = [];
36779         if (config.toolbar.items) {
36780             ti = config.toolbar.items ;
36781             delete config.toolbar.items ;
36782         }
36783         
36784         var nitems = [];
36785         this.toolbar.render(this.wrapEl, 'before');
36786         for(var i =0;i < ti.length;i++) {
36787           //  Roo.log(['add child', items[i]]);
36788             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36789         }
36790         this.toolbar.items = nitems;
36791         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36792         delete config.toolbar;
36793         
36794     }
36795     /*
36796     // xtype created footer. - not sure if will work as we normally have to render first..
36797     if (this.footer && !this.footer.el && this.footer.xtype) {
36798         if (!this.wrapEl) {
36799             this.wrapEl = this.el.wrap();
36800         }
36801     
36802         this.footer.container = this.wrapEl.createChild();
36803          
36804         this.footer = Roo.factory(this.footer, Roo);
36805         
36806     }
36807     */
36808     
36809      if(typeof config == "string"){
36810         this.title = config;
36811     }else{
36812         Roo.apply(this, config);
36813     }
36814     
36815     if(this.resizeEl){
36816         this.resizeEl = Roo.get(this.resizeEl, true);
36817     }else{
36818         this.resizeEl = this.el;
36819     }
36820     // handle view.xtype
36821     
36822  
36823     
36824     
36825     this.addEvents({
36826         /**
36827          * @event activate
36828          * Fires when this panel is activated. 
36829          * @param {Roo.ContentPanel} this
36830          */
36831         "activate" : true,
36832         /**
36833          * @event deactivate
36834          * Fires when this panel is activated. 
36835          * @param {Roo.ContentPanel} this
36836          */
36837         "deactivate" : true,
36838
36839         /**
36840          * @event resize
36841          * Fires when this panel is resized if fitToFrame is true.
36842          * @param {Roo.ContentPanel} this
36843          * @param {Number} width The width after any component adjustments
36844          * @param {Number} height The height after any component adjustments
36845          */
36846         "resize" : true,
36847         
36848          /**
36849          * @event render
36850          * Fires when this tab is created
36851          * @param {Roo.ContentPanel} this
36852          */
36853         "render" : true
36854         
36855         
36856         
36857     });
36858     
36859
36860     
36861     
36862     if(this.autoScroll){
36863         this.resizeEl.setStyle("overflow", "auto");
36864     } else {
36865         // fix randome scrolling
36866         //this.el.on('scroll', function() {
36867         //    Roo.log('fix random scolling');
36868         //    this.scrollTo('top',0); 
36869         //});
36870     }
36871     content = content || this.content;
36872     if(content){
36873         this.setContent(content);
36874     }
36875     if(config && config.url){
36876         this.setUrl(this.url, this.params, this.loadOnce);
36877     }
36878     
36879     
36880     
36881     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36882     
36883     if (this.view && typeof(this.view.xtype) != 'undefined') {
36884         this.view.el = this.el.appendChild(document.createElement("div"));
36885         this.view = Roo.factory(this.view); 
36886         this.view.render  &&  this.view.render(false, '');  
36887     }
36888     
36889     
36890     this.fireEvent('render', this);
36891 };
36892
36893 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36894     
36895     tabTip : '',
36896     
36897     setRegion : function(region){
36898         this.region = region;
36899         this.setActiveClass(region && !this.background);
36900     },
36901     
36902     
36903     setActiveClass: function(state)
36904     {
36905         if(state){
36906            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36907            this.el.setStyle('position','relative');
36908         }else{
36909            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36910            this.el.setStyle('position', 'absolute');
36911         } 
36912     },
36913     
36914     /**
36915      * Returns the toolbar for this Panel if one was configured. 
36916      * @return {Roo.Toolbar} 
36917      */
36918     getToolbar : function(){
36919         return this.toolbar;
36920     },
36921     
36922     setActiveState : function(active)
36923     {
36924         this.active = active;
36925         this.setActiveClass(active);
36926         if(!active){
36927             if(this.fireEvent("deactivate", this) === false){
36928                 return false;
36929             }
36930             return true;
36931         }
36932         this.fireEvent("activate", this);
36933         return true;
36934     },
36935     /**
36936      * Updates this panel's element
36937      * @param {String} content The new content
36938      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36939     */
36940     setContent : function(content, loadScripts){
36941         this.el.update(content, loadScripts);
36942     },
36943
36944     ignoreResize : function(w, h){
36945         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36946             return true;
36947         }else{
36948             this.lastSize = {width: w, height: h};
36949             return false;
36950         }
36951     },
36952     /**
36953      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36954      * @return {Roo.UpdateManager} The UpdateManager
36955      */
36956     getUpdateManager : function(){
36957         return this.el.getUpdateManager();
36958     },
36959      /**
36960      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36961      * @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:
36962 <pre><code>
36963 panel.load({
36964     url: "your-url.php",
36965     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36966     callback: yourFunction,
36967     scope: yourObject, //(optional scope)
36968     discardUrl: false,
36969     nocache: false,
36970     text: "Loading...",
36971     timeout: 30,
36972     scripts: false
36973 });
36974 </code></pre>
36975      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36976      * 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.
36977      * @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}
36978      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36979      * @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.
36980      * @return {Roo.ContentPanel} this
36981      */
36982     load : function(){
36983         var um = this.el.getUpdateManager();
36984         um.update.apply(um, arguments);
36985         return this;
36986     },
36987
36988
36989     /**
36990      * 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.
36991      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36992      * @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)
36993      * @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)
36994      * @return {Roo.UpdateManager} The UpdateManager
36995      */
36996     setUrl : function(url, params, loadOnce){
36997         if(this.refreshDelegate){
36998             this.removeListener("activate", this.refreshDelegate);
36999         }
37000         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37001         this.on("activate", this.refreshDelegate);
37002         return this.el.getUpdateManager();
37003     },
37004     
37005     _handleRefresh : function(url, params, loadOnce){
37006         if(!loadOnce || !this.loaded){
37007             var updater = this.el.getUpdateManager();
37008             updater.update(url, params, this._setLoaded.createDelegate(this));
37009         }
37010     },
37011     
37012     _setLoaded : function(){
37013         this.loaded = true;
37014     }, 
37015     
37016     /**
37017      * Returns this panel's id
37018      * @return {String} 
37019      */
37020     getId : function(){
37021         return this.el.id;
37022     },
37023     
37024     /** 
37025      * Returns this panel's element - used by regiosn to add.
37026      * @return {Roo.Element} 
37027      */
37028     getEl : function(){
37029         return this.wrapEl || this.el;
37030     },
37031     
37032    
37033     
37034     adjustForComponents : function(width, height)
37035     {
37036         //Roo.log('adjustForComponents ');
37037         if(this.resizeEl != this.el){
37038             width -= this.el.getFrameWidth('lr');
37039             height -= this.el.getFrameWidth('tb');
37040         }
37041         if(this.toolbar){
37042             var te = this.toolbar.getEl();
37043             te.setWidth(width);
37044             height -= te.getHeight();
37045         }
37046         if(this.footer){
37047             var te = this.footer.getEl();
37048             te.setWidth(width);
37049             height -= te.getHeight();
37050         }
37051         
37052         
37053         if(this.adjustments){
37054             width += this.adjustments[0];
37055             height += this.adjustments[1];
37056         }
37057         return {"width": width, "height": height};
37058     },
37059     
37060     setSize : function(width, height){
37061         if(this.fitToFrame && !this.ignoreResize(width, height)){
37062             if(this.fitContainer && this.resizeEl != this.el){
37063                 this.el.setSize(width, height);
37064             }
37065             var size = this.adjustForComponents(width, height);
37066             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37067             this.fireEvent('resize', this, size.width, size.height);
37068         }
37069     },
37070     
37071     /**
37072      * Returns this panel's title
37073      * @return {String} 
37074      */
37075     getTitle : function(){
37076         
37077         if (typeof(this.title) != 'object') {
37078             return this.title;
37079         }
37080         
37081         var t = '';
37082         for (var k in this.title) {
37083             if (!this.title.hasOwnProperty(k)) {
37084                 continue;
37085             }
37086             
37087             if (k.indexOf('-') >= 0) {
37088                 var s = k.split('-');
37089                 for (var i = 0; i<s.length; i++) {
37090                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37091                 }
37092             } else {
37093                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37094             }
37095         }
37096         return t;
37097     },
37098     
37099     /**
37100      * Set this panel's title
37101      * @param {String} title
37102      */
37103     setTitle : function(title){
37104         this.title = title;
37105         if(this.region){
37106             this.region.updatePanelTitle(this, title);
37107         }
37108     },
37109     
37110     /**
37111      * Returns true is this panel was configured to be closable
37112      * @return {Boolean} 
37113      */
37114     isClosable : function(){
37115         return this.closable;
37116     },
37117     
37118     beforeSlide : function(){
37119         this.el.clip();
37120         this.resizeEl.clip();
37121     },
37122     
37123     afterSlide : function(){
37124         this.el.unclip();
37125         this.resizeEl.unclip();
37126     },
37127     
37128     /**
37129      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37130      *   Will fail silently if the {@link #setUrl} method has not been called.
37131      *   This does not activate the panel, just updates its content.
37132      */
37133     refresh : function(){
37134         if(this.refreshDelegate){
37135            this.loaded = false;
37136            this.refreshDelegate();
37137         }
37138     },
37139     
37140     /**
37141      * Destroys this panel
37142      */
37143     destroy : function(){
37144         this.el.removeAllListeners();
37145         var tempEl = document.createElement("span");
37146         tempEl.appendChild(this.el.dom);
37147         tempEl.innerHTML = "";
37148         this.el.remove();
37149         this.el = null;
37150     },
37151     
37152     /**
37153      * form - if the content panel contains a form - this is a reference to it.
37154      * @type {Roo.form.Form}
37155      */
37156     form : false,
37157     /**
37158      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37159      *    This contains a reference to it.
37160      * @type {Roo.View}
37161      */
37162     view : false,
37163     
37164       /**
37165      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37166      * <pre><code>
37167
37168 layout.addxtype({
37169        xtype : 'Form',
37170        items: [ .... ]
37171    }
37172 );
37173
37174 </code></pre>
37175      * @param {Object} cfg Xtype definition of item to add.
37176      */
37177     
37178     
37179     getChildContainer: function () {
37180         return this.getEl();
37181     }
37182     
37183     
37184     /*
37185         var  ret = new Roo.factory(cfg);
37186         return ret;
37187         
37188         
37189         // add form..
37190         if (cfg.xtype.match(/^Form$/)) {
37191             
37192             var el;
37193             //if (this.footer) {
37194             //    el = this.footer.container.insertSibling(false, 'before');
37195             //} else {
37196                 el = this.el.createChild();
37197             //}
37198
37199             this.form = new  Roo.form.Form(cfg);
37200             
37201             
37202             if ( this.form.allItems.length) {
37203                 this.form.render(el.dom);
37204             }
37205             return this.form;
37206         }
37207         // should only have one of theses..
37208         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37209             // views.. should not be just added - used named prop 'view''
37210             
37211             cfg.el = this.el.appendChild(document.createElement("div"));
37212             // factory?
37213             
37214             var ret = new Roo.factory(cfg);
37215              
37216              ret.render && ret.render(false, ''); // render blank..
37217             this.view = ret;
37218             return ret;
37219         }
37220         return false;
37221     }
37222     \*/
37223 });
37224  
37225 /**
37226  * @class Roo.bootstrap.panel.Grid
37227  * @extends Roo.bootstrap.panel.Content
37228  * @constructor
37229  * Create a new GridPanel.
37230  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37231  * @param {Object} config A the config object
37232   
37233  */
37234
37235
37236
37237 Roo.bootstrap.panel.Grid = function(config)
37238 {
37239     
37240       
37241     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37242         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37243
37244     config.el = this.wrapper;
37245     //this.el = this.wrapper;
37246     
37247       if (config.container) {
37248         // ctor'ed from a Border/panel.grid
37249         
37250         
37251         this.wrapper.setStyle("overflow", "hidden");
37252         this.wrapper.addClass('roo-grid-container');
37253
37254     }
37255     
37256     
37257     if(config.toolbar){
37258         var tool_el = this.wrapper.createChild();    
37259         this.toolbar = Roo.factory(config.toolbar);
37260         var ti = [];
37261         if (config.toolbar.items) {
37262             ti = config.toolbar.items ;
37263             delete config.toolbar.items ;
37264         }
37265         
37266         var nitems = [];
37267         this.toolbar.render(tool_el);
37268         for(var i =0;i < ti.length;i++) {
37269           //  Roo.log(['add child', items[i]]);
37270             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37271         }
37272         this.toolbar.items = nitems;
37273         
37274         delete config.toolbar;
37275     }
37276     
37277     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37278     config.grid.scrollBody = true;;
37279     config.grid.monitorWindowResize = false; // turn off autosizing
37280     config.grid.autoHeight = false;
37281     config.grid.autoWidth = false;
37282     
37283     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37284     
37285     if (config.background) {
37286         // render grid on panel activation (if panel background)
37287         this.on('activate', function(gp) {
37288             if (!gp.grid.rendered) {
37289                 gp.grid.render(this.wrapper);
37290                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37291             }
37292         });
37293             
37294     } else {
37295         this.grid.render(this.wrapper);
37296         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37297
37298     }
37299     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37300     // ??? needed ??? config.el = this.wrapper;
37301     
37302     
37303     
37304   
37305     // xtype created footer. - not sure if will work as we normally have to render first..
37306     if (this.footer && !this.footer.el && this.footer.xtype) {
37307         
37308         var ctr = this.grid.getView().getFooterPanel(true);
37309         this.footer.dataSource = this.grid.dataSource;
37310         this.footer = Roo.factory(this.footer, Roo);
37311         this.footer.render(ctr);
37312         
37313     }
37314     
37315     
37316     
37317     
37318      
37319 };
37320
37321 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37322     getId : function(){
37323         return this.grid.id;
37324     },
37325     
37326     /**
37327      * Returns the grid for this panel
37328      * @return {Roo.bootstrap.Table} 
37329      */
37330     getGrid : function(){
37331         return this.grid;    
37332     },
37333     
37334     setSize : function(width, height){
37335         if(!this.ignoreResize(width, height)){
37336             var grid = this.grid;
37337             var size = this.adjustForComponents(width, height);
37338             var gridel = grid.getGridEl();
37339             gridel.setSize(size.width, size.height);
37340             /*
37341             var thd = grid.getGridEl().select('thead',true).first();
37342             var tbd = grid.getGridEl().select('tbody', true).first();
37343             if (tbd) {
37344                 tbd.setSize(width, height - thd.getHeight());
37345             }
37346             */
37347             grid.autoSize();
37348         }
37349     },
37350      
37351     
37352     
37353     beforeSlide : function(){
37354         this.grid.getView().scroller.clip();
37355     },
37356     
37357     afterSlide : function(){
37358         this.grid.getView().scroller.unclip();
37359     },
37360     
37361     destroy : function(){
37362         this.grid.destroy();
37363         delete this.grid;
37364         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37365     }
37366 });
37367
37368 /**
37369  * @class Roo.bootstrap.panel.Nest
37370  * @extends Roo.bootstrap.panel.Content
37371  * @constructor
37372  * Create a new Panel, that can contain a layout.Border.
37373  * 
37374  * 
37375  * @param {Roo.BorderLayout} layout The layout for this panel
37376  * @param {String/Object} config A string to set only the title or a config object
37377  */
37378 Roo.bootstrap.panel.Nest = function(config)
37379 {
37380     // construct with only one argument..
37381     /* FIXME - implement nicer consturctors
37382     if (layout.layout) {
37383         config = layout;
37384         layout = config.layout;
37385         delete config.layout;
37386     }
37387     if (layout.xtype && !layout.getEl) {
37388         // then layout needs constructing..
37389         layout = Roo.factory(layout, Roo);
37390     }
37391     */
37392     
37393     config.el =  config.layout.getEl();
37394     
37395     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37396     
37397     config.layout.monitorWindowResize = false; // turn off autosizing
37398     this.layout = config.layout;
37399     this.layout.getEl().addClass("roo-layout-nested-layout");
37400     
37401     
37402     
37403     
37404 };
37405
37406 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37407
37408     setSize : function(width, height){
37409         if(!this.ignoreResize(width, height)){
37410             var size = this.adjustForComponents(width, height);
37411             var el = this.layout.getEl();
37412             if (size.height < 1) {
37413                 el.setWidth(size.width);   
37414             } else {
37415                 el.setSize(size.width, size.height);
37416             }
37417             var touch = el.dom.offsetWidth;
37418             this.layout.layout();
37419             // ie requires a double layout on the first pass
37420             if(Roo.isIE && !this.initialized){
37421                 this.initialized = true;
37422                 this.layout.layout();
37423             }
37424         }
37425     },
37426     
37427     // activate all subpanels if not currently active..
37428     
37429     setActiveState : function(active){
37430         this.active = active;
37431         this.setActiveClass(active);
37432         
37433         if(!active){
37434             this.fireEvent("deactivate", this);
37435             return;
37436         }
37437         
37438         this.fireEvent("activate", this);
37439         // not sure if this should happen before or after..
37440         if (!this.layout) {
37441             return; // should not happen..
37442         }
37443         var reg = false;
37444         for (var r in this.layout.regions) {
37445             reg = this.layout.getRegion(r);
37446             if (reg.getActivePanel()) {
37447                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37448                 reg.setActivePanel(reg.getActivePanel());
37449                 continue;
37450             }
37451             if (!reg.panels.length) {
37452                 continue;
37453             }
37454             reg.showPanel(reg.getPanel(0));
37455         }
37456         
37457         
37458         
37459         
37460     },
37461     
37462     /**
37463      * Returns the nested BorderLayout for this panel
37464      * @return {Roo.BorderLayout} 
37465      */
37466     getLayout : function(){
37467         return this.layout;
37468     },
37469     
37470      /**
37471      * Adds a xtype elements to the layout of the nested panel
37472      * <pre><code>
37473
37474 panel.addxtype({
37475        xtype : 'ContentPanel',
37476        region: 'west',
37477        items: [ .... ]
37478    }
37479 );
37480
37481 panel.addxtype({
37482         xtype : 'NestedLayoutPanel',
37483         region: 'west',
37484         layout: {
37485            center: { },
37486            west: { }   
37487         },
37488         items : [ ... list of content panels or nested layout panels.. ]
37489    }
37490 );
37491 </code></pre>
37492      * @param {Object} cfg Xtype definition of item to add.
37493      */
37494     addxtype : function(cfg) {
37495         return this.layout.addxtype(cfg);
37496     
37497     }
37498 });        /*
37499  * Based on:
37500  * Ext JS Library 1.1.1
37501  * Copyright(c) 2006-2007, Ext JS, LLC.
37502  *
37503  * Originally Released Under LGPL - original licence link has changed is not relivant.
37504  *
37505  * Fork - LGPL
37506  * <script type="text/javascript">
37507  */
37508 /**
37509  * @class Roo.TabPanel
37510  * @extends Roo.util.Observable
37511  * A lightweight tab container.
37512  * <br><br>
37513  * Usage:
37514  * <pre><code>
37515 // basic tabs 1, built from existing content
37516 var tabs = new Roo.TabPanel("tabs1");
37517 tabs.addTab("script", "View Script");
37518 tabs.addTab("markup", "View Markup");
37519 tabs.activate("script");
37520
37521 // more advanced tabs, built from javascript
37522 var jtabs = new Roo.TabPanel("jtabs");
37523 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37524
37525 // set up the UpdateManager
37526 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37527 var updater = tab2.getUpdateManager();
37528 updater.setDefaultUrl("ajax1.htm");
37529 tab2.on('activate', updater.refresh, updater, true);
37530
37531 // Use setUrl for Ajax loading
37532 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37533 tab3.setUrl("ajax2.htm", null, true);
37534
37535 // Disabled tab
37536 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37537 tab4.disable();
37538
37539 jtabs.activate("jtabs-1");
37540  * </code></pre>
37541  * @constructor
37542  * Create a new TabPanel.
37543  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37544  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37545  */
37546 Roo.bootstrap.panel.Tabs = function(config){
37547     /**
37548     * The container element for this TabPanel.
37549     * @type Roo.Element
37550     */
37551     this.el = Roo.get(config.el);
37552     delete config.el;
37553     if(config){
37554         if(typeof config == "boolean"){
37555             this.tabPosition = config ? "bottom" : "top";
37556         }else{
37557             Roo.apply(this, config);
37558         }
37559     }
37560     
37561     if(this.tabPosition == "bottom"){
37562         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37563         this.el.addClass("roo-tabs-bottom");
37564     }
37565     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37566     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37567     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37568     if(Roo.isIE){
37569         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37570     }
37571     if(this.tabPosition != "bottom"){
37572         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37573          * @type Roo.Element
37574          */
37575         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37576         this.el.addClass("roo-tabs-top");
37577     }
37578     this.items = [];
37579
37580     this.bodyEl.setStyle("position", "relative");
37581
37582     this.active = null;
37583     this.activateDelegate = this.activate.createDelegate(this);
37584
37585     this.addEvents({
37586         /**
37587          * @event tabchange
37588          * Fires when the active tab changes
37589          * @param {Roo.TabPanel} this
37590          * @param {Roo.TabPanelItem} activePanel The new active tab
37591          */
37592         "tabchange": true,
37593         /**
37594          * @event beforetabchange
37595          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37596          * @param {Roo.TabPanel} this
37597          * @param {Object} e Set cancel to true on this object to cancel the tab change
37598          * @param {Roo.TabPanelItem} tab The tab being changed to
37599          */
37600         "beforetabchange" : true
37601     });
37602
37603     Roo.EventManager.onWindowResize(this.onResize, this);
37604     this.cpad = this.el.getPadding("lr");
37605     this.hiddenCount = 0;
37606
37607
37608     // toolbar on the tabbar support...
37609     if (this.toolbar) {
37610         alert("no toolbar support yet");
37611         this.toolbar  = false;
37612         /*
37613         var tcfg = this.toolbar;
37614         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37615         this.toolbar = new Roo.Toolbar(tcfg);
37616         if (Roo.isSafari) {
37617             var tbl = tcfg.container.child('table', true);
37618             tbl.setAttribute('width', '100%');
37619         }
37620         */
37621         
37622     }
37623    
37624
37625
37626     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37627 };
37628
37629 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37630     /*
37631      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37632      */
37633     tabPosition : "top",
37634     /*
37635      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37636      */
37637     currentTabWidth : 0,
37638     /*
37639      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37640      */
37641     minTabWidth : 40,
37642     /*
37643      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37644      */
37645     maxTabWidth : 250,
37646     /*
37647      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37648      */
37649     preferredTabWidth : 175,
37650     /*
37651      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37652      */
37653     resizeTabs : false,
37654     /*
37655      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37656      */
37657     monitorResize : true,
37658     /*
37659      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37660      */
37661     toolbar : false,
37662
37663     /**
37664      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37665      * @param {String} id The id of the div to use <b>or create</b>
37666      * @param {String} text The text for the tab
37667      * @param {String} content (optional) Content to put in the TabPanelItem body
37668      * @param {Boolean} closable (optional) True to create a close icon on the tab
37669      * @return {Roo.TabPanelItem} The created TabPanelItem
37670      */
37671     addTab : function(id, text, content, closable, tpl)
37672     {
37673         var item = new Roo.bootstrap.panel.TabItem({
37674             panel: this,
37675             id : id,
37676             text : text,
37677             closable : closable,
37678             tpl : tpl
37679         });
37680         this.addTabItem(item);
37681         if(content){
37682             item.setContent(content);
37683         }
37684         return item;
37685     },
37686
37687     /**
37688      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37689      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37690      * @return {Roo.TabPanelItem}
37691      */
37692     getTab : function(id){
37693         return this.items[id];
37694     },
37695
37696     /**
37697      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37698      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37699      */
37700     hideTab : function(id){
37701         var t = this.items[id];
37702         if(!t.isHidden()){
37703            t.setHidden(true);
37704            this.hiddenCount++;
37705            this.autoSizeTabs();
37706         }
37707     },
37708
37709     /**
37710      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37711      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37712      */
37713     unhideTab : function(id){
37714         var t = this.items[id];
37715         if(t.isHidden()){
37716            t.setHidden(false);
37717            this.hiddenCount--;
37718            this.autoSizeTabs();
37719         }
37720     },
37721
37722     /**
37723      * Adds an existing {@link Roo.TabPanelItem}.
37724      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37725      */
37726     addTabItem : function(item){
37727         this.items[item.id] = item;
37728         this.items.push(item);
37729       //  if(this.resizeTabs){
37730     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37731   //         this.autoSizeTabs();
37732 //        }else{
37733 //            item.autoSize();
37734        // }
37735     },
37736
37737     /**
37738      * Removes a {@link Roo.TabPanelItem}.
37739      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37740      */
37741     removeTab : function(id){
37742         var items = this.items;
37743         var tab = items[id];
37744         if(!tab) { return; }
37745         var index = items.indexOf(tab);
37746         if(this.active == tab && items.length > 1){
37747             var newTab = this.getNextAvailable(index);
37748             if(newTab) {
37749                 newTab.activate();
37750             }
37751         }
37752         this.stripEl.dom.removeChild(tab.pnode.dom);
37753         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37754             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37755         }
37756         items.splice(index, 1);
37757         delete this.items[tab.id];
37758         tab.fireEvent("close", tab);
37759         tab.purgeListeners();
37760         this.autoSizeTabs();
37761     },
37762
37763     getNextAvailable : function(start){
37764         var items = this.items;
37765         var index = start;
37766         // look for a next tab that will slide over to
37767         // replace the one being removed
37768         while(index < items.length){
37769             var item = items[++index];
37770             if(item && !item.isHidden()){
37771                 return item;
37772             }
37773         }
37774         // if one isn't found select the previous tab (on the left)
37775         index = start;
37776         while(index >= 0){
37777             var item = items[--index];
37778             if(item && !item.isHidden()){
37779                 return item;
37780             }
37781         }
37782         return null;
37783     },
37784
37785     /**
37786      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37787      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37788      */
37789     disableTab : function(id){
37790         var tab = this.items[id];
37791         if(tab && this.active != tab){
37792             tab.disable();
37793         }
37794     },
37795
37796     /**
37797      * Enables a {@link Roo.TabPanelItem} that is disabled.
37798      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37799      */
37800     enableTab : function(id){
37801         var tab = this.items[id];
37802         tab.enable();
37803     },
37804
37805     /**
37806      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37807      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37808      * @return {Roo.TabPanelItem} The TabPanelItem.
37809      */
37810     activate : function(id){
37811         var tab = this.items[id];
37812         if(!tab){
37813             return null;
37814         }
37815         if(tab == this.active || tab.disabled){
37816             return tab;
37817         }
37818         var e = {};
37819         this.fireEvent("beforetabchange", this, e, tab);
37820         if(e.cancel !== true && !tab.disabled){
37821             if(this.active){
37822                 this.active.hide();
37823             }
37824             this.active = this.items[id];
37825             this.active.show();
37826             this.fireEvent("tabchange", this, this.active);
37827         }
37828         return tab;
37829     },
37830
37831     /**
37832      * Gets the active {@link Roo.TabPanelItem}.
37833      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37834      */
37835     getActiveTab : function(){
37836         return this.active;
37837     },
37838
37839     /**
37840      * Updates the tab body element to fit the height of the container element
37841      * for overflow scrolling
37842      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37843      */
37844     syncHeight : function(targetHeight){
37845         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37846         var bm = this.bodyEl.getMargins();
37847         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37848         this.bodyEl.setHeight(newHeight);
37849         return newHeight;
37850     },
37851
37852     onResize : function(){
37853         if(this.monitorResize){
37854             this.autoSizeTabs();
37855         }
37856     },
37857
37858     /**
37859      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37860      */
37861     beginUpdate : function(){
37862         this.updating = true;
37863     },
37864
37865     /**
37866      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37867      */
37868     endUpdate : function(){
37869         this.updating = false;
37870         this.autoSizeTabs();
37871     },
37872
37873     /**
37874      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37875      */
37876     autoSizeTabs : function(){
37877         var count = this.items.length;
37878         var vcount = count - this.hiddenCount;
37879         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37880             return;
37881         }
37882         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37883         var availWidth = Math.floor(w / vcount);
37884         var b = this.stripBody;
37885         if(b.getWidth() > w){
37886             var tabs = this.items;
37887             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37888             if(availWidth < this.minTabWidth){
37889                 /*if(!this.sleft){    // incomplete scrolling code
37890                     this.createScrollButtons();
37891                 }
37892                 this.showScroll();
37893                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37894             }
37895         }else{
37896             if(this.currentTabWidth < this.preferredTabWidth){
37897                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37898             }
37899         }
37900     },
37901
37902     /**
37903      * Returns the number of tabs in this TabPanel.
37904      * @return {Number}
37905      */
37906      getCount : function(){
37907          return this.items.length;
37908      },
37909
37910     /**
37911      * Resizes all the tabs to the passed width
37912      * @param {Number} The new width
37913      */
37914     setTabWidth : function(width){
37915         this.currentTabWidth = width;
37916         for(var i = 0, len = this.items.length; i < len; i++) {
37917                 if(!this.items[i].isHidden()) {
37918                 this.items[i].setWidth(width);
37919             }
37920         }
37921     },
37922
37923     /**
37924      * Destroys this TabPanel
37925      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37926      */
37927     destroy : function(removeEl){
37928         Roo.EventManager.removeResizeListener(this.onResize, this);
37929         for(var i = 0, len = this.items.length; i < len; i++){
37930             this.items[i].purgeListeners();
37931         }
37932         if(removeEl === true){
37933             this.el.update("");
37934             this.el.remove();
37935         }
37936     },
37937     
37938     createStrip : function(container)
37939     {
37940         var strip = document.createElement("nav");
37941         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37942         container.appendChild(strip);
37943         return strip;
37944     },
37945     
37946     createStripList : function(strip)
37947     {
37948         // div wrapper for retard IE
37949         // returns the "tr" element.
37950         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37951         //'<div class="x-tabs-strip-wrap">'+
37952           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37953           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37954         return strip.firstChild; //.firstChild.firstChild.firstChild;
37955     },
37956     createBody : function(container)
37957     {
37958         var body = document.createElement("div");
37959         Roo.id(body, "tab-body");
37960         //Roo.fly(body).addClass("x-tabs-body");
37961         Roo.fly(body).addClass("tab-content");
37962         container.appendChild(body);
37963         return body;
37964     },
37965     createItemBody :function(bodyEl, id){
37966         var body = Roo.getDom(id);
37967         if(!body){
37968             body = document.createElement("div");
37969             body.id = id;
37970         }
37971         //Roo.fly(body).addClass("x-tabs-item-body");
37972         Roo.fly(body).addClass("tab-pane");
37973          bodyEl.insertBefore(body, bodyEl.firstChild);
37974         return body;
37975     },
37976     /** @private */
37977     createStripElements :  function(stripEl, text, closable, tpl)
37978     {
37979         var td = document.createElement("li"); // was td..
37980         
37981         
37982         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37983         
37984         
37985         stripEl.appendChild(td);
37986         /*if(closable){
37987             td.className = "x-tabs-closable";
37988             if(!this.closeTpl){
37989                 this.closeTpl = new Roo.Template(
37990                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37991                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37992                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37993                 );
37994             }
37995             var el = this.closeTpl.overwrite(td, {"text": text});
37996             var close = el.getElementsByTagName("div")[0];
37997             var inner = el.getElementsByTagName("em")[0];
37998             return {"el": el, "close": close, "inner": inner};
37999         } else {
38000         */
38001         // not sure what this is..
38002 //            if(!this.tabTpl){
38003                 //this.tabTpl = new Roo.Template(
38004                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38005                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38006                 //);
38007 //                this.tabTpl = new Roo.Template(
38008 //                   '<a href="#">' +
38009 //                   '<span unselectable="on"' +
38010 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38011 //                            ' >{text}</span></a>'
38012 //                );
38013 //                
38014 //            }
38015
38016
38017             var template = tpl || this.tabTpl || false;
38018             
38019             if(!template){
38020                 
38021                 template = new Roo.Template(
38022                    '<a href="#">' +
38023                    '<span unselectable="on"' +
38024                             (this.disableTooltips ? '' : ' title="{text}"') +
38025                             ' >{text}</span></a>'
38026                 );
38027             }
38028             
38029             switch (typeof(template)) {
38030                 case 'object' :
38031                     break;
38032                 case 'string' :
38033                     template = new Roo.Template(template);
38034                     break;
38035                 default :
38036                     break;
38037             }
38038             
38039             var el = template.overwrite(td, {"text": text});
38040             
38041             var inner = el.getElementsByTagName("span")[0];
38042             
38043             return {"el": el, "inner": inner};
38044             
38045     }
38046         
38047     
38048 });
38049
38050 /**
38051  * @class Roo.TabPanelItem
38052  * @extends Roo.util.Observable
38053  * Represents an individual item (tab plus body) in a TabPanel.
38054  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38055  * @param {String} id The id of this TabPanelItem
38056  * @param {String} text The text for the tab of this TabPanelItem
38057  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38058  */
38059 Roo.bootstrap.panel.TabItem = function(config){
38060     /**
38061      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38062      * @type Roo.TabPanel
38063      */
38064     this.tabPanel = config.panel;
38065     /**
38066      * The id for this TabPanelItem
38067      * @type String
38068      */
38069     this.id = config.id;
38070     /** @private */
38071     this.disabled = false;
38072     /** @private */
38073     this.text = config.text;
38074     /** @private */
38075     this.loaded = false;
38076     this.closable = config.closable;
38077
38078     /**
38079      * The body element for this TabPanelItem.
38080      * @type Roo.Element
38081      */
38082     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38083     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38084     this.bodyEl.setStyle("display", "block");
38085     this.bodyEl.setStyle("zoom", "1");
38086     //this.hideAction();
38087
38088     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38089     /** @private */
38090     this.el = Roo.get(els.el);
38091     this.inner = Roo.get(els.inner, true);
38092     this.textEl = Roo.get(this.el.dom.firstChild, true);
38093     this.pnode = Roo.get(els.el.parentNode, true);
38094 //    this.el.on("mousedown", this.onTabMouseDown, this);
38095     this.el.on("click", this.onTabClick, this);
38096     /** @private */
38097     if(config.closable){
38098         var c = Roo.get(els.close, true);
38099         c.dom.title = this.closeText;
38100         c.addClassOnOver("close-over");
38101         c.on("click", this.closeClick, this);
38102      }
38103
38104     this.addEvents({
38105          /**
38106          * @event activate
38107          * Fires when this tab becomes the active tab.
38108          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38109          * @param {Roo.TabPanelItem} this
38110          */
38111         "activate": true,
38112         /**
38113          * @event beforeclose
38114          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38115          * @param {Roo.TabPanelItem} this
38116          * @param {Object} e Set cancel to true on this object to cancel the close.
38117          */
38118         "beforeclose": true,
38119         /**
38120          * @event close
38121          * Fires when this tab is closed.
38122          * @param {Roo.TabPanelItem} this
38123          */
38124          "close": true,
38125         /**
38126          * @event deactivate
38127          * Fires when this tab is no longer the active tab.
38128          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38129          * @param {Roo.TabPanelItem} this
38130          */
38131          "deactivate" : true
38132     });
38133     this.hidden = false;
38134
38135     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38136 };
38137
38138 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38139            {
38140     purgeListeners : function(){
38141        Roo.util.Observable.prototype.purgeListeners.call(this);
38142        this.el.removeAllListeners();
38143     },
38144     /**
38145      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38146      */
38147     show : function(){
38148         this.pnode.addClass("active");
38149         this.showAction();
38150         if(Roo.isOpera){
38151             this.tabPanel.stripWrap.repaint();
38152         }
38153         this.fireEvent("activate", this.tabPanel, this);
38154     },
38155
38156     /**
38157      * Returns true if this tab is the active tab.
38158      * @return {Boolean}
38159      */
38160     isActive : function(){
38161         return this.tabPanel.getActiveTab() == this;
38162     },
38163
38164     /**
38165      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38166      */
38167     hide : function(){
38168         this.pnode.removeClass("active");
38169         this.hideAction();
38170         this.fireEvent("deactivate", this.tabPanel, this);
38171     },
38172
38173     hideAction : function(){
38174         this.bodyEl.hide();
38175         this.bodyEl.setStyle("position", "absolute");
38176         this.bodyEl.setLeft("-20000px");
38177         this.bodyEl.setTop("-20000px");
38178     },
38179
38180     showAction : function(){
38181         this.bodyEl.setStyle("position", "relative");
38182         this.bodyEl.setTop("");
38183         this.bodyEl.setLeft("");
38184         this.bodyEl.show();
38185     },
38186
38187     /**
38188      * Set the tooltip for the tab.
38189      * @param {String} tooltip The tab's tooltip
38190      */
38191     setTooltip : function(text){
38192         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38193             this.textEl.dom.qtip = text;
38194             this.textEl.dom.removeAttribute('title');
38195         }else{
38196             this.textEl.dom.title = text;
38197         }
38198     },
38199
38200     onTabClick : function(e){
38201         e.preventDefault();
38202         this.tabPanel.activate(this.id);
38203     },
38204
38205     onTabMouseDown : function(e){
38206         e.preventDefault();
38207         this.tabPanel.activate(this.id);
38208     },
38209 /*
38210     getWidth : function(){
38211         return this.inner.getWidth();
38212     },
38213
38214     setWidth : function(width){
38215         var iwidth = width - this.pnode.getPadding("lr");
38216         this.inner.setWidth(iwidth);
38217         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38218         this.pnode.setWidth(width);
38219     },
38220 */
38221     /**
38222      * Show or hide the tab
38223      * @param {Boolean} hidden True to hide or false to show.
38224      */
38225     setHidden : function(hidden){
38226         this.hidden = hidden;
38227         this.pnode.setStyle("display", hidden ? "none" : "");
38228     },
38229
38230     /**
38231      * Returns true if this tab is "hidden"
38232      * @return {Boolean}
38233      */
38234     isHidden : function(){
38235         return this.hidden;
38236     },
38237
38238     /**
38239      * Returns the text for this tab
38240      * @return {String}
38241      */
38242     getText : function(){
38243         return this.text;
38244     },
38245     /*
38246     autoSize : function(){
38247         //this.el.beginMeasure();
38248         this.textEl.setWidth(1);
38249         /*
38250          *  #2804 [new] Tabs in Roojs
38251          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38252          */
38253         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38254         //this.el.endMeasure();
38255     //},
38256
38257     /**
38258      * Sets the text for the tab (Note: this also sets the tooltip text)
38259      * @param {String} text The tab's text and tooltip
38260      */
38261     setText : function(text){
38262         this.text = text;
38263         this.textEl.update(text);
38264         this.setTooltip(text);
38265         //if(!this.tabPanel.resizeTabs){
38266         //    this.autoSize();
38267         //}
38268     },
38269     /**
38270      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38271      */
38272     activate : function(){
38273         this.tabPanel.activate(this.id);
38274     },
38275
38276     /**
38277      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38278      */
38279     disable : function(){
38280         if(this.tabPanel.active != this){
38281             this.disabled = true;
38282             this.pnode.addClass("disabled");
38283         }
38284     },
38285
38286     /**
38287      * Enables this TabPanelItem if it was previously disabled.
38288      */
38289     enable : function(){
38290         this.disabled = false;
38291         this.pnode.removeClass("disabled");
38292     },
38293
38294     /**
38295      * Sets the content for this TabPanelItem.
38296      * @param {String} content The content
38297      * @param {Boolean} loadScripts true to look for and load scripts
38298      */
38299     setContent : function(content, loadScripts){
38300         this.bodyEl.update(content, loadScripts);
38301     },
38302
38303     /**
38304      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38305      * @return {Roo.UpdateManager} The UpdateManager
38306      */
38307     getUpdateManager : function(){
38308         return this.bodyEl.getUpdateManager();
38309     },
38310
38311     /**
38312      * Set a URL to be used to load the content for this TabPanelItem.
38313      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38314      * @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)
38315      * @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)
38316      * @return {Roo.UpdateManager} The UpdateManager
38317      */
38318     setUrl : function(url, params, loadOnce){
38319         if(this.refreshDelegate){
38320             this.un('activate', this.refreshDelegate);
38321         }
38322         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38323         this.on("activate", this.refreshDelegate);
38324         return this.bodyEl.getUpdateManager();
38325     },
38326
38327     /** @private */
38328     _handleRefresh : function(url, params, loadOnce){
38329         if(!loadOnce || !this.loaded){
38330             var updater = this.bodyEl.getUpdateManager();
38331             updater.update(url, params, this._setLoaded.createDelegate(this));
38332         }
38333     },
38334
38335     /**
38336      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38337      *   Will fail silently if the setUrl method has not been called.
38338      *   This does not activate the panel, just updates its content.
38339      */
38340     refresh : function(){
38341         if(this.refreshDelegate){
38342            this.loaded = false;
38343            this.refreshDelegate();
38344         }
38345     },
38346
38347     /** @private */
38348     _setLoaded : function(){
38349         this.loaded = true;
38350     },
38351
38352     /** @private */
38353     closeClick : function(e){
38354         var o = {};
38355         e.stopEvent();
38356         this.fireEvent("beforeclose", this, o);
38357         if(o.cancel !== true){
38358             this.tabPanel.removeTab(this.id);
38359         }
38360     },
38361     /**
38362      * The text displayed in the tooltip for the close icon.
38363      * @type String
38364      */
38365     closeText : "Close this tab"
38366 });
38367 /**
38368 *    This script refer to:
38369 *    Title: International Telephone Input
38370 *    Author: Jack O'Connor
38371 *    Code version:  v12.1.12
38372 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38373 **/
38374
38375 Roo.bootstrap.PhoneInputData = function() {
38376     var d = [
38377       [
38378         "Afghanistan (‫افغانستان‬‎)",
38379         "af",
38380         "93"
38381       ],
38382       [
38383         "Albania (Shqipëri)",
38384         "al",
38385         "355"
38386       ],
38387       [
38388         "Algeria (‫الجزائر‬‎)",
38389         "dz",
38390         "213"
38391       ],
38392       [
38393         "American Samoa",
38394         "as",
38395         "1684"
38396       ],
38397       [
38398         "Andorra",
38399         "ad",
38400         "376"
38401       ],
38402       [
38403         "Angola",
38404         "ao",
38405         "244"
38406       ],
38407       [
38408         "Anguilla",
38409         "ai",
38410         "1264"
38411       ],
38412       [
38413         "Antigua and Barbuda",
38414         "ag",
38415         "1268"
38416       ],
38417       [
38418         "Argentina",
38419         "ar",
38420         "54"
38421       ],
38422       [
38423         "Armenia (Հայաստան)",
38424         "am",
38425         "374"
38426       ],
38427       [
38428         "Aruba",
38429         "aw",
38430         "297"
38431       ],
38432       [
38433         "Australia",
38434         "au",
38435         "61",
38436         0
38437       ],
38438       [
38439         "Austria (Österreich)",
38440         "at",
38441         "43"
38442       ],
38443       [
38444         "Azerbaijan (Azərbaycan)",
38445         "az",
38446         "994"
38447       ],
38448       [
38449         "Bahamas",
38450         "bs",
38451         "1242"
38452       ],
38453       [
38454         "Bahrain (‫البحرين‬‎)",
38455         "bh",
38456         "973"
38457       ],
38458       [
38459         "Bangladesh (বাংলাদেশ)",
38460         "bd",
38461         "880"
38462       ],
38463       [
38464         "Barbados",
38465         "bb",
38466         "1246"
38467       ],
38468       [
38469         "Belarus (Беларусь)",
38470         "by",
38471         "375"
38472       ],
38473       [
38474         "Belgium (België)",
38475         "be",
38476         "32"
38477       ],
38478       [
38479         "Belize",
38480         "bz",
38481         "501"
38482       ],
38483       [
38484         "Benin (Bénin)",
38485         "bj",
38486         "229"
38487       ],
38488       [
38489         "Bermuda",
38490         "bm",
38491         "1441"
38492       ],
38493       [
38494         "Bhutan (འབྲུག)",
38495         "bt",
38496         "975"
38497       ],
38498       [
38499         "Bolivia",
38500         "bo",
38501         "591"
38502       ],
38503       [
38504         "Bosnia and Herzegovina (Босна и Херцеговина)",
38505         "ba",
38506         "387"
38507       ],
38508       [
38509         "Botswana",
38510         "bw",
38511         "267"
38512       ],
38513       [
38514         "Brazil (Brasil)",
38515         "br",
38516         "55"
38517       ],
38518       [
38519         "British Indian Ocean Territory",
38520         "io",
38521         "246"
38522       ],
38523       [
38524         "British Virgin Islands",
38525         "vg",
38526         "1284"
38527       ],
38528       [
38529         "Brunei",
38530         "bn",
38531         "673"
38532       ],
38533       [
38534         "Bulgaria (България)",
38535         "bg",
38536         "359"
38537       ],
38538       [
38539         "Burkina Faso",
38540         "bf",
38541         "226"
38542       ],
38543       [
38544         "Burundi (Uburundi)",
38545         "bi",
38546         "257"
38547       ],
38548       [
38549         "Cambodia (កម្ពុជា)",
38550         "kh",
38551         "855"
38552       ],
38553       [
38554         "Cameroon (Cameroun)",
38555         "cm",
38556         "237"
38557       ],
38558       [
38559         "Canada",
38560         "ca",
38561         "1",
38562         1,
38563         ["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"]
38564       ],
38565       [
38566         "Cape Verde (Kabu Verdi)",
38567         "cv",
38568         "238"
38569       ],
38570       [
38571         "Caribbean Netherlands",
38572         "bq",
38573         "599",
38574         1
38575       ],
38576       [
38577         "Cayman Islands",
38578         "ky",
38579         "1345"
38580       ],
38581       [
38582         "Central African Republic (République centrafricaine)",
38583         "cf",
38584         "236"
38585       ],
38586       [
38587         "Chad (Tchad)",
38588         "td",
38589         "235"
38590       ],
38591       [
38592         "Chile",
38593         "cl",
38594         "56"
38595       ],
38596       [
38597         "China (中国)",
38598         "cn",
38599         "86"
38600       ],
38601       [
38602         "Christmas Island",
38603         "cx",
38604         "61",
38605         2
38606       ],
38607       [
38608         "Cocos (Keeling) Islands",
38609         "cc",
38610         "61",
38611         1
38612       ],
38613       [
38614         "Colombia",
38615         "co",
38616         "57"
38617       ],
38618       [
38619         "Comoros (‫جزر القمر‬‎)",
38620         "km",
38621         "269"
38622       ],
38623       [
38624         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38625         "cd",
38626         "243"
38627       ],
38628       [
38629         "Congo (Republic) (Congo-Brazzaville)",
38630         "cg",
38631         "242"
38632       ],
38633       [
38634         "Cook Islands",
38635         "ck",
38636         "682"
38637       ],
38638       [
38639         "Costa Rica",
38640         "cr",
38641         "506"
38642       ],
38643       [
38644         "Côte d’Ivoire",
38645         "ci",
38646         "225"
38647       ],
38648       [
38649         "Croatia (Hrvatska)",
38650         "hr",
38651         "385"
38652       ],
38653       [
38654         "Cuba",
38655         "cu",
38656         "53"
38657       ],
38658       [
38659         "Curaçao",
38660         "cw",
38661         "599",
38662         0
38663       ],
38664       [
38665         "Cyprus (Κύπρος)",
38666         "cy",
38667         "357"
38668       ],
38669       [
38670         "Czech Republic (Česká republika)",
38671         "cz",
38672         "420"
38673       ],
38674       [
38675         "Denmark (Danmark)",
38676         "dk",
38677         "45"
38678       ],
38679       [
38680         "Djibouti",
38681         "dj",
38682         "253"
38683       ],
38684       [
38685         "Dominica",
38686         "dm",
38687         "1767"
38688       ],
38689       [
38690         "Dominican Republic (República Dominicana)",
38691         "do",
38692         "1",
38693         2,
38694         ["809", "829", "849"]
38695       ],
38696       [
38697         "Ecuador",
38698         "ec",
38699         "593"
38700       ],
38701       [
38702         "Egypt (‫مصر‬‎)",
38703         "eg",
38704         "20"
38705       ],
38706       [
38707         "El Salvador",
38708         "sv",
38709         "503"
38710       ],
38711       [
38712         "Equatorial Guinea (Guinea Ecuatorial)",
38713         "gq",
38714         "240"
38715       ],
38716       [
38717         "Eritrea",
38718         "er",
38719         "291"
38720       ],
38721       [
38722         "Estonia (Eesti)",
38723         "ee",
38724         "372"
38725       ],
38726       [
38727         "Ethiopia",
38728         "et",
38729         "251"
38730       ],
38731       [
38732         "Falkland Islands (Islas Malvinas)",
38733         "fk",
38734         "500"
38735       ],
38736       [
38737         "Faroe Islands (Føroyar)",
38738         "fo",
38739         "298"
38740       ],
38741       [
38742         "Fiji",
38743         "fj",
38744         "679"
38745       ],
38746       [
38747         "Finland (Suomi)",
38748         "fi",
38749         "358",
38750         0
38751       ],
38752       [
38753         "France",
38754         "fr",
38755         "33"
38756       ],
38757       [
38758         "French Guiana (Guyane française)",
38759         "gf",
38760         "594"
38761       ],
38762       [
38763         "French Polynesia (Polynésie française)",
38764         "pf",
38765         "689"
38766       ],
38767       [
38768         "Gabon",
38769         "ga",
38770         "241"
38771       ],
38772       [
38773         "Gambia",
38774         "gm",
38775         "220"
38776       ],
38777       [
38778         "Georgia (საქართველო)",
38779         "ge",
38780         "995"
38781       ],
38782       [
38783         "Germany (Deutschland)",
38784         "de",
38785         "49"
38786       ],
38787       [
38788         "Ghana (Gaana)",
38789         "gh",
38790         "233"
38791       ],
38792       [
38793         "Gibraltar",
38794         "gi",
38795         "350"
38796       ],
38797       [
38798         "Greece (Ελλάδα)",
38799         "gr",
38800         "30"
38801       ],
38802       [
38803         "Greenland (Kalaallit Nunaat)",
38804         "gl",
38805         "299"
38806       ],
38807       [
38808         "Grenada",
38809         "gd",
38810         "1473"
38811       ],
38812       [
38813         "Guadeloupe",
38814         "gp",
38815         "590",
38816         0
38817       ],
38818       [
38819         "Guam",
38820         "gu",
38821         "1671"
38822       ],
38823       [
38824         "Guatemala",
38825         "gt",
38826         "502"
38827       ],
38828       [
38829         "Guernsey",
38830         "gg",
38831         "44",
38832         1
38833       ],
38834       [
38835         "Guinea (Guinée)",
38836         "gn",
38837         "224"
38838       ],
38839       [
38840         "Guinea-Bissau (Guiné Bissau)",
38841         "gw",
38842         "245"
38843       ],
38844       [
38845         "Guyana",
38846         "gy",
38847         "592"
38848       ],
38849       [
38850         "Haiti",
38851         "ht",
38852         "509"
38853       ],
38854       [
38855         "Honduras",
38856         "hn",
38857         "504"
38858       ],
38859       [
38860         "Hong Kong (香港)",
38861         "hk",
38862         "852"
38863       ],
38864       [
38865         "Hungary (Magyarország)",
38866         "hu",
38867         "36"
38868       ],
38869       [
38870         "Iceland (Ísland)",
38871         "is",
38872         "354"
38873       ],
38874       [
38875         "India (भारत)",
38876         "in",
38877         "91"
38878       ],
38879       [
38880         "Indonesia",
38881         "id",
38882         "62"
38883       ],
38884       [
38885         "Iran (‫ایران‬‎)",
38886         "ir",
38887         "98"
38888       ],
38889       [
38890         "Iraq (‫العراق‬‎)",
38891         "iq",
38892         "964"
38893       ],
38894       [
38895         "Ireland",
38896         "ie",
38897         "353"
38898       ],
38899       [
38900         "Isle of Man",
38901         "im",
38902         "44",
38903         2
38904       ],
38905       [
38906         "Israel (‫ישראל‬‎)",
38907         "il",
38908         "972"
38909       ],
38910       [
38911         "Italy (Italia)",
38912         "it",
38913         "39",
38914         0
38915       ],
38916       [
38917         "Jamaica",
38918         "jm",
38919         "1876"
38920       ],
38921       [
38922         "Japan (日本)",
38923         "jp",
38924         "81"
38925       ],
38926       [
38927         "Jersey",
38928         "je",
38929         "44",
38930         3
38931       ],
38932       [
38933         "Jordan (‫الأردن‬‎)",
38934         "jo",
38935         "962"
38936       ],
38937       [
38938         "Kazakhstan (Казахстан)",
38939         "kz",
38940         "7",
38941         1
38942       ],
38943       [
38944         "Kenya",
38945         "ke",
38946         "254"
38947       ],
38948       [
38949         "Kiribati",
38950         "ki",
38951         "686"
38952       ],
38953       [
38954         "Kosovo",
38955         "xk",
38956         "383"
38957       ],
38958       [
38959         "Kuwait (‫الكويت‬‎)",
38960         "kw",
38961         "965"
38962       ],
38963       [
38964         "Kyrgyzstan (Кыргызстан)",
38965         "kg",
38966         "996"
38967       ],
38968       [
38969         "Laos (ລາວ)",
38970         "la",
38971         "856"
38972       ],
38973       [
38974         "Latvia (Latvija)",
38975         "lv",
38976         "371"
38977       ],
38978       [
38979         "Lebanon (‫لبنان‬‎)",
38980         "lb",
38981         "961"
38982       ],
38983       [
38984         "Lesotho",
38985         "ls",
38986         "266"
38987       ],
38988       [
38989         "Liberia",
38990         "lr",
38991         "231"
38992       ],
38993       [
38994         "Libya (‫ليبيا‬‎)",
38995         "ly",
38996         "218"
38997       ],
38998       [
38999         "Liechtenstein",
39000         "li",
39001         "423"
39002       ],
39003       [
39004         "Lithuania (Lietuva)",
39005         "lt",
39006         "370"
39007       ],
39008       [
39009         "Luxembourg",
39010         "lu",
39011         "352"
39012       ],
39013       [
39014         "Macau (澳門)",
39015         "mo",
39016         "853"
39017       ],
39018       [
39019         "Macedonia (FYROM) (Македонија)",
39020         "mk",
39021         "389"
39022       ],
39023       [
39024         "Madagascar (Madagasikara)",
39025         "mg",
39026         "261"
39027       ],
39028       [
39029         "Malawi",
39030         "mw",
39031         "265"
39032       ],
39033       [
39034         "Malaysia",
39035         "my",
39036         "60"
39037       ],
39038       [
39039         "Maldives",
39040         "mv",
39041         "960"
39042       ],
39043       [
39044         "Mali",
39045         "ml",
39046         "223"
39047       ],
39048       [
39049         "Malta",
39050         "mt",
39051         "356"
39052       ],
39053       [
39054         "Marshall Islands",
39055         "mh",
39056         "692"
39057       ],
39058       [
39059         "Martinique",
39060         "mq",
39061         "596"
39062       ],
39063       [
39064         "Mauritania (‫موريتانيا‬‎)",
39065         "mr",
39066         "222"
39067       ],
39068       [
39069         "Mauritius (Moris)",
39070         "mu",
39071         "230"
39072       ],
39073       [
39074         "Mayotte",
39075         "yt",
39076         "262",
39077         1
39078       ],
39079       [
39080         "Mexico (México)",
39081         "mx",
39082         "52"
39083       ],
39084       [
39085         "Micronesia",
39086         "fm",
39087         "691"
39088       ],
39089       [
39090         "Moldova (Republica Moldova)",
39091         "md",
39092         "373"
39093       ],
39094       [
39095         "Monaco",
39096         "mc",
39097         "377"
39098       ],
39099       [
39100         "Mongolia (Монгол)",
39101         "mn",
39102         "976"
39103       ],
39104       [
39105         "Montenegro (Crna Gora)",
39106         "me",
39107         "382"
39108       ],
39109       [
39110         "Montserrat",
39111         "ms",
39112         "1664"
39113       ],
39114       [
39115         "Morocco (‫المغرب‬‎)",
39116         "ma",
39117         "212",
39118         0
39119       ],
39120       [
39121         "Mozambique (Moçambique)",
39122         "mz",
39123         "258"
39124       ],
39125       [
39126         "Myanmar (Burma) (မြန်မာ)",
39127         "mm",
39128         "95"
39129       ],
39130       [
39131         "Namibia (Namibië)",
39132         "na",
39133         "264"
39134       ],
39135       [
39136         "Nauru",
39137         "nr",
39138         "674"
39139       ],
39140       [
39141         "Nepal (नेपाल)",
39142         "np",
39143         "977"
39144       ],
39145       [
39146         "Netherlands (Nederland)",
39147         "nl",
39148         "31"
39149       ],
39150       [
39151         "New Caledonia (Nouvelle-Calédonie)",
39152         "nc",
39153         "687"
39154       ],
39155       [
39156         "New Zealand",
39157         "nz",
39158         "64"
39159       ],
39160       [
39161         "Nicaragua",
39162         "ni",
39163         "505"
39164       ],
39165       [
39166         "Niger (Nijar)",
39167         "ne",
39168         "227"
39169       ],
39170       [
39171         "Nigeria",
39172         "ng",
39173         "234"
39174       ],
39175       [
39176         "Niue",
39177         "nu",
39178         "683"
39179       ],
39180       [
39181         "Norfolk Island",
39182         "nf",
39183         "672"
39184       ],
39185       [
39186         "North Korea (조선 민주주의 인민 공화국)",
39187         "kp",
39188         "850"
39189       ],
39190       [
39191         "Northern Mariana Islands",
39192         "mp",
39193         "1670"
39194       ],
39195       [
39196         "Norway (Norge)",
39197         "no",
39198         "47",
39199         0
39200       ],
39201       [
39202         "Oman (‫عُمان‬‎)",
39203         "om",
39204         "968"
39205       ],
39206       [
39207         "Pakistan (‫پاکستان‬‎)",
39208         "pk",
39209         "92"
39210       ],
39211       [
39212         "Palau",
39213         "pw",
39214         "680"
39215       ],
39216       [
39217         "Palestine (‫فلسطين‬‎)",
39218         "ps",
39219         "970"
39220       ],
39221       [
39222         "Panama (Panamá)",
39223         "pa",
39224         "507"
39225       ],
39226       [
39227         "Papua New Guinea",
39228         "pg",
39229         "675"
39230       ],
39231       [
39232         "Paraguay",
39233         "py",
39234         "595"
39235       ],
39236       [
39237         "Peru (Perú)",
39238         "pe",
39239         "51"
39240       ],
39241       [
39242         "Philippines",
39243         "ph",
39244         "63"
39245       ],
39246       [
39247         "Poland (Polska)",
39248         "pl",
39249         "48"
39250       ],
39251       [
39252         "Portugal",
39253         "pt",
39254         "351"
39255       ],
39256       [
39257         "Puerto Rico",
39258         "pr",
39259         "1",
39260         3,
39261         ["787", "939"]
39262       ],
39263       [
39264         "Qatar (‫قطر‬‎)",
39265         "qa",
39266         "974"
39267       ],
39268       [
39269         "Réunion (La Réunion)",
39270         "re",
39271         "262",
39272         0
39273       ],
39274       [
39275         "Romania (România)",
39276         "ro",
39277         "40"
39278       ],
39279       [
39280         "Russia (Россия)",
39281         "ru",
39282         "7",
39283         0
39284       ],
39285       [
39286         "Rwanda",
39287         "rw",
39288         "250"
39289       ],
39290       [
39291         "Saint Barthélemy",
39292         "bl",
39293         "590",
39294         1
39295       ],
39296       [
39297         "Saint Helena",
39298         "sh",
39299         "290"
39300       ],
39301       [
39302         "Saint Kitts and Nevis",
39303         "kn",
39304         "1869"
39305       ],
39306       [
39307         "Saint Lucia",
39308         "lc",
39309         "1758"
39310       ],
39311       [
39312         "Saint Martin (Saint-Martin (partie française))",
39313         "mf",
39314         "590",
39315         2
39316       ],
39317       [
39318         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39319         "pm",
39320         "508"
39321       ],
39322       [
39323         "Saint Vincent and the Grenadines",
39324         "vc",
39325         "1784"
39326       ],
39327       [
39328         "Samoa",
39329         "ws",
39330         "685"
39331       ],
39332       [
39333         "San Marino",
39334         "sm",
39335         "378"
39336       ],
39337       [
39338         "São Tomé and Príncipe (São Tomé e Príncipe)",
39339         "st",
39340         "239"
39341       ],
39342       [
39343         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39344         "sa",
39345         "966"
39346       ],
39347       [
39348         "Senegal (Sénégal)",
39349         "sn",
39350         "221"
39351       ],
39352       [
39353         "Serbia (Србија)",
39354         "rs",
39355         "381"
39356       ],
39357       [
39358         "Seychelles",
39359         "sc",
39360         "248"
39361       ],
39362       [
39363         "Sierra Leone",
39364         "sl",
39365         "232"
39366       ],
39367       [
39368         "Singapore",
39369         "sg",
39370         "65"
39371       ],
39372       [
39373         "Sint Maarten",
39374         "sx",
39375         "1721"
39376       ],
39377       [
39378         "Slovakia (Slovensko)",
39379         "sk",
39380         "421"
39381       ],
39382       [
39383         "Slovenia (Slovenija)",
39384         "si",
39385         "386"
39386       ],
39387       [
39388         "Solomon Islands",
39389         "sb",
39390         "677"
39391       ],
39392       [
39393         "Somalia (Soomaaliya)",
39394         "so",
39395         "252"
39396       ],
39397       [
39398         "South Africa",
39399         "za",
39400         "27"
39401       ],
39402       [
39403         "South Korea (대한민국)",
39404         "kr",
39405         "82"
39406       ],
39407       [
39408         "South Sudan (‫جنوب السودان‬‎)",
39409         "ss",
39410         "211"
39411       ],
39412       [
39413         "Spain (España)",
39414         "es",
39415         "34"
39416       ],
39417       [
39418         "Sri Lanka (ශ්‍රී ලංකාව)",
39419         "lk",
39420         "94"
39421       ],
39422       [
39423         "Sudan (‫السودان‬‎)",
39424         "sd",
39425         "249"
39426       ],
39427       [
39428         "Suriname",
39429         "sr",
39430         "597"
39431       ],
39432       [
39433         "Svalbard and Jan Mayen",
39434         "sj",
39435         "47",
39436         1
39437       ],
39438       [
39439         "Swaziland",
39440         "sz",
39441         "268"
39442       ],
39443       [
39444         "Sweden (Sverige)",
39445         "se",
39446         "46"
39447       ],
39448       [
39449         "Switzerland (Schweiz)",
39450         "ch",
39451         "41"
39452       ],
39453       [
39454         "Syria (‫سوريا‬‎)",
39455         "sy",
39456         "963"
39457       ],
39458       [
39459         "Taiwan (台灣)",
39460         "tw",
39461         "886"
39462       ],
39463       [
39464         "Tajikistan",
39465         "tj",
39466         "992"
39467       ],
39468       [
39469         "Tanzania",
39470         "tz",
39471         "255"
39472       ],
39473       [
39474         "Thailand (ไทย)",
39475         "th",
39476         "66"
39477       ],
39478       [
39479         "Timor-Leste",
39480         "tl",
39481         "670"
39482       ],
39483       [
39484         "Togo",
39485         "tg",
39486         "228"
39487       ],
39488       [
39489         "Tokelau",
39490         "tk",
39491         "690"
39492       ],
39493       [
39494         "Tonga",
39495         "to",
39496         "676"
39497       ],
39498       [
39499         "Trinidad and Tobago",
39500         "tt",
39501         "1868"
39502       ],
39503       [
39504         "Tunisia (‫تونس‬‎)",
39505         "tn",
39506         "216"
39507       ],
39508       [
39509         "Turkey (Türkiye)",
39510         "tr",
39511         "90"
39512       ],
39513       [
39514         "Turkmenistan",
39515         "tm",
39516         "993"
39517       ],
39518       [
39519         "Turks and Caicos Islands",
39520         "tc",
39521         "1649"
39522       ],
39523       [
39524         "Tuvalu",
39525         "tv",
39526         "688"
39527       ],
39528       [
39529         "U.S. Virgin Islands",
39530         "vi",
39531         "1340"
39532       ],
39533       [
39534         "Uganda",
39535         "ug",
39536         "256"
39537       ],
39538       [
39539         "Ukraine (Україна)",
39540         "ua",
39541         "380"
39542       ],
39543       [
39544         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39545         "ae",
39546         "971"
39547       ],
39548       [
39549         "United Kingdom",
39550         "gb",
39551         "44",
39552         0
39553       ],
39554       [
39555         "United States",
39556         "us",
39557         "1",
39558         0
39559       ],
39560       [
39561         "Uruguay",
39562         "uy",
39563         "598"
39564       ],
39565       [
39566         "Uzbekistan (Oʻzbekiston)",
39567         "uz",
39568         "998"
39569       ],
39570       [
39571         "Vanuatu",
39572         "vu",
39573         "678"
39574       ],
39575       [
39576         "Vatican City (Città del Vaticano)",
39577         "va",
39578         "39",
39579         1
39580       ],
39581       [
39582         "Venezuela",
39583         "ve",
39584         "58"
39585       ],
39586       [
39587         "Vietnam (Việt Nam)",
39588         "vn",
39589         "84"
39590       ],
39591       [
39592         "Wallis and Futuna (Wallis-et-Futuna)",
39593         "wf",
39594         "681"
39595       ],
39596       [
39597         "Western Sahara (‫الصحراء الغربية‬‎)",
39598         "eh",
39599         "212",
39600         1
39601       ],
39602       [
39603         "Yemen (‫اليمن‬‎)",
39604         "ye",
39605         "967"
39606       ],
39607       [
39608         "Zambia",
39609         "zm",
39610         "260"
39611       ],
39612       [
39613         "Zimbabwe",
39614         "zw",
39615         "263"
39616       ],
39617       [
39618         "Åland Islands",
39619         "ax",
39620         "358",
39621         1
39622       ]
39623   ];
39624   
39625   return d;
39626 }/**
39627 *    This script refer to:
39628 *    Title: International Telephone Input
39629 *    Author: Jack O'Connor
39630 *    Code version:  v12.1.12
39631 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39632 **/
39633
39634 /**
39635  * @class Roo.bootstrap.PhoneInput
39636  * @extends Roo.bootstrap.TriggerField
39637  * An input with International dial-code selection
39638  
39639  * @cfg {String} defaultDialCode default '+852'
39640  * @cfg {Array} preferedCountries default []
39641   
39642  * @constructor
39643  * Create a new PhoneInput.
39644  * @param {Object} config Configuration options
39645  */
39646
39647 Roo.bootstrap.PhoneInput = function(config) {
39648     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39649 };
39650
39651 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39652         
39653         listWidth: undefined,
39654         
39655         selectedClass: 'active',
39656         
39657         invalidClass : "has-warning",
39658         
39659         validClass: 'has-success',
39660         
39661         allowed: '0123456789',
39662         
39663         /**
39664          * @cfg {String} defaultDialCode The default dial code when initializing the input
39665          */
39666         defaultDialCode: '+852',
39667         
39668         /**
39669          * @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
39670          */
39671         preferedCountries: false,
39672         
39673         getAutoCreate : function()
39674         {
39675             var data = Roo.bootstrap.PhoneInputData();
39676             var align = this.labelAlign || this.parentLabelAlign();
39677             var id = Roo.id();
39678             
39679             this.allCountries = [];
39680             this.dialCodeMapping = [];
39681             
39682             for (var i = 0; i < data.length; i++) {
39683               var c = data[i];
39684               this.allCountries[i] = {
39685                 name: c[0],
39686                 iso2: c[1],
39687                 dialCode: c[2],
39688                 priority: c[3] || 0,
39689                 areaCodes: c[4] || null
39690               };
39691               this.dialCodeMapping[c[2]] = {
39692                   name: c[0],
39693                   iso2: c[1],
39694                   priority: c[3] || 0,
39695                   areaCodes: c[4] || null
39696               };
39697             }
39698             
39699             var cfg = {
39700                 cls: 'form-group',
39701                 cn: []
39702             };
39703             
39704             var input =  {
39705                 tag: 'input',
39706                 id : id,
39707                 cls : 'form-control tel-input',
39708                 autocomplete: 'new-password'
39709             };
39710             
39711             var hiddenInput = {
39712                 tag: 'input',
39713                 type: 'hidden',
39714                 cls: 'hidden-tel-input'
39715             };
39716             
39717             if (this.name) {
39718                 hiddenInput.name = this.name;
39719             }
39720             
39721             if (this.disabled) {
39722                 input.disabled = true;
39723             }
39724             
39725             var flag_container = {
39726                 tag: 'div',
39727                 cls: 'flag-box',
39728                 cn: [
39729                     {
39730                         tag: 'div',
39731                         cls: 'flag'
39732                     },
39733                     {
39734                         tag: 'div',
39735                         cls: 'caret'
39736                     }
39737                 ]
39738             };
39739             
39740             var box = {
39741                 tag: 'div',
39742                 cls: this.hasFeedback ? 'has-feedback' : '',
39743                 cn: [
39744                     hiddenInput,
39745                     input,
39746                     {
39747                         tag: 'input',
39748                         cls: 'dial-code-holder',
39749                         disabled: true
39750                     }
39751                 ]
39752             };
39753             
39754             var container = {
39755                 cls: 'roo-select2-container input-group',
39756                 cn: [
39757                     flag_container,
39758                     box
39759                 ]
39760             };
39761             
39762             if (this.fieldLabel.length) {
39763                 var indicator = {
39764                     tag: 'i',
39765                     tooltip: 'This field is required'
39766                 };
39767                 
39768                 var label = {
39769                     tag: 'label',
39770                     'for':  id,
39771                     cls: 'control-label',
39772                     cn: []
39773                 };
39774                 
39775                 var label_text = {
39776                     tag: 'span',
39777                     html: this.fieldLabel
39778                 };
39779                 
39780                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39781                 label.cn = [
39782                     indicator,
39783                     label_text
39784                 ];
39785                 
39786                 if(this.indicatorpos == 'right') {
39787                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39788                     label.cn = [
39789                         label_text,
39790                         indicator
39791                     ];
39792                 }
39793                 
39794                 if(align == 'left') {
39795                     container = {
39796                         tag: 'div',
39797                         cn: [
39798                             container
39799                         ]
39800                     };
39801                     
39802                     if(this.labelWidth > 12){
39803                         label.style = "width: " + this.labelWidth + 'px';
39804                     }
39805                     if(this.labelWidth < 13 && this.labelmd == 0){
39806                         this.labelmd = this.labelWidth;
39807                     }
39808                     if(this.labellg > 0){
39809                         label.cls += ' col-lg-' + this.labellg;
39810                         input.cls += ' col-lg-' + (12 - this.labellg);
39811                     }
39812                     if(this.labelmd > 0){
39813                         label.cls += ' col-md-' + this.labelmd;
39814                         container.cls += ' col-md-' + (12 - this.labelmd);
39815                     }
39816                     if(this.labelsm > 0){
39817                         label.cls += ' col-sm-' + this.labelsm;
39818                         container.cls += ' col-sm-' + (12 - this.labelsm);
39819                     }
39820                     if(this.labelxs > 0){
39821                         label.cls += ' col-xs-' + this.labelxs;
39822                         container.cls += ' col-xs-' + (12 - this.labelxs);
39823                     }
39824                 }
39825             }
39826             
39827             cfg.cn = [
39828                 label,
39829                 container
39830             ];
39831             
39832             var settings = this;
39833             
39834             ['xs','sm','md','lg'].map(function(size){
39835                 if (settings[size]) {
39836                     cfg.cls += ' col-' + size + '-' + settings[size];
39837                 }
39838             });
39839             
39840             this.store = new Roo.data.Store({
39841                 proxy : new Roo.data.MemoryProxy({}),
39842                 reader : new Roo.data.JsonReader({
39843                     fields : [
39844                         {
39845                             'name' : 'name',
39846                             'type' : 'string'
39847                         },
39848                         {
39849                             'name' : 'iso2',
39850                             'type' : 'string'
39851                         },
39852                         {
39853                             'name' : 'dialCode',
39854                             'type' : 'string'
39855                         },
39856                         {
39857                             'name' : 'priority',
39858                             'type' : 'string'
39859                         },
39860                         {
39861                             'name' : 'areaCodes',
39862                             'type' : 'string'
39863                         }
39864                     ]
39865                 })
39866             });
39867             
39868             if(!this.preferedCountries) {
39869                 this.preferedCountries = [
39870                     'hk',
39871                     'gb',
39872                     'us'
39873                 ];
39874             }
39875             
39876             var p = this.preferedCountries.reverse();
39877             
39878             if(p) {
39879                 for (var i = 0; i < p.length; i++) {
39880                     for (var j = 0; j < this.allCountries.length; j++) {
39881                         if(this.allCountries[j].iso2 == p[i]) {
39882                             var t = this.allCountries[j];
39883                             this.allCountries.splice(j,1);
39884                             this.allCountries.unshift(t);
39885                         }
39886                     } 
39887                 }
39888             }
39889             
39890             this.store.proxy.data = {
39891                 success: true,
39892                 data: this.allCountries
39893             };
39894             
39895             return cfg;
39896         },
39897         
39898         initEvents : function()
39899         {
39900             this.createList();
39901             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39902             
39903             this.indicator = this.indicatorEl();
39904             this.flag = this.flagEl();
39905             this.dialCodeHolder = this.dialCodeHolderEl();
39906             
39907             this.trigger = this.el.select('div.flag-box',true).first();
39908             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39909             
39910             var _this = this;
39911             
39912             (function(){
39913                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39914                 _this.list.setWidth(lw);
39915             }).defer(100);
39916             
39917             this.list.on('mouseover', this.onViewOver, this);
39918             this.list.on('mousemove', this.onViewMove, this);
39919             this.inputEl().on("keyup", this.onKeyUp, this);
39920             
39921             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39922
39923             this.view = new Roo.View(this.list, this.tpl, {
39924                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39925             });
39926             
39927             this.view.on('click', this.onViewClick, this);
39928             this.setValue(this.defaultDialCode);
39929         },
39930         
39931         onTriggerClick : function(e)
39932         {
39933             Roo.log('trigger click');
39934             if(this.disabled){
39935                 return;
39936             }
39937             
39938             if(this.isExpanded()){
39939                 this.collapse();
39940                 this.hasFocus = false;
39941             }else {
39942                 this.store.load({});
39943                 this.hasFocus = true;
39944                 this.expand();
39945             }
39946         },
39947         
39948         isExpanded : function()
39949         {
39950             return this.list.isVisible();
39951         },
39952         
39953         collapse : function()
39954         {
39955             if(!this.isExpanded()){
39956                 return;
39957             }
39958             this.list.hide();
39959             Roo.get(document).un('mousedown', this.collapseIf, this);
39960             Roo.get(document).un('mousewheel', this.collapseIf, this);
39961             this.fireEvent('collapse', this);
39962             this.validate();
39963         },
39964         
39965         expand : function()
39966         {
39967             Roo.log('expand');
39968
39969             if(this.isExpanded() || !this.hasFocus){
39970                 return;
39971             }
39972             
39973             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39974             this.list.setWidth(lw);
39975             
39976             this.list.show();
39977             this.restrictHeight();
39978             
39979             Roo.get(document).on('mousedown', this.collapseIf, this);
39980             Roo.get(document).on('mousewheel', this.collapseIf, this);
39981             
39982             this.fireEvent('expand', this);
39983         },
39984         
39985         restrictHeight : function()
39986         {
39987             this.list.alignTo(this.inputEl(), this.listAlign);
39988             this.list.alignTo(this.inputEl(), this.listAlign);
39989         },
39990         
39991         onViewOver : function(e, t)
39992         {
39993             if(this.inKeyMode){
39994                 return;
39995             }
39996             var item = this.view.findItemFromChild(t);
39997             
39998             if(item){
39999                 var index = this.view.indexOf(item);
40000                 this.select(index, false);
40001             }
40002         },
40003
40004         // private
40005         onViewClick : function(view, doFocus, el, e)
40006         {
40007             var index = this.view.getSelectedIndexes()[0];
40008             
40009             var r = this.store.getAt(index);
40010             
40011             if(r){
40012                 this.onSelect(r, index);
40013             }
40014             if(doFocus !== false && !this.blockFocus){
40015                 this.inputEl().focus();
40016             }
40017         },
40018         
40019         onViewMove : function(e, t)
40020         {
40021             this.inKeyMode = false;
40022         },
40023         
40024         select : function(index, scrollIntoView)
40025         {
40026             this.selectedIndex = index;
40027             this.view.select(index);
40028             if(scrollIntoView !== false){
40029                 var el = this.view.getNode(index);
40030                 if(el){
40031                     this.list.scrollChildIntoView(el, false);
40032                 }
40033             }
40034         },
40035         
40036         createList : function()
40037         {
40038             this.list = Roo.get(document.body).createChild({
40039                 tag: 'ul',
40040                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40041                 style: 'display:none'
40042             });
40043             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
40044         },
40045         
40046         collapseIf : function(e)
40047         {
40048             var in_combo  = e.within(this.el);
40049             var in_list =  e.within(this.list);
40050             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40051             
40052             if (in_combo || in_list || is_list) {
40053                 return;
40054             }
40055             this.collapse();
40056         },
40057         
40058         onSelect : function(record, index)
40059         {
40060             if(this.fireEvent('beforeselect', this, record, index) !== false){
40061                 
40062                 this.setFlagClass(record.data.iso2);
40063                 this.setDialCode(record.data.dialCode);
40064                 this.hasFocus = false;
40065                 this.collapse();
40066                 this.fireEvent('select', this, record, index);
40067             }
40068         },
40069         
40070         flagEl : function()
40071         {
40072             var flag = this.el.select('div.flag',true).first();
40073             if(!flag){
40074                 return false;
40075             }
40076             return flag;
40077         },
40078         
40079         dialCodeHolderEl : function()
40080         {
40081             var d = this.el.select('input.dial-code-holder',true).first();
40082             if(!d){
40083                 return false;
40084             }
40085             return d;
40086         },
40087         
40088         setDialCode : function(v)
40089         {
40090             this.dialCodeHolder.dom.value = '+'+v;
40091         },
40092         
40093         setFlagClass : function(n)
40094         {
40095             this.flag.dom.className = 'flag '+n;
40096         },
40097         
40098         getValue : function()
40099         {
40100             var v = this.inputEl().getValue();
40101             if(this.dialCodeHolder) {
40102                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40103             }
40104             return v;
40105         },
40106         
40107         setValue : function(v)
40108         {
40109             var d = this.getDialCode(v);
40110             
40111             //invalid dial code
40112             if(v.length == 0 || !d || d.length == 0) {
40113                 if(this.rendered){
40114                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40115                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40116                 }
40117                 return;
40118             }
40119             
40120             //valid dial code
40121             this.setFlagClass(this.dialCodeMapping[d].iso2);
40122             this.setDialCode(d);
40123             this.inputEl().dom.value = v.replace('+'+d,'');
40124             this.hiddenEl().dom.value = this.getValue();
40125             
40126             this.validate();
40127         },
40128         
40129         getDialCode : function(v = '')
40130         {
40131             if (v.length == 0) {
40132                 return this.dialCodeHolder.dom.value;
40133             }
40134             
40135             var dialCode = "";
40136             if (v.charAt(0) != "+") {
40137                 return false;
40138             }
40139             var numericChars = "";
40140             for (var i = 1; i < v.length; i++) {
40141               var c = v.charAt(i);
40142               if (!isNaN(c)) {
40143                 numericChars += c;
40144                 if (this.dialCodeMapping[numericChars]) {
40145                   dialCode = v.substr(1, i);
40146                 }
40147                 if (numericChars.length == 4) {
40148                   break;
40149                 }
40150               }
40151             }
40152             return dialCode;
40153         },
40154         
40155         reset : function()
40156         {
40157             this.setValue(this.defaultDialCode);
40158             this.validate();
40159         },
40160         
40161         hiddenEl : function()
40162         {
40163             return this.el.select('input.hidden-tel-input',true).first();
40164         },
40165         
40166         onKeyUp : function(e){
40167             
40168             var k = e.getKey();
40169             var c = e.getCharCode();
40170             
40171             if(
40172                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40173                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40174             ){
40175                 e.stopEvent();
40176             }
40177             
40178             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40179             //     return;
40180             // }
40181             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40182                 e.stopEvent();
40183             }
40184             
40185             this.setValue(this.getValue());
40186         }
40187         
40188 });
40189 /**
40190  * @class Roo.bootstrap.MoneyField
40191  * @extends Roo.bootstrap.ComboBox
40192  * Bootstrap MoneyField class
40193  * 
40194  * @constructor
40195  * Create a new MoneyField.
40196  * @param {Object} config Configuration options
40197  */
40198
40199 Roo.bootstrap.MoneyField = function(config) {
40200     
40201     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40202     
40203 };
40204
40205 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40206     
40207     /**
40208      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40209      */
40210     allowDecimals : true,
40211     /**
40212      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40213      */
40214     decimalSeparator : ".",
40215     /**
40216      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40217      */
40218     decimalPrecision : 0,
40219     /**
40220      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40221      */
40222     allowNegative : true,
40223     /**
40224      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40225      */
40226     minValue : Number.NEGATIVE_INFINITY,
40227     /**
40228      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40229      */
40230     maxValue : Number.MAX_VALUE,
40231     /**
40232      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40233      */
40234     minText : "The minimum value for this field is {0}",
40235     /**
40236      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40237      */
40238     maxText : "The maximum value for this field is {0}",
40239     /**
40240      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40241      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40242      */
40243     nanText : "{0} is not a valid number",
40244     /**
40245      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40246      */
40247     castInt : true,
40248     /**
40249      * @cfg {String} defaults currency of the MoneyField
40250      * value should be in lkey
40251      */
40252     defaultCurrency : false,
40253     /**
40254      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40255      */
40256     thousandsDelimiter : false,
40257     
40258     
40259     inputlg : 9,
40260     inputmd : 9,
40261     inputsm : 9,
40262     inputxs : 6,
40263     
40264     store : false,
40265     
40266     getAutoCreate : function()
40267     {
40268         var align = this.labelAlign || this.parentLabelAlign();
40269         
40270         var id = Roo.id();
40271
40272         var cfg = {
40273             cls: 'form-group',
40274             cn: []
40275         };
40276
40277         var input =  {
40278             tag: 'input',
40279             id : id,
40280             cls : 'form-control roo-money-amount-input',
40281             autocomplete: 'new-password'
40282         };
40283         
40284         var hiddenInput = {
40285             tag: 'input',
40286             type: 'hidden',
40287             id: Roo.id(),
40288             cls: 'hidden-number-input'
40289         };
40290         
40291         if (this.name) {
40292             hiddenInput.name = this.name;
40293         }
40294
40295         if (this.disabled) {
40296             input.disabled = true;
40297         }
40298
40299         var clg = 12 - this.inputlg;
40300         var cmd = 12 - this.inputmd;
40301         var csm = 12 - this.inputsm;
40302         var cxs = 12 - this.inputxs;
40303         
40304         var container = {
40305             tag : 'div',
40306             cls : 'row roo-money-field',
40307             cn : [
40308                 {
40309                     tag : 'div',
40310                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40311                     cn : [
40312                         {
40313                             tag : 'div',
40314                             cls: 'roo-select2-container input-group',
40315                             cn: [
40316                                 {
40317                                     tag : 'input',
40318                                     cls : 'form-control roo-money-currency-input',
40319                                     autocomplete: 'new-password',
40320                                     readOnly : 1,
40321                                     name : this.currencyName
40322                                 },
40323                                 {
40324                                     tag :'span',
40325                                     cls : 'input-group-addon',
40326                                     cn : [
40327                                         {
40328                                             tag: 'span',
40329                                             cls: 'caret'
40330                                         }
40331                                     ]
40332                                 }
40333                             ]
40334                         }
40335                     ]
40336                 },
40337                 {
40338                     tag : 'div',
40339                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40340                     cn : [
40341                         {
40342                             tag: 'div',
40343                             cls: this.hasFeedback ? 'has-feedback' : '',
40344                             cn: [
40345                                 input
40346                             ]
40347                         }
40348                     ]
40349                 }
40350             ]
40351             
40352         };
40353         
40354         if (this.fieldLabel.length) {
40355             var indicator = {
40356                 tag: 'i',
40357                 tooltip: 'This field is required'
40358             };
40359
40360             var label = {
40361                 tag: 'label',
40362                 'for':  id,
40363                 cls: 'control-label',
40364                 cn: []
40365             };
40366
40367             var label_text = {
40368                 tag: 'span',
40369                 html: this.fieldLabel
40370             };
40371
40372             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40373             label.cn = [
40374                 indicator,
40375                 label_text
40376             ];
40377
40378             if(this.indicatorpos == 'right') {
40379                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40380                 label.cn = [
40381                     label_text,
40382                     indicator
40383                 ];
40384             }
40385
40386             if(align == 'left') {
40387                 container = {
40388                     tag: 'div',
40389                     cn: [
40390                         container
40391                     ]
40392                 };
40393
40394                 if(this.labelWidth > 12){
40395                     label.style = "width: " + this.labelWidth + 'px';
40396                 }
40397                 if(this.labelWidth < 13 && this.labelmd == 0){
40398                     this.labelmd = this.labelWidth;
40399                 }
40400                 if(this.labellg > 0){
40401                     label.cls += ' col-lg-' + this.labellg;
40402                     input.cls += ' col-lg-' + (12 - this.labellg);
40403                 }
40404                 if(this.labelmd > 0){
40405                     label.cls += ' col-md-' + this.labelmd;
40406                     container.cls += ' col-md-' + (12 - this.labelmd);
40407                 }
40408                 if(this.labelsm > 0){
40409                     label.cls += ' col-sm-' + this.labelsm;
40410                     container.cls += ' col-sm-' + (12 - this.labelsm);
40411                 }
40412                 if(this.labelxs > 0){
40413                     label.cls += ' col-xs-' + this.labelxs;
40414                     container.cls += ' col-xs-' + (12 - this.labelxs);
40415                 }
40416             }
40417         }
40418
40419         cfg.cn = [
40420             label,
40421             container,
40422             hiddenInput
40423         ];
40424         
40425         var settings = this;
40426
40427         ['xs','sm','md','lg'].map(function(size){
40428             if (settings[size]) {
40429                 cfg.cls += ' col-' + size + '-' + settings[size];
40430             }
40431         });
40432         
40433         return cfg;
40434     },
40435     
40436     initEvents : function()
40437     {
40438         this.indicator = this.indicatorEl();
40439         
40440         this.initCurrencyEvent();
40441         
40442         this.initNumberEvent();
40443     },
40444     
40445     initCurrencyEvent : function()
40446     {
40447         if (!this.store) {
40448             throw "can not find store for combo";
40449         }
40450         
40451         this.store = Roo.factory(this.store, Roo.data);
40452         this.store.parent = this;
40453         
40454         this.createList();
40455         
40456         this.triggerEl = this.el.select('.input-group-addon', true).first();
40457         
40458         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40459         
40460         var _this = this;
40461         
40462         (function(){
40463             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40464             _this.list.setWidth(lw);
40465         }).defer(100);
40466         
40467         this.list.on('mouseover', this.onViewOver, this);
40468         this.list.on('mousemove', this.onViewMove, this);
40469         this.list.on('scroll', this.onViewScroll, this);
40470         
40471         if(!this.tpl){
40472             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40473         }
40474         
40475         this.view = new Roo.View(this.list, this.tpl, {
40476             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40477         });
40478         
40479         this.view.on('click', this.onViewClick, this);
40480         
40481         this.store.on('beforeload', this.onBeforeLoad, this);
40482         this.store.on('load', this.onLoad, this);
40483         this.store.on('loadexception', this.onLoadException, this);
40484         
40485         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40486             "up" : function(e){
40487                 this.inKeyMode = true;
40488                 this.selectPrev();
40489             },
40490
40491             "down" : function(e){
40492                 if(!this.isExpanded()){
40493                     this.onTriggerClick();
40494                 }else{
40495                     this.inKeyMode = true;
40496                     this.selectNext();
40497                 }
40498             },
40499
40500             "enter" : function(e){
40501                 this.collapse();
40502                 
40503                 if(this.fireEvent("specialkey", this, e)){
40504                     this.onViewClick(false);
40505                 }
40506                 
40507                 return true;
40508             },
40509
40510             "esc" : function(e){
40511                 this.collapse();
40512             },
40513
40514             "tab" : function(e){
40515                 this.collapse();
40516                 
40517                 if(this.fireEvent("specialkey", this, e)){
40518                     this.onViewClick(false);
40519                 }
40520                 
40521                 return true;
40522             },
40523
40524             scope : this,
40525
40526             doRelay : function(foo, bar, hname){
40527                 if(hname == 'down' || this.scope.isExpanded()){
40528                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40529                 }
40530                 return true;
40531             },
40532
40533             forceKeyDown: true
40534         });
40535         
40536         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40537         
40538     },
40539     
40540     initNumberEvent : function(e)
40541     {
40542         this.inputEl().on("keydown" , this.fireKey,  this);
40543         this.inputEl().on("focus", this.onFocus,  this);
40544         this.inputEl().on("blur", this.onBlur,  this);
40545         
40546         this.inputEl().relayEvent('keyup', this);
40547         
40548         if(this.indicator){
40549             this.indicator.addClass('invisible');
40550         }
40551  
40552         this.originalValue = this.getValue();
40553         
40554         if(this.validationEvent == 'keyup'){
40555             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40556             this.inputEl().on('keyup', this.filterValidation, this);
40557         }
40558         else if(this.validationEvent !== false){
40559             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40560         }
40561         
40562         if(this.selectOnFocus){
40563             this.on("focus", this.preFocus, this);
40564             
40565         }
40566         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40567             this.inputEl().on("keypress", this.filterKeys, this);
40568         } else {
40569             this.inputEl().relayEvent('keypress', this);
40570         }
40571         
40572         var allowed = "0123456789";
40573         
40574         if(this.allowDecimals){
40575             allowed += this.decimalSeparator;
40576         }
40577         
40578         if(this.allowNegative){
40579             allowed += "-";
40580         }
40581         
40582         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40583         
40584         var keyPress = function(e){
40585             
40586             var k = e.getKey();
40587             
40588             var c = e.getCharCode();
40589             
40590             if(
40591                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40592                     allowed.indexOf(String.fromCharCode(c)) === -1
40593             ){
40594                 e.stopEvent();
40595                 return;
40596             }
40597             
40598             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40599                 return;
40600             }
40601             
40602             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40603                 e.stopEvent();
40604             }
40605         };
40606         
40607         this.inputEl().on("keypress", keyPress, this);
40608         
40609     },
40610     
40611     onTriggerClick : function(e)
40612     {   
40613         if(this.disabled){
40614             return;
40615         }
40616         
40617         this.page = 0;
40618         this.loadNext = false;
40619         
40620         if(this.isExpanded()){
40621             this.collapse();
40622             return;
40623         }
40624         
40625         this.hasFocus = true;
40626         
40627         if(this.triggerAction == 'all') {
40628             this.doQuery(this.allQuery, true);
40629             return;
40630         }
40631         
40632         this.doQuery(this.getRawValue());
40633     },
40634     
40635     getCurrency : function()
40636     {   
40637         var v = this.currencyEl().getValue();
40638         
40639         return v;
40640     },
40641     
40642     restrictHeight : function()
40643     {
40644         this.list.alignTo(this.currencyEl(), this.listAlign);
40645         this.list.alignTo(this.currencyEl(), this.listAlign);
40646     },
40647     
40648     onViewClick : function(view, doFocus, el, e)
40649     {
40650         var index = this.view.getSelectedIndexes()[0];
40651         
40652         var r = this.store.getAt(index);
40653         
40654         if(r){
40655             this.onSelect(r, index);
40656         }
40657     },
40658     
40659     onSelect : function(record, index){
40660         
40661         if(this.fireEvent('beforeselect', this, record, index) !== false){
40662         
40663             this.setFromCurrencyData(index > -1 ? record.data : false);
40664             
40665             this.collapse();
40666             
40667             this.fireEvent('select', this, record, index);
40668         }
40669     },
40670     
40671     setFromCurrencyData : function(o)
40672     {
40673         var currency = '';
40674         
40675         this.lastCurrency = o;
40676         
40677         if (this.currencyField) {
40678             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40679         } else {
40680             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40681         }
40682         
40683         this.lastSelectionText = currency;
40684         
40685         //setting default currency
40686         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40687             this.setCurrency(this.defaultCurrency);
40688             return;
40689         }
40690         
40691         this.setCurrency(currency);
40692     },
40693     
40694     setFromData : function(o)
40695     {
40696         var c = {};
40697         
40698         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40699         
40700         this.setFromCurrencyData(c);
40701         
40702         var value = '';
40703         
40704         if (this.name) {
40705             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40706         } else {
40707             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40708         }
40709         
40710         this.setValue(value);
40711         
40712     },
40713     
40714     setCurrency : function(v)
40715     {   
40716         this.currencyValue = v;
40717         
40718         if(this.rendered){
40719             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40720             this.validate();
40721         }
40722     },
40723     
40724     setValue : function(v)
40725     {
40726         v = this.fixPrecision(v);
40727         
40728         v = String(v).replace(".", this.decimalSeparator);
40729         
40730         this.value = v;
40731         
40732         if(this.rendered){
40733             
40734             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40735             
40736             this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision, 
40737                 this.thousandsDelimiter || ','
40738             );
40739             
40740             if(this.allowBlank && !v) {
40741                 this.inputEl().dom.value = '';
40742             }
40743             
40744             this.validate();
40745         }
40746     },
40747     
40748     getRawValue : function()
40749     {
40750         var v = this.inputEl().getValue();
40751         
40752         return v;
40753     },
40754     
40755     getValue : function()
40756     {
40757         return this.fixPrecision(this.parseValue(this.getRawValue()));
40758     },
40759     
40760     parseValue : function(value)
40761     {
40762         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40763         return isNaN(value) ? '' : value;
40764     },
40765     
40766     fixPrecision : function(value)
40767     {
40768         var nan = isNaN(value);
40769         
40770         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40771             return nan ? '' : value;
40772         }
40773         
40774         return parseFloat(value).toFixed(this.decimalPrecision);
40775     },
40776     
40777     decimalPrecisionFcn : function(v)
40778     {
40779         return Math.floor(v);
40780     },
40781     
40782     validateValue : function(value)
40783     {
40784         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40785             return false;
40786         }
40787         
40788         var num = this.parseValue(value);
40789         
40790         if(isNaN(num)){
40791             this.markInvalid(String.format(this.nanText, value));
40792             return false;
40793         }
40794         
40795         if(num < this.minValue){
40796             this.markInvalid(String.format(this.minText, this.minValue));
40797             return false;
40798         }
40799         
40800         if(num > this.maxValue){
40801             this.markInvalid(String.format(this.maxText, this.maxValue));
40802             return false;
40803         }
40804         
40805         return true;
40806     },
40807     
40808     validate : function()
40809     {
40810         if(this.disabled || this.allowBlank){
40811             this.markValid();
40812             return true;
40813         }
40814         
40815         var currency = this.getCurrency();
40816         
40817         if(this.validateValue(this.getRawValue()) && currency.length){
40818             this.markValid();
40819             return true;
40820         }
40821         
40822         this.markInvalid();
40823         return false;
40824     },
40825     
40826     getName: function()
40827     {
40828         return this.name;
40829     },
40830     
40831     beforeBlur : function()
40832     {
40833         if(!this.castInt){
40834             return;
40835         }
40836         
40837         var v = this.parseValue(this.getRawValue());
40838         
40839         if(v || v == 0){
40840             this.setValue(v);
40841         }
40842     },
40843     
40844     onBlur : function()
40845     {
40846         this.beforeBlur();
40847         
40848         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40849             //this.el.removeClass(this.focusClass);
40850         }
40851         
40852         this.hasFocus = false;
40853         
40854         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40855             this.validate();
40856         }
40857         
40858         var v = this.getValue();
40859         
40860         if(String(v) !== String(this.startValue)){
40861             this.fireEvent('change', this, v, this.startValue);
40862         }
40863         
40864         this.fireEvent("blur", this);
40865     },
40866     
40867     inputEl : function()
40868     {
40869         return this.el.select('.roo-money-amount-input', true).first();
40870     },
40871     
40872     currencyEl : function()
40873     {
40874         return this.el.select('.roo-money-currency-input', true).first();
40875     },
40876     
40877     hiddenEl : function()
40878     {
40879         return this.el.select('input.hidden-number-input',true).first();
40880     }
40881     
40882 });