Roo/bootstrap/ComboBox.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  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372     }
373 });
374
375  /*
376  * - LGPL
377  *
378  * Body
379  *
380  */
381
382 /**
383  * @class Roo.bootstrap.Body
384  * @extends Roo.bootstrap.Component
385  * Bootstrap Body class
386  *
387  * @constructor
388  * Create a new body
389  * @param {Object} config The config object
390  */
391
392 Roo.bootstrap.Body = function(config){
393
394     config = config || {};
395
396     Roo.bootstrap.Body.superclass.constructor.call(this, config);
397     this.el = Roo.get(config.el ? config.el : document.body );
398     if (this.cls && this.cls.length) {
399         Roo.get(document.body).addClass(this.cls);
400     }
401 };
402
403 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
404
405     is_body : true,// just to make sure it's constructed?
406
407         autoCreate : {
408         cls: 'container'
409     },
410     onRender : function(ct, position)
411     {
412        /* Roo.log("Roo.bootstrap.Body - onRender");
413         if (this.cls && this.cls.length) {
414             Roo.get(document.body).addClass(this.cls);
415         }
416         // style??? xttr???
417         */
418     }
419
420
421
422
423 });
424 /*
425  * - LGPL
426  *
427  * button group
428  * 
429  */
430
431
432 /**
433  * @class Roo.bootstrap.ButtonGroup
434  * @extends Roo.bootstrap.Component
435  * Bootstrap ButtonGroup class
436  * @cfg {String} size lg | sm | xs (default empty normal)
437  * @cfg {String} align vertical | justified  (default none)
438  * @cfg {String} direction up | down (default down)
439  * @cfg {Boolean} toolbar false | true
440  * @cfg {Boolean} btn true | false
441  * 
442  * 
443  * @constructor
444  * Create a new Input
445  * @param {Object} config The config object
446  */
447
448 Roo.bootstrap.ButtonGroup = function(config){
449     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
450 };
451
452 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
453     
454     size: '',
455     align: '',
456     direction: '',
457     toolbar: false,
458     btn: true,
459
460     getAutoCreate : function(){
461         var cfg = {
462             cls: 'btn-group',
463             html : null
464         };
465         
466         cfg.html = this.html || cfg.html;
467         
468         if (this.toolbar) {
469             cfg = {
470                 cls: 'btn-toolbar',
471                 html: null
472             };
473             
474             return cfg;
475         }
476         
477         if (['vertical','justified'].indexOf(this.align)!==-1) {
478             cfg.cls = 'btn-group-' + this.align;
479             
480             if (this.align == 'justified') {
481                 console.log(this.items);
482             }
483         }
484         
485         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
486             cfg.cls += ' btn-group-' + this.size;
487         }
488         
489         if (this.direction == 'up') {
490             cfg.cls += ' dropup' ;
491         }
492         
493         return cfg;
494     }
495    
496 });
497
498  /*
499  * - LGPL
500  *
501  * button
502  * 
503  */
504
505 /**
506  * @class Roo.bootstrap.Button
507  * @extends Roo.bootstrap.Component
508  * Bootstrap Button class
509  * @cfg {String} html The button content
510  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
511  * @cfg {String} size ( lg | sm | xs)
512  * @cfg {String} tag ( a | input | submit)
513  * @cfg {String} href empty or href
514  * @cfg {Boolean} disabled default false;
515  * @cfg {Boolean} isClose default false;
516  * @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)
517  * @cfg {String} badge text for badge
518  * @cfg {String} theme default 
519  * @cfg {Boolean} inverse 
520  * @cfg {Boolean} toggle 
521  * @cfg {String} ontext text for on toggle state
522  * @cfg {String} offtext text for off toggle state
523  * @cfg {Boolean} defaulton 
524  * @cfg {Boolean} preventDefault  default true
525  * @cfg {Boolean} removeClass remove the standard class..
526  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
527  * 
528  * @constructor
529  * Create a new button
530  * @param {Object} config The config object
531  */
532
533
534 Roo.bootstrap.Button = function(config){
535     Roo.bootstrap.Button.superclass.constructor.call(this, config);
536     this.weightClass = ["btn-default", 
537                        "btn-primary", 
538                        "btn-success", 
539                        "btn-info", 
540                        "btn-warning",
541                        "btn-danger",
542                        "btn-link"
543                       ],  
544     this.addEvents({
545         // raw events
546         /**
547          * @event click
548          * When a butotn is pressed
549          * @param {Roo.bootstrap.Button} this
550          * @param {Roo.EventObject} e
551          */
552         "click" : true,
553          /**
554          * @event toggle
555          * After the button has been toggles
556          * @param {Roo.EventObject} e
557          * @param {boolean} pressed (also available as button.pressed)
558          */
559         "toggle" : true
560     });
561 };
562
563 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
564     html: false,
565     active: false,
566     weight: '',
567     size: '',
568     tag: 'button',
569     href: '',
570     disabled: false,
571     isClose: false,
572     glyphicon: '',
573     badge: '',
574     theme: 'default',
575     inverse: false,
576     
577     toggle: false,
578     ontext: 'ON',
579     offtext: 'OFF',
580     defaulton: true,
581     preventDefault: true,
582     removeClass: false,
583     name: false,
584     target: false,
585     
586     
587     pressed : null,
588      
589     
590     getAutoCreate : function(){
591         
592         var cfg = {
593             tag : 'button',
594             cls : 'roo-button',
595             html: ''
596         };
597         
598         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
599             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
600             this.tag = 'button';
601         } else {
602             cfg.tag = this.tag;
603         }
604         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
605         
606         if (this.toggle == true) {
607             cfg={
608                 tag: 'div',
609                 cls: 'slider-frame roo-button',
610                 cn: [
611                     {
612                         tag: 'span',
613                         'data-on-text':'ON',
614                         'data-off-text':'OFF',
615                         cls: 'slider-button',
616                         html: this.offtext
617                     }
618                 ]
619             };
620             
621             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
622                 cfg.cls += ' '+this.weight;
623             }
624             
625             return cfg;
626         }
627         
628         if (this.isClose) {
629             cfg.cls += ' close';
630             
631             cfg["aria-hidden"] = true;
632             
633             cfg.html = "&times;";
634             
635             return cfg;
636         }
637         
638          
639         if (this.theme==='default') {
640             cfg.cls = 'btn roo-button';
641             
642             //if (this.parentType != 'Navbar') {
643             this.weight = this.weight.length ?  this.weight : 'default';
644             //}
645             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646                 
647                 cfg.cls += ' btn-' + this.weight;
648             }
649         } else if (this.theme==='glow') {
650             
651             cfg.tag = 'a';
652             cfg.cls = 'btn-glow roo-button';
653             
654             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
655                 
656                 cfg.cls += ' ' + this.weight;
657             }
658         }
659    
660         
661         if (this.inverse) {
662             this.cls += ' inverse';
663         }
664         
665         
666         if (this.active) {
667             cfg.cls += ' active';
668         }
669         
670         if (this.disabled) {
671             cfg.disabled = 'disabled';
672         }
673         
674         if (this.items) {
675             Roo.log('changing to ul' );
676             cfg.tag = 'ul';
677             this.glyphicon = 'caret';
678         }
679         
680         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
681          
682         //gsRoo.log(this.parentType);
683         if (this.parentType === 'Navbar' && !this.parent().bar) {
684             Roo.log('changing to li?');
685             
686             cfg.tag = 'li';
687             
688             cfg.cls = '';
689             cfg.cn =  [{
690                 tag : 'a',
691                 cls : 'roo-button',
692                 html : this.html,
693                 href : this.href || '#'
694             }];
695             if (this.menu) {
696                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
697                 cfg.cls += ' dropdown';
698             }   
699             
700             delete cfg.html;
701             
702         }
703         
704        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
705         
706         if (this.glyphicon) {
707             cfg.html = ' ' + cfg.html;
708             
709             cfg.cn = [
710                 {
711                     tag: 'span',
712                     cls: 'glyphicon glyphicon-' + this.glyphicon
713                 }
714             ];
715         }
716         
717         if (this.badge) {
718             cfg.html += ' ';
719             
720             cfg.tag = 'a';
721             
722 //            cfg.cls='btn roo-button';
723             
724             cfg.href=this.href;
725             
726             var value = cfg.html;
727             
728             if(this.glyphicon){
729                 value = {
730                             tag: 'span',
731                             cls: 'glyphicon glyphicon-' + this.glyphicon,
732                             html: this.html
733                         };
734                 
735             }
736             
737             cfg.cn = [
738                 value,
739                 {
740                     tag: 'span',
741                     cls: 'badge',
742                     html: this.badge
743                 }
744             ];
745             
746             cfg.html='';
747         }
748         
749         if (this.menu) {
750             cfg.cls += ' dropdown';
751             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
752         }
753         
754         if (cfg.tag !== 'a' && this.href !== '') {
755             throw "Tag must be a to set href.";
756         } else if (this.href.length > 0) {
757             cfg.href = this.href;
758         }
759         
760         if(this.removeClass){
761             cfg.cls = '';
762         }
763         
764         if(this.target){
765             cfg.target = this.target;
766         }
767         
768         return cfg;
769     },
770     initEvents: function() {
771        // Roo.log('init events?');
772 //        Roo.log(this.el.dom);
773         // add the menu...
774         
775         if (typeof (this.menu) != 'undefined') {
776             this.menu.parentType = this.xtype;
777             this.menu.triggerEl = this.el;
778             this.addxtype(Roo.apply({}, this.menu));
779         }
780
781
782        if (this.el.hasClass('roo-button')) {
783             this.el.on('click', this.onClick, this);
784        } else {
785             this.el.select('.roo-button').on('click', this.onClick, this);
786        }
787        
788        if(this.removeClass){
789            this.el.on('click', this.onClick, this);
790        }
791        
792        this.el.enableDisplayMode();
793         
794     },
795     onClick : function(e)
796     {
797         if (this.disabled) {
798             return;
799         }
800         
801         
802         Roo.log('button on click ');
803         if(this.preventDefault){
804             e.preventDefault();
805         }
806         if (this.pressed === true || this.pressed === false) {
807             this.pressed = !this.pressed;
808             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
809             this.fireEvent('toggle', this, e, this.pressed);
810         }
811         
812         
813         this.fireEvent('click', this, e);
814     },
815     
816     /**
817      * Enables this button
818      */
819     enable : function()
820     {
821         this.disabled = false;
822         this.el.removeClass('disabled');
823     },
824     
825     /**
826      * Disable this button
827      */
828     disable : function()
829     {
830         this.disabled = true;
831         this.el.addClass('disabled');
832     },
833      /**
834      * sets the active state on/off, 
835      * @param {Boolean} state (optional) Force a particular state
836      */
837     setActive : function(v) {
838         
839         this.el[v ? 'addClass' : 'removeClass']('active');
840     },
841      /**
842      * toggles the current active state 
843      */
844     toggleActive : function()
845     {
846        var active = this.el.hasClass('active');
847        this.setActive(!active);
848        
849         
850     },
851     setText : function(str)
852     {
853         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
854     },
855     getText : function()
856     {
857         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858     },
859     hide: function() {
860        
861      
862         this.el.hide();   
863     },
864     show: function() {
865        
866         this.el.show();   
867     },
868     setWeight : function(str)
869     {
870           this.el.removeClass(this.weightClass);
871         this.el.addClass('btn-' + str);        
872     }
873     
874     
875 });
876
877  /*
878  * - LGPL
879  *
880  * column
881  * 
882  */
883
884 /**
885  * @class Roo.bootstrap.Column
886  * @extends Roo.bootstrap.Component
887  * Bootstrap Column class
888  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
889  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
890  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
891  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
892  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
893  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
894  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
895  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
896  *
897  * 
898  * @cfg {Boolean} hidden (true|false) hide the element
899  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
900  * @cfg {String} fa (ban|check|...) font awesome icon
901  * @cfg {Number} fasize (1|2|....) font awsome size
902
903  * @cfg {String} icon (info-sign|check|...) glyphicon name
904
905  * @cfg {String} html content of column.
906  * 
907  * @constructor
908  * Create a new Column
909  * @param {Object} config The config object
910  */
911
912 Roo.bootstrap.Column = function(config){
913     Roo.bootstrap.Column.superclass.constructor.call(this, config);
914 };
915
916 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
917     
918     xs: false,
919     sm: false,
920     md: false,
921     lg: false,
922     xsoff: false,
923     smoff: false,
924     mdoff: false,
925     lgoff: false,
926     html: '',
927     offset: 0,
928     alert: false,
929     fa: false,
930     icon : false,
931     hidden : false,
932     fasize : 1,
933     
934     getAutoCreate : function(){
935         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
936         
937         cfg = {
938             tag: 'div',
939             cls: 'column'
940         };
941         
942         var settings=this;
943         ['xs','sm','md','lg'].map(function(size){
944             //Roo.log( size + ':' + settings[size]);
945             
946             if (settings[size+'off'] !== false) {
947                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
948             }
949             
950             if (settings[size] === false) {
951                 return;
952             }
953             
954             if (!settings[size]) { // 0 = hidden
955                 cfg.cls += ' hidden-' + size;
956                 return;
957             }
958             cfg.cls += ' col-' + size + '-' + settings[size];
959             
960         });
961         
962         if (this.hidden) {
963             cfg.cls += ' hidden';
964         }
965         
966         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
967             cfg.cls +=' alert alert-' + this.alert;
968         }
969         
970         
971         if (this.html.length) {
972             cfg.html = this.html;
973         }
974         if (this.fa) {
975             var fasize = '';
976             if (this.fasize > 1) {
977                 fasize = ' fa-' + this.fasize + 'x';
978             }
979             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
980             
981             
982         }
983         if (this.icon) {
984             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
985         }
986         
987         return cfg;
988     }
989    
990 });
991
992  
993
994  /*
995  * - LGPL
996  *
997  * page container.
998  * 
999  */
1000
1001
1002 /**
1003  * @class Roo.bootstrap.Container
1004  * @extends Roo.bootstrap.Component
1005  * Bootstrap Container class
1006  * @cfg {Boolean} jumbotron is it a jumbotron element
1007  * @cfg {String} html content of element
1008  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1009  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
1010  * @cfg {String} header content of header (for panel)
1011  * @cfg {String} footer content of footer (for panel)
1012  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1013  * @cfg {String} tag (header|aside|section) type of HTML tag.
1014  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1015  * @cfg {String} fa font awesome icon
1016  * @cfg {String} icon (info-sign|check|...) glyphicon name
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {Boolean} expandable (true|false) default false
1019  * @cfg {Boolean} expanded (true|false) default true
1020  * @cfg {String} rheader contet on the right of header
1021  * @cfg {Boolean} clickable (true|false) default false
1022
1023  *     
1024  * @constructor
1025  * Create a new Container
1026  * @param {Object} config The config object
1027  */
1028
1029 Roo.bootstrap.Container = function(config){
1030     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1031     
1032     this.addEvents({
1033         // raw events
1034          /**
1035          * @event expand
1036          * After the panel has been expand
1037          * 
1038          * @param {Roo.bootstrap.Container} this
1039          */
1040         "expand" : true,
1041         /**
1042          * @event collapse
1043          * After the panel has been collapsed
1044          * 
1045          * @param {Roo.bootstrap.Container} this
1046          */
1047         "collapse" : true,
1048         /**
1049          * @event click
1050          * When a element is chick
1051          * @param {Roo.bootstrap.Container} this
1052          * @param {Roo.EventObject} e
1053          */
1054         "click" : true
1055     });
1056 };
1057
1058 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1059     
1060     jumbotron : false,
1061     well: '',
1062     panel : '',
1063     header: '',
1064     footer : '',
1065     sticky: '',
1066     tag : false,
1067     alert : false,
1068     fa: false,
1069     icon : false,
1070     expandable : false,
1071     rheader : '',
1072     expanded : true,
1073     clickable: false,
1074   
1075      
1076     getChildContainer : function() {
1077         
1078         if(!this.el){
1079             return false;
1080         }
1081         
1082         if (this.panel.length) {
1083             return this.el.select('.panel-body',true).first();
1084         }
1085         
1086         return this.el;
1087     },
1088     
1089     
1090     getAutoCreate : function(){
1091         
1092         var cfg = {
1093             tag : this.tag || 'div',
1094             html : '',
1095             cls : ''
1096         };
1097         if (this.jumbotron) {
1098             cfg.cls = 'jumbotron';
1099         }
1100         
1101         
1102         
1103         // - this is applied by the parent..
1104         //if (this.cls) {
1105         //    cfg.cls = this.cls + '';
1106         //}
1107         
1108         if (this.sticky.length) {
1109             
1110             var bd = Roo.get(document.body);
1111             if (!bd.hasClass('bootstrap-sticky')) {
1112                 bd.addClass('bootstrap-sticky');
1113                 Roo.select('html',true).setStyle('height', '100%');
1114             }
1115              
1116             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1117         }
1118         
1119         
1120         if (this.well.length) {
1121             switch (this.well) {
1122                 case 'lg':
1123                 case 'sm':
1124                     cfg.cls +=' well well-' +this.well;
1125                     break;
1126                 default:
1127                     cfg.cls +=' well';
1128                     break;
1129             }
1130         }
1131         
1132         if (this.hidden) {
1133             cfg.cls += ' hidden';
1134         }
1135         
1136         
1137         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1138             cfg.cls +=' alert alert-' + this.alert;
1139         }
1140         
1141         var body = cfg;
1142         
1143         if (this.panel.length) {
1144             cfg.cls += ' panel panel-' + this.panel;
1145             cfg.cn = [];
1146             if (this.header.length) {
1147                 
1148                 var h = [];
1149                 
1150                 if(this.expandable){
1151                     
1152                     cfg.cls = cfg.cls + ' expandable';
1153                     
1154                     h.push({
1155                         tag: 'i',
1156                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1157                     });
1158                     
1159                 }
1160                 
1161                 h.push(
1162                     {
1163                         tag: 'span',
1164                         cls : 'panel-title',
1165                         html : (this.expandable ? '&nbsp;' : '') + this.header
1166                     },
1167                     {
1168                         tag: 'span',
1169                         cls: 'panel-header-right',
1170                         html: this.rheader
1171                     }
1172                 );
1173                 
1174                 cfg.cn.push({
1175                     cls : 'panel-heading',
1176                     style : this.expandable ? 'cursor: pointer' : '',
1177                     cn : h
1178                 });
1179                 
1180             }
1181             
1182             body = false;
1183             cfg.cn.push({
1184                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1185                 html : this.html
1186             });
1187             
1188             
1189             if (this.footer.length) {
1190                 cfg.cn.push({
1191                     cls : 'panel-footer',
1192                     html : this.footer
1193                     
1194                 });
1195             }
1196             
1197         }
1198         
1199         if (body) {
1200             body.html = this.html || cfg.html;
1201             // prefix with the icons..
1202             if (this.fa) {
1203                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1204             }
1205             if (this.icon) {
1206                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1207             }
1208             
1209             
1210         }
1211         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1212             cfg.cls =  'container';
1213         }
1214         
1215         return cfg;
1216     },
1217     
1218     initEvents: function() 
1219     {
1220         if(this.expandable){
1221             var headerEl = this.headerEl();
1222         
1223             if(headerEl){
1224                 headerEl.on('click', this.onToggleClick, this);
1225             }
1226         }
1227         
1228         if(this.clickable){
1229             this.el.on('click', this.onClick, this);
1230         }
1231         
1232     },
1233     
1234     onToggleClick : function()
1235     {
1236         var headerEl = this.headerEl();
1237         
1238         if(!headerEl){
1239             return;
1240         }
1241         
1242         if(this.expanded){
1243             this.collapse();
1244             return;
1245         }
1246         
1247         this.expand();
1248     },
1249     
1250     expand : function()
1251     {
1252         if(this.fireEvent('expand', this)) {
1253             
1254             this.expanded = true;
1255             
1256             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1257             
1258             this.el.select('.panel-body',true).first().removeClass('hide');
1259             
1260             var toggleEl = this.toggleEl();
1261
1262             if(!toggleEl){
1263                 return;
1264             }
1265
1266             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1267         }
1268         
1269     },
1270     
1271     collapse : function()
1272     {
1273         if(this.fireEvent('collapse', this)) {
1274             
1275             this.expanded = false;
1276             
1277             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1278             this.el.select('.panel-body',true).first().addClass('hide');
1279         
1280             var toggleEl = this.toggleEl();
1281
1282             if(!toggleEl){
1283                 return;
1284             }
1285
1286             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1287         }
1288     },
1289     
1290     toggleEl : function()
1291     {
1292         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1293             return;
1294         }
1295         
1296         return this.el.select('.panel-heading .fa',true).first();
1297     },
1298     
1299     headerEl : function()
1300     {
1301         if(!this.el || !this.panel.length || !this.header.length){
1302             return;
1303         }
1304         
1305         return this.el.select('.panel-heading',true).first()
1306     },
1307     
1308     bodyEl : function()
1309     {
1310         if(!this.el || !this.panel.length){
1311             return;
1312         }
1313         
1314         return this.el.select('.panel-body',true).first()
1315     },
1316     
1317     titleEl : function()
1318     {
1319         if(!this.el || !this.panel.length || !this.header.length){
1320             return;
1321         }
1322         
1323         return this.el.select('.panel-title',true).first();
1324     },
1325     
1326     setTitle : function(v)
1327     {
1328         var titleEl = this.titleEl();
1329         
1330         if(!titleEl){
1331             return;
1332         }
1333         
1334         titleEl.dom.innerHTML = v;
1335     },
1336     
1337     getTitle : function()
1338     {
1339         
1340         var titleEl = this.titleEl();
1341         
1342         if(!titleEl){
1343             return '';
1344         }
1345         
1346         return titleEl.dom.innerHTML;
1347     },
1348     
1349     setRightTitle : function(v)
1350     {
1351         var t = this.el.select('.panel-header-right',true).first();
1352         
1353         if(!t){
1354             return;
1355         }
1356         
1357         t.dom.innerHTML = v;
1358     },
1359     
1360     onClick : function(e)
1361     {
1362         e.preventDefault();
1363         
1364         this.fireEvent('click', this, e);
1365     }
1366    
1367 });
1368
1369  /*
1370  * - LGPL
1371  *
1372  * image
1373  * 
1374  */
1375
1376
1377 /**
1378  * @class Roo.bootstrap.Img
1379  * @extends Roo.bootstrap.Component
1380  * Bootstrap Img class
1381  * @cfg {Boolean} imgResponsive false | true
1382  * @cfg {String} border rounded | circle | thumbnail
1383  * @cfg {String} src image source
1384  * @cfg {String} alt image alternative text
1385  * @cfg {String} href a tag href
1386  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1387  * @cfg {String} xsUrl xs image source
1388  * @cfg {String} smUrl sm image source
1389  * @cfg {String} mdUrl md image source
1390  * @cfg {String} lgUrl lg image source
1391  * 
1392  * @constructor
1393  * Create a new Input
1394  * @param {Object} config The config object
1395  */
1396
1397 Roo.bootstrap.Img = function(config){
1398     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1399     
1400     this.addEvents({
1401         // img events
1402         /**
1403          * @event click
1404          * The img click event for the img.
1405          * @param {Roo.EventObject} e
1406          */
1407         "click" : true
1408     });
1409 };
1410
1411 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1412     
1413     imgResponsive: true,
1414     border: '',
1415     src: 'about:blank',
1416     href: false,
1417     target: false,
1418     xsUrl: '',
1419     smUrl: '',
1420     mdUrl: '',
1421     lgUrl: '',
1422
1423     getAutoCreate : function()
1424     {   
1425         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1426             return this.createSingleImg();
1427         }
1428         
1429         var cfg = {
1430             tag: 'div',
1431             cls: 'roo-image-responsive-group',
1432             cn: []
1433         };
1434         var _this = this;
1435         
1436         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1437             
1438             if(!_this[size + 'Url']){
1439                 return;
1440             }
1441             
1442             var img = {
1443                 tag: 'img',
1444                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1445                 html: _this.html || cfg.html,
1446                 src: _this[size + 'Url']
1447             };
1448             
1449             img.cls += ' roo-image-responsive-' + size;
1450             
1451             var s = ['xs', 'sm', 'md', 'lg'];
1452             
1453             s.splice(s.indexOf(size), 1);
1454             
1455             Roo.each(s, function(ss){
1456                 img.cls += ' hidden-' + ss;
1457             });
1458             
1459             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1460                 cfg.cls += ' img-' + _this.border;
1461             }
1462             
1463             if(_this.alt){
1464                 cfg.alt = _this.alt;
1465             }
1466             
1467             if(_this.href){
1468                 var a = {
1469                     tag: 'a',
1470                     href: _this.href,
1471                     cn: [
1472                         img
1473                     ]
1474                 };
1475
1476                 if(this.target){
1477                     a.target = _this.target;
1478                 }
1479             }
1480             
1481             cfg.cn.push((_this.href) ? a : img);
1482             
1483         });
1484         
1485         return cfg;
1486     },
1487     
1488     createSingleImg : function()
1489     {
1490         var cfg = {
1491             tag: 'img',
1492             cls: (this.imgResponsive) ? 'img-responsive' : '',
1493             html : null,
1494             src : 'about:blank'  // just incase src get's set to undefined?!?
1495         };
1496         
1497         cfg.html = this.html || cfg.html;
1498         
1499         cfg.src = this.src || cfg.src;
1500         
1501         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1502             cfg.cls += ' img-' + this.border;
1503         }
1504         
1505         if(this.alt){
1506             cfg.alt = this.alt;
1507         }
1508         
1509         if(this.href){
1510             var a = {
1511                 tag: 'a',
1512                 href: this.href,
1513                 cn: [
1514                     cfg
1515                 ]
1516             };
1517             
1518             if(this.target){
1519                 a.target = this.target;
1520             }
1521             
1522         }
1523         
1524         return (this.href) ? a : cfg;
1525     },
1526     
1527     initEvents: function() 
1528     {
1529         if(!this.href){
1530             this.el.on('click', this.onClick, this);
1531         }
1532         
1533     },
1534     
1535     onClick : function(e)
1536     {
1537         Roo.log('img onclick');
1538         this.fireEvent('click', this, e);
1539     },
1540     /**
1541      * Sets the url of the image - used to update it
1542      * @param {String} url the url of the image
1543      */
1544     
1545     setSrc : function(url)
1546     {
1547         this.src =  url;
1548         
1549         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1550             this.el.dom.src =  url;
1551             return;
1552         }
1553         
1554         this.el.select('img', true).first().dom.src =  url;
1555     }
1556     
1557     
1558    
1559 });
1560
1561  /*
1562  * - LGPL
1563  *
1564  * image
1565  * 
1566  */
1567
1568
1569 /**
1570  * @class Roo.bootstrap.Link
1571  * @extends Roo.bootstrap.Component
1572  * Bootstrap Link Class
1573  * @cfg {String} alt image alternative text
1574  * @cfg {String} href a tag href
1575  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1576  * @cfg {String} html the content of the link.
1577  * @cfg {String} anchor name for the anchor link
1578  * @cfg {String} fa - favicon
1579
1580  * @cfg {Boolean} preventDefault (true | false) default false
1581
1582  * 
1583  * @constructor
1584  * Create a new Input
1585  * @param {Object} config The config object
1586  */
1587
1588 Roo.bootstrap.Link = function(config){
1589     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1590     
1591     this.addEvents({
1592         // img events
1593         /**
1594          * @event click
1595          * The img click event for the img.
1596          * @param {Roo.EventObject} e
1597          */
1598         "click" : true
1599     });
1600 };
1601
1602 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1603     
1604     href: false,
1605     target: false,
1606     preventDefault: false,
1607     anchor : false,
1608     alt : false,
1609     fa: false,
1610
1611
1612     getAutoCreate : function()
1613     {
1614         var html = this.html || '';
1615         
1616         if (this.fa !== false) {
1617             html = '<i class="fa fa-' + this.fa + '"></i>';
1618         }
1619         var cfg = {
1620             tag: 'a'
1621         };
1622         // anchor's do not require html/href...
1623         if (this.anchor === false) {
1624             cfg.html = html;
1625             cfg.href = this.href || '#';
1626         } else {
1627             cfg.name = this.anchor;
1628             if (this.html !== false || this.fa !== false) {
1629                 cfg.html = html;
1630             }
1631             if (this.href !== false) {
1632                 cfg.href = this.href;
1633             }
1634         }
1635         
1636         if(this.alt !== false){
1637             cfg.alt = this.alt;
1638         }
1639         
1640         
1641         if(this.target !== false) {
1642             cfg.target = this.target;
1643         }
1644         
1645         return cfg;
1646     },
1647     
1648     initEvents: function() {
1649         
1650         if(!this.href || this.preventDefault){
1651             this.el.on('click', this.onClick, this);
1652         }
1653     },
1654     
1655     onClick : function(e)
1656     {
1657         if(this.preventDefault){
1658             e.preventDefault();
1659         }
1660         //Roo.log('img onclick');
1661         this.fireEvent('click', this, e);
1662     }
1663    
1664 });
1665
1666  /*
1667  * - LGPL
1668  *
1669  * header
1670  * 
1671  */
1672
1673 /**
1674  * @class Roo.bootstrap.Header
1675  * @extends Roo.bootstrap.Component
1676  * Bootstrap Header class
1677  * @cfg {String} html content of header
1678  * @cfg {Number} level (1|2|3|4|5|6) default 1
1679  * 
1680  * @constructor
1681  * Create a new Header
1682  * @param {Object} config The config object
1683  */
1684
1685
1686 Roo.bootstrap.Header  = function(config){
1687     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1688 };
1689
1690 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1691     
1692     //href : false,
1693     html : false,
1694     level : 1,
1695     
1696     
1697     
1698     getAutoCreate : function(){
1699         
1700         
1701         
1702         var cfg = {
1703             tag: 'h' + (1 *this.level),
1704             html: this.html || ''
1705         } ;
1706         
1707         return cfg;
1708     }
1709    
1710 });
1711
1712  
1713
1714  /*
1715  * Based on:
1716  * Ext JS Library 1.1.1
1717  * Copyright(c) 2006-2007, Ext JS, LLC.
1718  *
1719  * Originally Released Under LGPL - original licence link has changed is not relivant.
1720  *
1721  * Fork - LGPL
1722  * <script type="text/javascript">
1723  */
1724  
1725 /**
1726  * @class Roo.bootstrap.MenuMgr
1727  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1728  * @singleton
1729  */
1730 Roo.bootstrap.MenuMgr = function(){
1731    var menus, active, groups = {}, attached = false, lastShow = new Date();
1732
1733    // private - called when first menu is created
1734    function init(){
1735        menus = {};
1736        active = new Roo.util.MixedCollection();
1737        Roo.get(document).addKeyListener(27, function(){
1738            if(active.length > 0){
1739                hideAll();
1740            }
1741        });
1742    }
1743
1744    // private
1745    function hideAll(){
1746        if(active && active.length > 0){
1747            var c = active.clone();
1748            c.each(function(m){
1749                m.hide();
1750            });
1751        }
1752    }
1753
1754    // private
1755    function onHide(m){
1756        active.remove(m);
1757        if(active.length < 1){
1758            Roo.get(document).un("mouseup", onMouseDown);
1759             
1760            attached = false;
1761        }
1762    }
1763
1764    // private
1765    function onShow(m){
1766        var last = active.last();
1767        lastShow = new Date();
1768        active.add(m);
1769        if(!attached){
1770           Roo.get(document).on("mouseup", onMouseDown);
1771            
1772            attached = true;
1773        }
1774        if(m.parentMenu){
1775           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1776           m.parentMenu.activeChild = m;
1777        }else if(last && last.isVisible()){
1778           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1779        }
1780    }
1781
1782    // private
1783    function onBeforeHide(m){
1784        if(m.activeChild){
1785            m.activeChild.hide();
1786        }
1787        if(m.autoHideTimer){
1788            clearTimeout(m.autoHideTimer);
1789            delete m.autoHideTimer;
1790        }
1791    }
1792
1793    // private
1794    function onBeforeShow(m){
1795        var pm = m.parentMenu;
1796        if(!pm && !m.allowOtherMenus){
1797            hideAll();
1798        }else if(pm && pm.activeChild && active != m){
1799            pm.activeChild.hide();
1800        }
1801    }
1802
1803    // private this should really trigger on mouseup..
1804    function onMouseDown(e){
1805         Roo.log("on Mouse Up");
1806         
1807         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1808             Roo.log("MenuManager hideAll");
1809             hideAll();
1810             e.stopEvent();
1811         }
1812         
1813         
1814    }
1815
1816    // private
1817    function onBeforeCheck(mi, state){
1818        if(state){
1819            var g = groups[mi.group];
1820            for(var i = 0, l = g.length; i < l; i++){
1821                if(g[i] != mi){
1822                    g[i].setChecked(false);
1823                }
1824            }
1825        }
1826    }
1827
1828    return {
1829
1830        /**
1831         * Hides all menus that are currently visible
1832         */
1833        hideAll : function(){
1834             hideAll();  
1835        },
1836
1837        // private
1838        register : function(menu){
1839            if(!menus){
1840                init();
1841            }
1842            menus[menu.id] = menu;
1843            menu.on("beforehide", onBeforeHide);
1844            menu.on("hide", onHide);
1845            menu.on("beforeshow", onBeforeShow);
1846            menu.on("show", onShow);
1847            var g = menu.group;
1848            if(g && menu.events["checkchange"]){
1849                if(!groups[g]){
1850                    groups[g] = [];
1851                }
1852                groups[g].push(menu);
1853                menu.on("checkchange", onCheck);
1854            }
1855        },
1856
1857         /**
1858          * Returns a {@link Roo.menu.Menu} object
1859          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1860          * be used to generate and return a new Menu instance.
1861          */
1862        get : function(menu){
1863            if(typeof menu == "string"){ // menu id
1864                return menus[menu];
1865            }else if(menu.events){  // menu instance
1866                return menu;
1867            }
1868            /*else if(typeof menu.length == 'number'){ // array of menu items?
1869                return new Roo.bootstrap.Menu({items:menu});
1870            }else{ // otherwise, must be a config
1871                return new Roo.bootstrap.Menu(menu);
1872            }
1873            */
1874            return false;
1875        },
1876
1877        // private
1878        unregister : function(menu){
1879            delete menus[menu.id];
1880            menu.un("beforehide", onBeforeHide);
1881            menu.un("hide", onHide);
1882            menu.un("beforeshow", onBeforeShow);
1883            menu.un("show", onShow);
1884            var g = menu.group;
1885            if(g && menu.events["checkchange"]){
1886                groups[g].remove(menu);
1887                menu.un("checkchange", onCheck);
1888            }
1889        },
1890
1891        // private
1892        registerCheckable : function(menuItem){
1893            var g = menuItem.group;
1894            if(g){
1895                if(!groups[g]){
1896                    groups[g] = [];
1897                }
1898                groups[g].push(menuItem);
1899                menuItem.on("beforecheckchange", onBeforeCheck);
1900            }
1901        },
1902
1903        // private
1904        unregisterCheckable : function(menuItem){
1905            var g = menuItem.group;
1906            if(g){
1907                groups[g].remove(menuItem);
1908                menuItem.un("beforecheckchange", onBeforeCheck);
1909            }
1910        }
1911    };
1912 }();/*
1913  * - LGPL
1914  *
1915  * menu
1916  * 
1917  */
1918
1919 /**
1920  * @class Roo.bootstrap.Menu
1921  * @extends Roo.bootstrap.Component
1922  * Bootstrap Menu class - container for MenuItems
1923  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1924  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1925  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1926  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1927  * 
1928  * @constructor
1929  * Create a new Menu
1930  * @param {Object} config The config object
1931  */
1932
1933
1934 Roo.bootstrap.Menu = function(config){
1935     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1936     if (this.registerMenu && this.type != 'treeview')  {
1937         Roo.bootstrap.MenuMgr.register(this);
1938     }
1939     this.addEvents({
1940         /**
1941          * @event beforeshow
1942          * Fires before this menu is displayed
1943          * @param {Roo.menu.Menu} this
1944          */
1945         beforeshow : true,
1946         /**
1947          * @event beforehide
1948          * Fires before this menu is hidden
1949          * @param {Roo.menu.Menu} this
1950          */
1951         beforehide : true,
1952         /**
1953          * @event show
1954          * Fires after this menu is displayed
1955          * @param {Roo.menu.Menu} this
1956          */
1957         show : true,
1958         /**
1959          * @event hide
1960          * Fires after this menu is hidden
1961          * @param {Roo.menu.Menu} this
1962          */
1963         hide : true,
1964         /**
1965          * @event click
1966          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1967          * @param {Roo.menu.Menu} this
1968          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1969          * @param {Roo.EventObject} e
1970          */
1971         click : true,
1972         /**
1973          * @event mouseover
1974          * Fires when the mouse is hovering over this menu
1975          * @param {Roo.menu.Menu} this
1976          * @param {Roo.EventObject} e
1977          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1978          */
1979         mouseover : true,
1980         /**
1981          * @event mouseout
1982          * Fires when the mouse exits this menu
1983          * @param {Roo.menu.Menu} this
1984          * @param {Roo.EventObject} e
1985          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1986          */
1987         mouseout : true,
1988         /**
1989          * @event itemclick
1990          * Fires when a menu item contained in this menu is clicked
1991          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1992          * @param {Roo.EventObject} e
1993          */
1994         itemclick: true
1995     });
1996     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1997 };
1998
1999 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2000     
2001    /// html : false,
2002     //align : '',
2003     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2004     type: false,
2005     /**
2006      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2007      */
2008     registerMenu : true,
2009     
2010     menuItems :false, // stores the menu items..
2011     
2012     hidden:true,
2013         
2014     parentMenu : false,
2015     
2016     stopEvent : true,
2017     
2018     isLink : false,
2019     
2020     getChildContainer : function() {
2021         return this.el;  
2022     },
2023     
2024     getAutoCreate : function(){
2025          
2026         //if (['right'].indexOf(this.align)!==-1) {
2027         //    cfg.cn[1].cls += ' pull-right'
2028         //}
2029         
2030         
2031         var cfg = {
2032             tag : 'ul',
2033             cls : 'dropdown-menu' ,
2034             style : 'z-index:1000'
2035             
2036         };
2037         
2038         if (this.type === 'submenu') {
2039             cfg.cls = 'submenu active';
2040         }
2041         if (this.type === 'treeview') {
2042             cfg.cls = 'treeview-menu';
2043         }
2044         
2045         return cfg;
2046     },
2047     initEvents : function() {
2048         
2049        // Roo.log("ADD event");
2050        // Roo.log(this.triggerEl.dom);
2051         
2052         this.triggerEl.on('click', this.onTriggerClick, this);
2053         
2054         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2055         
2056         this.triggerEl.addClass('dropdown-toggle');
2057         
2058         if (Roo.isTouch) {
2059             this.el.on('touchstart'  , this.onTouch, this);
2060         }
2061         this.el.on('click' , this.onClick, this);
2062
2063         this.el.on("mouseover", this.onMouseOver, this);
2064         this.el.on("mouseout", this.onMouseOut, this);
2065         
2066     },
2067     
2068     findTargetItem : function(e)
2069     {
2070         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2071         if(!t){
2072             return false;
2073         }
2074         //Roo.log(t);         Roo.log(t.id);
2075         if(t && t.id){
2076             //Roo.log(this.menuitems);
2077             return this.menuitems.get(t.id);
2078             
2079             //return this.items.get(t.menuItemId);
2080         }
2081         
2082         return false;
2083     },
2084     
2085     onTouch : function(e) 
2086     {
2087         Roo.log("menu.onTouch");
2088         //e.stopEvent(); this make the user popdown broken
2089         this.onClick(e);
2090     },
2091     
2092     onClick : function(e)
2093     {
2094         Roo.log("menu.onClick");
2095         
2096         var t = this.findTargetItem(e);
2097         if(!t || t.isContainer){
2098             return;
2099         }
2100         Roo.log(e);
2101         /*
2102         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2103             if(t == this.activeItem && t.shouldDeactivate(e)){
2104                 this.activeItem.deactivate();
2105                 delete this.activeItem;
2106                 return;
2107             }
2108             if(t.canActivate){
2109                 this.setActiveItem(t, true);
2110             }
2111             return;
2112             
2113             
2114         }
2115         */
2116        
2117         Roo.log('pass click event');
2118         
2119         t.onClick(e);
2120         
2121         this.fireEvent("click", this, t, e);
2122         
2123         var _this = this;
2124         
2125         if(!t.href.length || t.href == '#'){
2126             (function() { _this.hide(); }).defer(100);
2127         }
2128         
2129     },
2130     
2131     onMouseOver : function(e){
2132         var t  = this.findTargetItem(e);
2133         //Roo.log(t);
2134         //if(t){
2135         //    if(t.canActivate && !t.disabled){
2136         //        this.setActiveItem(t, true);
2137         //    }
2138         //}
2139         
2140         this.fireEvent("mouseover", this, e, t);
2141     },
2142     isVisible : function(){
2143         return !this.hidden;
2144     },
2145      onMouseOut : function(e){
2146         var t  = this.findTargetItem(e);
2147         
2148         //if(t ){
2149         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2150         //        this.activeItem.deactivate();
2151         //        delete this.activeItem;
2152         //    }
2153         //}
2154         this.fireEvent("mouseout", this, e, t);
2155     },
2156     
2157     
2158     /**
2159      * Displays this menu relative to another element
2160      * @param {String/HTMLElement/Roo.Element} element The element to align to
2161      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2162      * the element (defaults to this.defaultAlign)
2163      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2164      */
2165     show : function(el, pos, parentMenu){
2166         this.parentMenu = parentMenu;
2167         if(!this.el){
2168             this.render();
2169         }
2170         this.fireEvent("beforeshow", this);
2171         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2172     },
2173      /**
2174      * Displays this menu at a specific xy position
2175      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2176      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2177      */
2178     showAt : function(xy, parentMenu, /* private: */_e){
2179         this.parentMenu = parentMenu;
2180         if(!this.el){
2181             this.render();
2182         }
2183         if(_e !== false){
2184             this.fireEvent("beforeshow", this);
2185             //xy = this.el.adjustForConstraints(xy);
2186         }
2187         
2188         //this.el.show();
2189         this.hideMenuItems();
2190         this.hidden = false;
2191         this.triggerEl.addClass('open');
2192         
2193         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2194             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2195         }
2196         
2197         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2198             this.el.setXY(xy);
2199         }
2200         
2201         this.focus();
2202         this.fireEvent("show", this);
2203     },
2204     
2205     focus : function(){
2206         return;
2207         if(!this.hidden){
2208             this.doFocus.defer(50, this);
2209         }
2210     },
2211
2212     doFocus : function(){
2213         if(!this.hidden){
2214             this.focusEl.focus();
2215         }
2216     },
2217
2218     /**
2219      * Hides this menu and optionally all parent menus
2220      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2221      */
2222     hide : function(deep)
2223     {
2224         
2225         this.hideMenuItems();
2226         if(this.el && this.isVisible()){
2227             this.fireEvent("beforehide", this);
2228             if(this.activeItem){
2229                 this.activeItem.deactivate();
2230                 this.activeItem = null;
2231             }
2232             this.triggerEl.removeClass('open');;
2233             this.hidden = true;
2234             this.fireEvent("hide", this);
2235         }
2236         if(deep === true && this.parentMenu){
2237             this.parentMenu.hide(true);
2238         }
2239     },
2240     
2241     onTriggerClick : function(e)
2242     {
2243         Roo.log('trigger click');
2244         
2245         var target = e.getTarget();
2246         
2247         Roo.log(target.nodeName.toLowerCase());
2248         
2249         if(target.nodeName.toLowerCase() === 'i'){
2250             e.preventDefault();
2251         }
2252         
2253     },
2254     
2255     onTriggerPress  : function(e)
2256     {
2257         Roo.log('trigger press');
2258         //Roo.log(e.getTarget());
2259        // Roo.log(this.triggerEl.dom);
2260        
2261         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2262         var pel = Roo.get(e.getTarget());
2263         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2264             Roo.log('is treeview or dropdown?');
2265             return;
2266         }
2267         
2268         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2269             return;
2270         }
2271         
2272         if (this.isVisible()) {
2273             Roo.log('hide');
2274             this.hide();
2275         } else {
2276             Roo.log('show');
2277             this.show(this.triggerEl, false, false);
2278         }
2279         
2280         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2281             e.stopEvent();
2282         }
2283         
2284     },
2285        
2286     
2287     hideMenuItems : function()
2288     {
2289         Roo.log("hide Menu Items");
2290         if (!this.el) { 
2291             return;
2292         }
2293         //$(backdrop).remove()
2294         this.el.select('.open',true).each(function(aa) {
2295             
2296             aa.removeClass('open');
2297           //var parent = getParent($(this))
2298           //var relatedTarget = { relatedTarget: this }
2299           
2300            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2301           //if (e.isDefaultPrevented()) return
2302            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2303         });
2304     },
2305     addxtypeChild : function (tree, cntr) {
2306         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2307           
2308         this.menuitems.add(comp);
2309         return comp;
2310
2311     },
2312     getEl : function()
2313     {
2314         Roo.log(this.el);
2315         return this.el;
2316     }
2317 });
2318
2319  
2320  /*
2321  * - LGPL
2322  *
2323  * menu item
2324  * 
2325  */
2326
2327
2328 /**
2329  * @class Roo.bootstrap.MenuItem
2330  * @extends Roo.bootstrap.Component
2331  * Bootstrap MenuItem class
2332  * @cfg {String} html the menu label
2333  * @cfg {String} href the link
2334  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2335  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2336  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2337  * @cfg {String} fa favicon to show on left of menu item.
2338  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2339  * 
2340  * 
2341  * @constructor
2342  * Create a new MenuItem
2343  * @param {Object} config The config object
2344  */
2345
2346
2347 Roo.bootstrap.MenuItem = function(config){
2348     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2349     this.addEvents({
2350         // raw events
2351         /**
2352          * @event click
2353          * The raw click event for the entire grid.
2354          * @param {Roo.bootstrap.MenuItem} this
2355          * @param {Roo.EventObject} e
2356          */
2357         "click" : true
2358     });
2359 };
2360
2361 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2362     
2363     href : false,
2364     html : false,
2365     preventDefault: false,
2366     isContainer : false,
2367     active : false,
2368     fa: false,
2369     
2370     getAutoCreate : function(){
2371         
2372         if(this.isContainer){
2373             return {
2374                 tag: 'li',
2375                 cls: 'dropdown-menu-item'
2376             };
2377         }
2378         var ctag = {
2379             tag: 'span',
2380             html: 'Link'
2381         };
2382         
2383         var anc = {
2384             tag : 'a',
2385             href : '#',
2386             cn : [  ]
2387         };
2388         
2389         if (this.fa !== false) {
2390             anc.cn.push({
2391                 tag : 'i',
2392                 cls : 'fa fa-' + this.fa
2393             });
2394         }
2395         
2396         anc.cn.push(ctag);
2397         
2398         
2399         var cfg= {
2400             tag: 'li',
2401             cls: 'dropdown-menu-item',
2402             cn: [ anc ]
2403         };
2404         if (this.parent().type == 'treeview') {
2405             cfg.cls = 'treeview-menu';
2406         }
2407         if (this.active) {
2408             cfg.cls += ' active';
2409         }
2410         
2411         
2412         
2413         anc.href = this.href || cfg.cn[0].href ;
2414         ctag.html = this.html || cfg.cn[0].html ;
2415         return cfg;
2416     },
2417     
2418     initEvents: function()
2419     {
2420         if (this.parent().type == 'treeview') {
2421             this.el.select('a').on('click', this.onClick, this);
2422         }
2423         
2424         if (this.menu) {
2425             this.menu.parentType = this.xtype;
2426             this.menu.triggerEl = this.el;
2427             this.menu = this.addxtype(Roo.apply({}, this.menu));
2428         }
2429         
2430     },
2431     onClick : function(e)
2432     {
2433         Roo.log('item on click ');
2434         
2435         if(this.preventDefault){
2436             e.preventDefault();
2437         }
2438         //this.parent().hideMenuItems();
2439         
2440         this.fireEvent('click', this, e);
2441     },
2442     getEl : function()
2443     {
2444         return this.el;
2445     } 
2446 });
2447
2448  
2449
2450  /*
2451  * - LGPL
2452  *
2453  * menu separator
2454  * 
2455  */
2456
2457
2458 /**
2459  * @class Roo.bootstrap.MenuSeparator
2460  * @extends Roo.bootstrap.Component
2461  * Bootstrap MenuSeparator class
2462  * 
2463  * @constructor
2464  * Create a new MenuItem
2465  * @param {Object} config The config object
2466  */
2467
2468
2469 Roo.bootstrap.MenuSeparator = function(config){
2470     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2471 };
2472
2473 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2474     
2475     getAutoCreate : function(){
2476         var cfg = {
2477             cls: 'divider',
2478             tag : 'li'
2479         };
2480         
2481         return cfg;
2482     }
2483    
2484 });
2485
2486  
2487
2488  
2489 /*
2490 * Licence: LGPL
2491 */
2492
2493 /**
2494  * @class Roo.bootstrap.Modal
2495  * @extends Roo.bootstrap.Component
2496  * Bootstrap Modal class
2497  * @cfg {String} title Title of dialog
2498  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2499  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2500  * @cfg {Boolean} specificTitle default false
2501  * @cfg {Array} buttons Array of buttons or standard button set..
2502  * @cfg {String} buttonPosition (left|right|center) default right
2503  * @cfg {Boolean} animate default true
2504  * @cfg {Boolean} allow_close default true
2505  * @cfg {Boolean} fitwindow default false
2506  * @cfg {String} size (sm|lg) default empty
2507  *
2508  *
2509  * @constructor
2510  * Create a new Modal Dialog
2511  * @param {Object} config The config object
2512  */
2513
2514 Roo.bootstrap.Modal = function(config){
2515     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2516     this.addEvents({
2517         // raw events
2518         /**
2519          * @event btnclick
2520          * The raw btnclick event for the button
2521          * @param {Roo.EventObject} e
2522          */
2523         "btnclick" : true,
2524         /**
2525          * @event resize
2526          * Fire when dialog resize
2527          * @param {Roo.bootstrap.Modal} this
2528          * @param {Roo.EventObject} e
2529          */
2530         "resize" : true
2531     });
2532     this.buttons = this.buttons || [];
2533
2534     if (this.tmpl) {
2535         this.tmpl = Roo.factory(this.tmpl);
2536     }
2537
2538 };
2539
2540 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2541
2542     title : 'test dialog',
2543
2544     buttons : false,
2545
2546     // set on load...
2547
2548     html: false,
2549
2550     tmp: false,
2551
2552     specificTitle: false,
2553
2554     buttonPosition: 'right',
2555
2556     allow_close : true,
2557
2558     animate : true,
2559
2560     fitwindow: false,
2561
2562
2563      // private
2564     dialogEl: false,
2565     bodyEl:  false,
2566     footerEl:  false,
2567     titleEl:  false,
2568     closeEl:  false,
2569
2570     size: '',
2571
2572
2573     onRender : function(ct, position)
2574     {
2575         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2576
2577         if(!this.el){
2578             var cfg = Roo.apply({},  this.getAutoCreate());
2579             cfg.id = Roo.id();
2580             //if(!cfg.name){
2581             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2582             //}
2583             //if (!cfg.name.length) {
2584             //    delete cfg.name;
2585            // }
2586             if (this.cls) {
2587                 cfg.cls += ' ' + this.cls;
2588             }
2589             if (this.style) {
2590                 cfg.style = this.style;
2591             }
2592             this.el = Roo.get(document.body).createChild(cfg, position);
2593         }
2594         //var type = this.el.dom.type;
2595
2596
2597         if(this.tabIndex !== undefined){
2598             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2599         }
2600
2601         this.dialogEl = this.el.select('.modal-dialog',true).first();
2602         this.bodyEl = this.el.select('.modal-body',true).first();
2603         this.closeEl = this.el.select('.modal-header .close', true).first();
2604         this.headerEl = this.el.select('.modal-header',true).first();
2605         this.titleEl = this.el.select('.modal-title',true).first();
2606         this.footerEl = this.el.select('.modal-footer',true).first();
2607
2608         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2609         this.maskEl.enableDisplayMode("block");
2610         this.maskEl.hide();
2611         //this.el.addClass("x-dlg-modal");
2612
2613         if (this.buttons.length) {
2614             Roo.each(this.buttons, function(bb) {
2615                 var b = Roo.apply({}, bb);
2616                 b.xns = b.xns || Roo.bootstrap;
2617                 b.xtype = b.xtype || 'Button';
2618                 if (typeof(b.listeners) == 'undefined') {
2619                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2620                 }
2621
2622                 var btn = Roo.factory(b);
2623
2624                 btn.render(this.el.select('.modal-footer div').first());
2625
2626             },this);
2627         }
2628         // render the children.
2629         var nitems = [];
2630
2631         if(typeof(this.items) != 'undefined'){
2632             var items = this.items;
2633             delete this.items;
2634
2635             for(var i =0;i < items.length;i++) {
2636                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2637             }
2638         }
2639
2640         this.items = nitems;
2641
2642         // where are these used - they used to be body/close/footer
2643
2644
2645         this.initEvents();
2646         //this.el.addClass([this.fieldClass, this.cls]);
2647
2648     },
2649
2650     getAutoCreate : function(){
2651
2652
2653         var bdy = {
2654                 cls : 'modal-body',
2655                 html : this.html || ''
2656         };
2657
2658         var title = {
2659             tag: 'h4',
2660             cls : 'modal-title',
2661             html : this.title
2662         };
2663
2664         if(this.specificTitle){
2665             title = this.title;
2666
2667         };
2668
2669         var header = [];
2670         if (this.allow_close) {
2671             header.push({
2672                 tag: 'button',
2673                 cls : 'close',
2674                 html : '&times'
2675             });
2676         }
2677
2678         header.push(title);
2679
2680         var size = '';
2681
2682         if(this.size.length){
2683             size = 'modal-' + this.size;
2684         }
2685
2686         var modal = {
2687             cls: "modal",
2688             style : 'display: none',
2689             cn : [
2690                 {
2691                     cls: "modal-dialog " + size,
2692                     cn : [
2693                         {
2694                             cls : "modal-content",
2695                             cn : [
2696                                 {
2697                                     cls : 'modal-header',
2698                                     cn : header
2699                                 },
2700                                 bdy,
2701                                 {
2702                                     cls : 'modal-footer',
2703                                     cn : [
2704                                         {
2705                                             tag: 'div',
2706                                             cls: 'btn-' + this.buttonPosition
2707                                         }
2708                                     ]
2709
2710                                 }
2711
2712
2713                             ]
2714
2715                         }
2716                     ]
2717
2718                 }
2719             ]
2720         };
2721
2722         if(this.animate){
2723             modal.cls += ' fade';
2724         }
2725
2726         return modal;
2727
2728     },
2729     getChildContainer : function() {
2730
2731          return this.bodyEl;
2732
2733     },
2734     getButtonContainer : function() {
2735          return this.el.select('.modal-footer div',true).first();
2736
2737     },
2738     initEvents : function()
2739     {
2740         if (this.allow_close) {
2741             this.closeEl.on('click', this.hide, this);
2742         }
2743         Roo.EventManager.onWindowResize(this.resize, this, true);
2744
2745
2746     },
2747
2748     resize : function()
2749     {
2750         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2751         if (this.fitwindow) {
2752             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2753             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2754             this.setSize(w,h);
2755         }
2756     },
2757
2758     setSize : function(w,h)
2759     {
2760         if (!w && !h) {
2761             return;
2762         }
2763         this.resizeTo(w,h);
2764     },
2765
2766     show : function() {
2767
2768         if (!this.rendered) {
2769             this.render();
2770         }
2771
2772         this.el.setStyle('display', 'block');
2773
2774         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2775             var _this = this;
2776             (function(){
2777                 this.el.addClass('in');
2778             }).defer(50, this);
2779         }else{
2780             this.el.addClass('in');
2781
2782         }
2783
2784         // not sure how we can show data in here..
2785         //if (this.tmpl) {
2786         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2787         //}
2788
2789         Roo.get(document.body).addClass("x-body-masked");
2790         
2791         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2792         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2793         this.maskEl.show();
2794         
2795         this.resize();
2796         
2797         this.fireEvent('show', this);
2798
2799         // set zindex here - otherwise it appears to be ignored...
2800         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2801
2802         (function () {
2803             this.items.forEach( function(e) {
2804                 e.layout ? e.layout() : false;
2805
2806             });
2807         }).defer(100,this);
2808
2809     },
2810     hide : function()
2811     {
2812         if(this.fireEvent("beforehide", this) !== false){
2813             this.maskEl.hide();
2814             Roo.get(document.body).removeClass("x-body-masked");
2815             this.el.removeClass('in');
2816             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2817
2818             if(this.animate){ // why
2819                 var _this = this;
2820                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2821             }else{
2822                 this.el.setStyle('display', 'none');
2823             }
2824             this.fireEvent('hide', this);
2825         }
2826     },
2827
2828     addButton : function(str, cb)
2829     {
2830
2831
2832         var b = Roo.apply({}, { html : str } );
2833         b.xns = b.xns || Roo.bootstrap;
2834         b.xtype = b.xtype || 'Button';
2835         if (typeof(b.listeners) == 'undefined') {
2836             b.listeners = { click : cb.createDelegate(this)  };
2837         }
2838
2839         var btn = Roo.factory(b);
2840
2841         btn.render(this.el.select('.modal-footer div').first());
2842
2843         return btn;
2844
2845     },
2846
2847     setDefaultButton : function(btn)
2848     {
2849         //this.el.select('.modal-footer').()
2850     },
2851     diff : false,
2852
2853     resizeTo: function(w,h)
2854     {
2855         // skip.. ?? why??
2856
2857         this.dialogEl.setWidth(w);
2858         if (this.diff === false) {
2859             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2860         }
2861
2862         this.bodyEl.setHeight(h-this.diff);
2863
2864         this.fireEvent('resize', this);
2865
2866     },
2867     setContentSize  : function(w, h)
2868     {
2869
2870     },
2871     onButtonClick: function(btn,e)
2872     {
2873         //Roo.log([a,b,c]);
2874         this.fireEvent('btnclick', btn.name, e);
2875     },
2876      /**
2877      * Set the title of the Dialog
2878      * @param {String} str new Title
2879      */
2880     setTitle: function(str) {
2881         this.titleEl.dom.innerHTML = str;
2882     },
2883     /**
2884      * Set the body of the Dialog
2885      * @param {String} str new Title
2886      */
2887     setBody: function(str) {
2888         this.bodyEl.dom.innerHTML = str;
2889     },
2890     /**
2891      * Set the body of the Dialog using the template
2892      * @param {Obj} data - apply this data to the template and replace the body contents.
2893      */
2894     applyBody: function(obj)
2895     {
2896         if (!this.tmpl) {
2897             Roo.log("Error - using apply Body without a template");
2898             //code
2899         }
2900         this.tmpl.overwrite(this.bodyEl, obj);
2901     }
2902
2903 });
2904
2905
2906 Roo.apply(Roo.bootstrap.Modal,  {
2907     /**
2908          * Button config that displays a single OK button
2909          * @type Object
2910          */
2911         OK :  [{
2912             name : 'ok',
2913             weight : 'primary',
2914             html : 'OK'
2915         }],
2916         /**
2917          * Button config that displays Yes and No buttons
2918          * @type Object
2919          */
2920         YESNO : [
2921             {
2922                 name  : 'no',
2923                 html : 'No'
2924             },
2925             {
2926                 name  :'yes',
2927                 weight : 'primary',
2928                 html : 'Yes'
2929             }
2930         ],
2931
2932         /**
2933          * Button config that displays OK and Cancel buttons
2934          * @type Object
2935          */
2936         OKCANCEL : [
2937             {
2938                name : 'cancel',
2939                 html : 'Cancel'
2940             },
2941             {
2942                 name : 'ok',
2943                 weight : 'primary',
2944                 html : 'OK'
2945             }
2946         ],
2947         /**
2948          * Button config that displays Yes, No and Cancel buttons
2949          * @type Object
2950          */
2951         YESNOCANCEL : [
2952             {
2953                 name : 'yes',
2954                 weight : 'primary',
2955                 html : 'Yes'
2956             },
2957             {
2958                 name : 'no',
2959                 html : 'No'
2960             },
2961             {
2962                 name : 'cancel',
2963                 html : 'Cancel'
2964             }
2965         ],
2966         
2967         zIndex : 10001
2968 });
2969 /*
2970  * - LGPL
2971  *
2972  * messagebox - can be used as a replace
2973  * 
2974  */
2975 /**
2976  * @class Roo.MessageBox
2977  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2978  * Example usage:
2979  *<pre><code>
2980 // Basic alert:
2981 Roo.Msg.alert('Status', 'Changes saved successfully.');
2982
2983 // Prompt for user data:
2984 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2985     if (btn == 'ok'){
2986         // process text value...
2987     }
2988 });
2989
2990 // Show a dialog using config options:
2991 Roo.Msg.show({
2992    title:'Save Changes?',
2993    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2994    buttons: Roo.Msg.YESNOCANCEL,
2995    fn: processResult,
2996    animEl: 'elId'
2997 });
2998 </code></pre>
2999  * @singleton
3000  */
3001 Roo.bootstrap.MessageBox = function(){
3002     var dlg, opt, mask, waitTimer;
3003     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3004     var buttons, activeTextEl, bwidth;
3005
3006     
3007     // private
3008     var handleButton = function(button){
3009         dlg.hide();
3010         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3011     };
3012
3013     // private
3014     var handleHide = function(){
3015         if(opt && opt.cls){
3016             dlg.el.removeClass(opt.cls);
3017         }
3018         //if(waitTimer){
3019         //    Roo.TaskMgr.stop(waitTimer);
3020         //    waitTimer = null;
3021         //}
3022     };
3023
3024     // private
3025     var updateButtons = function(b){
3026         var width = 0;
3027         if(!b){
3028             buttons["ok"].hide();
3029             buttons["cancel"].hide();
3030             buttons["yes"].hide();
3031             buttons["no"].hide();
3032             //dlg.footer.dom.style.display = 'none';
3033             return width;
3034         }
3035         dlg.footerEl.dom.style.display = '';
3036         for(var k in buttons){
3037             if(typeof buttons[k] != "function"){
3038                 if(b[k]){
3039                     buttons[k].show();
3040                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3041                     width += buttons[k].el.getWidth()+15;
3042                 }else{
3043                     buttons[k].hide();
3044                 }
3045             }
3046         }
3047         return width;
3048     };
3049
3050     // private
3051     var handleEsc = function(d, k, e){
3052         if(opt && opt.closable !== false){
3053             dlg.hide();
3054         }
3055         if(e){
3056             e.stopEvent();
3057         }
3058     };
3059
3060     return {
3061         /**
3062          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3063          * @return {Roo.BasicDialog} The BasicDialog element
3064          */
3065         getDialog : function(){
3066            if(!dlg){
3067                 dlg = new Roo.bootstrap.Modal( {
3068                     //draggable: true,
3069                     //resizable:false,
3070                     //constraintoviewport:false,
3071                     //fixedcenter:true,
3072                     //collapsible : false,
3073                     //shim:true,
3074                     //modal: true,
3075                 //    width: 'auto',
3076                   //  height:100,
3077                     //buttonAlign:"center",
3078                     closeClick : function(){
3079                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3080                             handleButton("no");
3081                         }else{
3082                             handleButton("cancel");
3083                         }
3084                     }
3085                 });
3086                 dlg.render();
3087                 dlg.on("hide", handleHide);
3088                 mask = dlg.mask;
3089                 //dlg.addKeyListener(27, handleEsc);
3090                 buttons = {};
3091                 this.buttons = buttons;
3092                 var bt = this.buttonText;
3093                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3094                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3095                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3096                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3097                 //Roo.log(buttons);
3098                 bodyEl = dlg.bodyEl.createChild({
3099
3100                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3101                         '<textarea class="roo-mb-textarea"></textarea>' +
3102                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3103                 });
3104                 msgEl = bodyEl.dom.firstChild;
3105                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3106                 textboxEl.enableDisplayMode();
3107                 textboxEl.addKeyListener([10,13], function(){
3108                     if(dlg.isVisible() && opt && opt.buttons){
3109                         if(opt.buttons.ok){
3110                             handleButton("ok");
3111                         }else if(opt.buttons.yes){
3112                             handleButton("yes");
3113                         }
3114                     }
3115                 });
3116                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3117                 textareaEl.enableDisplayMode();
3118                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3119                 progressEl.enableDisplayMode();
3120                 
3121                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3122                 //var pf = progressEl.dom.firstChild;
3123                 //if (pf) {
3124                     //pp = Roo.get(pf.firstChild);
3125                     //pp.setHeight(pf.offsetHeight);
3126                 //}
3127                 
3128             }
3129             return dlg;
3130         },
3131
3132         /**
3133          * Updates the message box body text
3134          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3135          * the XHTML-compliant non-breaking space character '&amp;#160;')
3136          * @return {Roo.MessageBox} This message box
3137          */
3138         updateText : function(text)
3139         {
3140             if(!dlg.isVisible() && !opt.width){
3141                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3142                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3143             }
3144             msgEl.innerHTML = text || '&#160;';
3145       
3146             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3147             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3148             var w = Math.max(
3149                     Math.min(opt.width || cw , this.maxWidth), 
3150                     Math.max(opt.minWidth || this.minWidth, bwidth)
3151             );
3152             if(opt.prompt){
3153                 activeTextEl.setWidth(w);
3154             }
3155             if(dlg.isVisible()){
3156                 dlg.fixedcenter = false;
3157             }
3158             // to big, make it scroll. = But as usual stupid IE does not support
3159             // !important..
3160             
3161             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3162                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3163                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3164             } else {
3165                 bodyEl.dom.style.height = '';
3166                 bodyEl.dom.style.overflowY = '';
3167             }
3168             if (cw > w) {
3169                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3170             } else {
3171                 bodyEl.dom.style.overflowX = '';
3172             }
3173             
3174             dlg.setContentSize(w, bodyEl.getHeight());
3175             if(dlg.isVisible()){
3176                 dlg.fixedcenter = true;
3177             }
3178             return this;
3179         },
3180
3181         /**
3182          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3183          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3184          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3185          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3186          * @return {Roo.MessageBox} This message box
3187          */
3188         updateProgress : function(value, text){
3189             if(text){
3190                 this.updateText(text);
3191             }
3192             if (pp) { // weird bug on my firefox - for some reason this is not defined
3193                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3194             }
3195             return this;
3196         },        
3197
3198         /**
3199          * Returns true if the message box is currently displayed
3200          * @return {Boolean} True if the message box is visible, else false
3201          */
3202         isVisible : function(){
3203             return dlg && dlg.isVisible();  
3204         },
3205
3206         /**
3207          * Hides the message box if it is displayed
3208          */
3209         hide : function(){
3210             if(this.isVisible()){
3211                 dlg.hide();
3212             }  
3213         },
3214
3215         /**
3216          * Displays a new message box, or reinitializes an existing message box, based on the config options
3217          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3218          * The following config object properties are supported:
3219          * <pre>
3220 Property    Type             Description
3221 ----------  ---------------  ------------------------------------------------------------------------------------
3222 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3223                                    closes (defaults to undefined)
3224 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3225                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3226 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3227                                    progress and wait dialogs will ignore this property and always hide the
3228                                    close button as they can only be closed programmatically.
3229 cls               String           A custom CSS class to apply to the message box element
3230 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3231                                    displayed (defaults to 75)
3232 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3233                                    function will be btn (the name of the button that was clicked, if applicable,
3234                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3235                                    Progress and wait dialogs will ignore this option since they do not respond to
3236                                    user actions and can only be closed programmatically, so any required function
3237                                    should be called by the same code after it closes the dialog.
3238 icon              String           A CSS class that provides a background image to be used as an icon for
3239                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3240 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3241 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3242 modal             Boolean          False to allow user interaction with the page while the message box is
3243                                    displayed (defaults to true)
3244 msg               String           A string that will replace the existing message box body text (defaults
3245                                    to the XHTML-compliant non-breaking space character '&#160;')
3246 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3247 progress          Boolean          True to display a progress bar (defaults to false)
3248 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3249 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3250 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3251 title             String           The title text
3252 value             String           The string value to set into the active textbox element if displayed
3253 wait              Boolean          True to display a progress bar (defaults to false)
3254 width             Number           The width of the dialog in pixels
3255 </pre>
3256          *
3257          * Example usage:
3258          * <pre><code>
3259 Roo.Msg.show({
3260    title: 'Address',
3261    msg: 'Please enter your address:',
3262    width: 300,
3263    buttons: Roo.MessageBox.OKCANCEL,
3264    multiline: true,
3265    fn: saveAddress,
3266    animEl: 'addAddressBtn'
3267 });
3268 </code></pre>
3269          * @param {Object} config Configuration options
3270          * @return {Roo.MessageBox} This message box
3271          */
3272         show : function(options)
3273         {
3274             
3275             // this causes nightmares if you show one dialog after another
3276             // especially on callbacks..
3277              
3278             if(this.isVisible()){
3279                 
3280                 this.hide();
3281                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3282                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3283                 Roo.log("New Dialog Message:" +  options.msg )
3284                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3285                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3286                 
3287             }
3288             var d = this.getDialog();
3289             opt = options;
3290             d.setTitle(opt.title || "&#160;");
3291             d.closeEl.setDisplayed(opt.closable !== false);
3292             activeTextEl = textboxEl;
3293             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3294             if(opt.prompt){
3295                 if(opt.multiline){
3296                     textboxEl.hide();
3297                     textareaEl.show();
3298                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3299                         opt.multiline : this.defaultTextHeight);
3300                     activeTextEl = textareaEl;
3301                 }else{
3302                     textboxEl.show();
3303                     textareaEl.hide();
3304                 }
3305             }else{
3306                 textboxEl.hide();
3307                 textareaEl.hide();
3308             }
3309             progressEl.setDisplayed(opt.progress === true);
3310             this.updateProgress(0);
3311             activeTextEl.dom.value = opt.value || "";
3312             if(opt.prompt){
3313                 dlg.setDefaultButton(activeTextEl);
3314             }else{
3315                 var bs = opt.buttons;
3316                 var db = null;
3317                 if(bs && bs.ok){
3318                     db = buttons["ok"];
3319                 }else if(bs && bs.yes){
3320                     db = buttons["yes"];
3321                 }
3322                 dlg.setDefaultButton(db);
3323             }
3324             bwidth = updateButtons(opt.buttons);
3325             this.updateText(opt.msg);
3326             if(opt.cls){
3327                 d.el.addClass(opt.cls);
3328             }
3329             d.proxyDrag = opt.proxyDrag === true;
3330             d.modal = opt.modal !== false;
3331             d.mask = opt.modal !== false ? mask : false;
3332             if(!d.isVisible()){
3333                 // force it to the end of the z-index stack so it gets a cursor in FF
3334                 document.body.appendChild(dlg.el.dom);
3335                 d.animateTarget = null;
3336                 d.show(options.animEl);
3337             }
3338             return this;
3339         },
3340
3341         /**
3342          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3343          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3344          * and closing the message box when the process is complete.
3345          * @param {String} title The title bar text
3346          * @param {String} msg The message box body text
3347          * @return {Roo.MessageBox} This message box
3348          */
3349         progress : function(title, msg){
3350             this.show({
3351                 title : title,
3352                 msg : msg,
3353                 buttons: false,
3354                 progress:true,
3355                 closable:false,
3356                 minWidth: this.minProgressWidth,
3357                 modal : true
3358             });
3359             return this;
3360         },
3361
3362         /**
3363          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3364          * If a callback function is passed it will be called after the user clicks the button, and the
3365          * id of the button that was clicked will be passed as the only parameter to the callback
3366          * (could also be the top-right close button).
3367          * @param {String} title The title bar text
3368          * @param {String} msg The message box body text
3369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3370          * @param {Object} scope (optional) The scope of the callback function
3371          * @return {Roo.MessageBox} This message box
3372          */
3373         alert : function(title, msg, fn, scope)
3374         {
3375             this.show({
3376                 title : title,
3377                 msg : msg,
3378                 buttons: this.OK,
3379                 fn: fn,
3380                 closable : false,
3381                 scope : scope,
3382                 modal : true
3383             });
3384             return this;
3385         },
3386
3387         /**
3388          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3389          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3390          * You are responsible for closing the message box when the process is complete.
3391          * @param {String} msg The message box body text
3392          * @param {String} title (optional) The title bar text
3393          * @return {Roo.MessageBox} This message box
3394          */
3395         wait : function(msg, title){
3396             this.show({
3397                 title : title,
3398                 msg : msg,
3399                 buttons: false,
3400                 closable:false,
3401                 progress:true,
3402                 modal:true,
3403                 width:300,
3404                 wait:true
3405             });
3406             waitTimer = Roo.TaskMgr.start({
3407                 run: function(i){
3408                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3409                 },
3410                 interval: 1000
3411             });
3412             return this;
3413         },
3414
3415         /**
3416          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3417          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3418          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3419          * @param {String} title The title bar text
3420          * @param {String} msg The message box body text
3421          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3422          * @param {Object} scope (optional) The scope of the callback function
3423          * @return {Roo.MessageBox} This message box
3424          */
3425         confirm : function(title, msg, fn, scope){
3426             this.show({
3427                 title : title,
3428                 msg : msg,
3429                 buttons: this.YESNO,
3430                 fn: fn,
3431                 scope : scope,
3432                 modal : true
3433             });
3434             return this;
3435         },
3436
3437         /**
3438          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3439          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3440          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3441          * (could also be the top-right close button) and the text that was entered will be passed as the two
3442          * parameters to the callback.
3443          * @param {String} title The title bar text
3444          * @param {String} msg The message box body text
3445          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3446          * @param {Object} scope (optional) The scope of the callback function
3447          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3448          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3449          * @return {Roo.MessageBox} This message box
3450          */
3451         prompt : function(title, msg, fn, scope, multiline){
3452             this.show({
3453                 title : title,
3454                 msg : msg,
3455                 buttons: this.OKCANCEL,
3456                 fn: fn,
3457                 minWidth:250,
3458                 scope : scope,
3459                 prompt:true,
3460                 multiline: multiline,
3461                 modal : true
3462             });
3463             return this;
3464         },
3465
3466         /**
3467          * Button config that displays a single OK button
3468          * @type Object
3469          */
3470         OK : {ok:true},
3471         /**
3472          * Button config that displays Yes and No buttons
3473          * @type Object
3474          */
3475         YESNO : {yes:true, no:true},
3476         /**
3477          * Button config that displays OK and Cancel buttons
3478          * @type Object
3479          */
3480         OKCANCEL : {ok:true, cancel:true},
3481         /**
3482          * Button config that displays Yes, No and Cancel buttons
3483          * @type Object
3484          */
3485         YESNOCANCEL : {yes:true, no:true, cancel:true},
3486
3487         /**
3488          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3489          * @type Number
3490          */
3491         defaultTextHeight : 75,
3492         /**
3493          * The maximum width in pixels of the message box (defaults to 600)
3494          * @type Number
3495          */
3496         maxWidth : 600,
3497         /**
3498          * The minimum width in pixels of the message box (defaults to 100)
3499          * @type Number
3500          */
3501         minWidth : 100,
3502         /**
3503          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3504          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3505          * @type Number
3506          */
3507         minProgressWidth : 250,
3508         /**
3509          * An object containing the default button text strings that can be overriden for localized language support.
3510          * Supported properties are: ok, cancel, yes and no.
3511          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3512          * @type Object
3513          */
3514         buttonText : {
3515             ok : "OK",
3516             cancel : "Cancel",
3517             yes : "Yes",
3518             no : "No"
3519         }
3520     };
3521 }();
3522
3523 /**
3524  * Shorthand for {@link Roo.MessageBox}
3525  */
3526 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3527 Roo.Msg = Roo.Msg || Roo.MessageBox;
3528 /*
3529  * - LGPL
3530  *
3531  * navbar
3532  * 
3533  */
3534
3535 /**
3536  * @class Roo.bootstrap.Navbar
3537  * @extends Roo.bootstrap.Component
3538  * Bootstrap Navbar class
3539
3540  * @constructor
3541  * Create a new Navbar
3542  * @param {Object} config The config object
3543  */
3544
3545
3546 Roo.bootstrap.Navbar = function(config){
3547     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3548     this.addEvents({
3549         // raw events
3550         /**
3551          * @event beforetoggle
3552          * Fire before toggle the menu
3553          * @param {Roo.EventObject} e
3554          */
3555         "beforetoggle" : true
3556     });
3557 };
3558
3559 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3560     
3561     
3562    
3563     // private
3564     navItems : false,
3565     loadMask : false,
3566     
3567     
3568     getAutoCreate : function(){
3569         
3570         
3571         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3572         
3573     },
3574     
3575     initEvents :function ()
3576     {
3577         //Roo.log(this.el.select('.navbar-toggle',true));
3578         this.el.select('.navbar-toggle',true).on('click', function() {
3579             if(this.fireEvent('beforetoggle', this) !== false){
3580                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3581             }
3582             
3583         }, this);
3584         
3585         var mark = {
3586             tag: "div",
3587             cls:"x-dlg-mask"
3588         };
3589         
3590         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3591         
3592         var size = this.el.getSize();
3593         this.maskEl.setSize(size.width, size.height);
3594         this.maskEl.enableDisplayMode("block");
3595         this.maskEl.hide();
3596         
3597         if(this.loadMask){
3598             this.maskEl.show();
3599         }
3600     },
3601     
3602     
3603     getChildContainer : function()
3604     {
3605         if (this.el.select('.collapse').getCount()) {
3606             return this.el.select('.collapse',true).first();
3607         }
3608         
3609         return this.el;
3610     },
3611     
3612     mask : function()
3613     {
3614         this.maskEl.show();
3615     },
3616     
3617     unmask : function()
3618     {
3619         this.maskEl.hide();
3620     } 
3621     
3622     
3623     
3624     
3625 });
3626
3627
3628
3629  
3630
3631  /*
3632  * - LGPL
3633  *
3634  * navbar
3635  * 
3636  */
3637
3638 /**
3639  * @class Roo.bootstrap.NavSimplebar
3640  * @extends Roo.bootstrap.Navbar
3641  * Bootstrap Sidebar class
3642  *
3643  * @cfg {Boolean} inverse is inverted color
3644  * 
3645  * @cfg {String} type (nav | pills | tabs)
3646  * @cfg {Boolean} arrangement stacked | justified
3647  * @cfg {String} align (left | right) alignment
3648  * 
3649  * @cfg {Boolean} main (true|false) main nav bar? default false
3650  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3651  * 
3652  * @cfg {String} tag (header|footer|nav|div) default is nav 
3653
3654  * 
3655  * 
3656  * 
3657  * @constructor
3658  * Create a new Sidebar
3659  * @param {Object} config The config object
3660  */
3661
3662
3663 Roo.bootstrap.NavSimplebar = function(config){
3664     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3665 };
3666
3667 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3668     
3669     inverse: false,
3670     
3671     type: false,
3672     arrangement: '',
3673     align : false,
3674     
3675     
3676     
3677     main : false,
3678     
3679     
3680     tag : false,
3681     
3682     
3683     getAutoCreate : function(){
3684         
3685         
3686         var cfg = {
3687             tag : this.tag || 'div',
3688             cls : 'navbar'
3689         };
3690           
3691         
3692         cfg.cn = [
3693             {
3694                 cls: 'nav',
3695                 tag : 'ul'
3696             }
3697         ];
3698         
3699          
3700         this.type = this.type || 'nav';
3701         if (['tabs','pills'].indexOf(this.type)!==-1) {
3702             cfg.cn[0].cls += ' nav-' + this.type
3703         
3704         
3705         } else {
3706             if (this.type!=='nav') {
3707                 Roo.log('nav type must be nav/tabs/pills')
3708             }
3709             cfg.cn[0].cls += ' navbar-nav'
3710         }
3711         
3712         
3713         
3714         
3715         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3716             cfg.cn[0].cls += ' nav-' + this.arrangement;
3717         }
3718         
3719         
3720         if (this.align === 'right') {
3721             cfg.cn[0].cls += ' navbar-right';
3722         }
3723         
3724         if (this.inverse) {
3725             cfg.cls += ' navbar-inverse';
3726             
3727         }
3728         
3729         
3730         return cfg;
3731     
3732         
3733     }
3734     
3735     
3736     
3737 });
3738
3739
3740
3741  
3742
3743  
3744        /*
3745  * - LGPL
3746  *
3747  * navbar
3748  * 
3749  */
3750
3751 /**
3752  * @class Roo.bootstrap.NavHeaderbar
3753  * @extends Roo.bootstrap.NavSimplebar
3754  * Bootstrap Sidebar class
3755  *
3756  * @cfg {String} brand what is brand
3757  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3758  * @cfg {String} brand_href href of the brand
3759  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3760  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3761  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3762  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3763  * 
3764  * @constructor
3765  * Create a new Sidebar
3766  * @param {Object} config The config object
3767  */
3768
3769
3770 Roo.bootstrap.NavHeaderbar = function(config){
3771     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3772       
3773 };
3774
3775 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3776     
3777     position: '',
3778     brand: '',
3779     brand_href: false,
3780     srButton : true,
3781     autohide : false,
3782     desktopCenter : false,
3783    
3784     
3785     getAutoCreate : function(){
3786         
3787         var   cfg = {
3788             tag: this.nav || 'nav',
3789             cls: 'navbar',
3790             role: 'navigation',
3791             cn: []
3792         };
3793         
3794         var cn = cfg.cn;
3795         if (this.desktopCenter) {
3796             cn.push({cls : 'container', cn : []});
3797             cn = cn[0].cn;
3798         }
3799         
3800         if(this.srButton){
3801             cn.push({
3802                 tag: 'div',
3803                 cls: 'navbar-header',
3804                 cn: [
3805                     {
3806                         tag: 'button',
3807                         type: 'button',
3808                         cls: 'navbar-toggle',
3809                         'data-toggle': 'collapse',
3810                         cn: [
3811                             {
3812                                 tag: 'span',
3813                                 cls: 'sr-only',
3814                                 html: 'Toggle navigation'
3815                             },
3816                             {
3817                                 tag: 'span',
3818                                 cls: 'icon-bar'
3819                             },
3820                             {
3821                                 tag: 'span',
3822                                 cls: 'icon-bar'
3823                             },
3824                             {
3825                                 tag: 'span',
3826                                 cls: 'icon-bar'
3827                             }
3828                         ]
3829                     }
3830                 ]
3831             });
3832         }
3833         
3834         cn.push({
3835             tag: 'div',
3836             cls: 'collapse navbar-collapse',
3837             cn : []
3838         });
3839         
3840         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3841         
3842         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3843             cfg.cls += ' navbar-' + this.position;
3844             
3845             // tag can override this..
3846             
3847             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3848         }
3849         
3850         if (this.brand !== '') {
3851             cn[0].cn.push({
3852                 tag: 'a',
3853                 href: this.brand_href ? this.brand_href : '#',
3854                 cls: 'navbar-brand',
3855                 cn: [
3856                 this.brand
3857                 ]
3858             });
3859         }
3860         
3861         if(this.main){
3862             cfg.cls += ' main-nav';
3863         }
3864         
3865         
3866         return cfg;
3867
3868         
3869     },
3870     getHeaderChildContainer : function()
3871     {
3872         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3873             return this.el.select('.navbar-header',true).first();
3874         }
3875         
3876         return this.getChildContainer();
3877     },
3878     
3879     
3880     initEvents : function()
3881     {
3882         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3883         
3884         if (this.autohide) {
3885             
3886             var prevScroll = 0;
3887             var ft = this.el;
3888             
3889             Roo.get(document).on('scroll',function(e) {
3890                 var ns = Roo.get(document).getScroll().top;
3891                 var os = prevScroll;
3892                 prevScroll = ns;
3893                 
3894                 if(ns > os){
3895                     ft.removeClass('slideDown');
3896                     ft.addClass('slideUp');
3897                     return;
3898                 }
3899                 ft.removeClass('slideUp');
3900                 ft.addClass('slideDown');
3901                  
3902               
3903           },this);
3904         }
3905     }    
3906     
3907 });
3908
3909
3910
3911  
3912
3913  /*
3914  * - LGPL
3915  *
3916  * navbar
3917  * 
3918  */
3919
3920 /**
3921  * @class Roo.bootstrap.NavSidebar
3922  * @extends Roo.bootstrap.Navbar
3923  * Bootstrap Sidebar class
3924  * 
3925  * @constructor
3926  * Create a new Sidebar
3927  * @param {Object} config The config object
3928  */
3929
3930
3931 Roo.bootstrap.NavSidebar = function(config){
3932     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3933 };
3934
3935 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3936     
3937     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3938     
3939     getAutoCreate : function(){
3940         
3941         
3942         return  {
3943             tag: 'div',
3944             cls: 'sidebar sidebar-nav'
3945         };
3946     
3947         
3948     }
3949     
3950     
3951     
3952 });
3953
3954
3955
3956  
3957
3958  /*
3959  * - LGPL
3960  *
3961  * nav group
3962  * 
3963  */
3964
3965 /**
3966  * @class Roo.bootstrap.NavGroup
3967  * @extends Roo.bootstrap.Component
3968  * Bootstrap NavGroup class
3969  * @cfg {String} align (left|right)
3970  * @cfg {Boolean} inverse
3971  * @cfg {String} type (nav|pills|tab) default nav
3972  * @cfg {String} navId - reference Id for navbar.
3973
3974  * 
3975  * @constructor
3976  * Create a new nav group
3977  * @param {Object} config The config object
3978  */
3979
3980 Roo.bootstrap.NavGroup = function(config){
3981     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3982     this.navItems = [];
3983    
3984     Roo.bootstrap.NavGroup.register(this);
3985      this.addEvents({
3986         /**
3987              * @event changed
3988              * Fires when the active item changes
3989              * @param {Roo.bootstrap.NavGroup} this
3990              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3991              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3992          */
3993         'changed': true
3994      });
3995     
3996 };
3997
3998 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3999     
4000     align: '',
4001     inverse: false,
4002     form: false,
4003     type: 'nav',
4004     navId : '',
4005     // private
4006     
4007     navItems : false, 
4008     
4009     getAutoCreate : function()
4010     {
4011         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4012         
4013         cfg = {
4014             tag : 'ul',
4015             cls: 'nav' 
4016         };
4017         
4018         if (['tabs','pills'].indexOf(this.type)!==-1) {
4019             cfg.cls += ' nav-' + this.type
4020         } else {
4021             if (this.type!=='nav') {
4022                 Roo.log('nav type must be nav/tabs/pills')
4023             }
4024             cfg.cls += ' navbar-nav'
4025         }
4026         
4027         if (this.parent() && this.parent().sidebar) {
4028             cfg = {
4029                 tag: 'ul',
4030                 cls: 'dashboard-menu sidebar-menu'
4031             };
4032             
4033             return cfg;
4034         }
4035         
4036         if (this.form === true) {
4037             cfg = {
4038                 tag: 'form',
4039                 cls: 'navbar-form'
4040             };
4041             
4042             if (this.align === 'right') {
4043                 cfg.cls += ' navbar-right';
4044             } else {
4045                 cfg.cls += ' navbar-left';
4046             }
4047         }
4048         
4049         if (this.align === 'right') {
4050             cfg.cls += ' navbar-right';
4051         }
4052         
4053         if (this.inverse) {
4054             cfg.cls += ' navbar-inverse';
4055             
4056         }
4057         
4058         
4059         return cfg;
4060     },
4061     /**
4062     * sets the active Navigation item
4063     * @param {Roo.bootstrap.NavItem} the new current navitem
4064     */
4065     setActiveItem : function(item)
4066     {
4067         var prev = false;
4068         Roo.each(this.navItems, function(v){
4069             if (v == item) {
4070                 return ;
4071             }
4072             if (v.isActive()) {
4073                 v.setActive(false, true);
4074                 prev = v;
4075                 
4076             }
4077             
4078         });
4079
4080         item.setActive(true, true);
4081         this.fireEvent('changed', this, item, prev);
4082         
4083         
4084     },
4085     /**
4086     * gets the active Navigation item
4087     * @return {Roo.bootstrap.NavItem} the current navitem
4088     */
4089     getActive : function()
4090     {
4091         
4092         var prev = false;
4093         Roo.each(this.navItems, function(v){
4094             
4095             if (v.isActive()) {
4096                 prev = v;
4097                 
4098             }
4099             
4100         });
4101         return prev;
4102     },
4103     
4104     indexOfNav : function()
4105     {
4106         
4107         var prev = false;
4108         Roo.each(this.navItems, function(v,i){
4109             
4110             if (v.isActive()) {
4111                 prev = i;
4112                 
4113             }
4114             
4115         });
4116         return prev;
4117     },
4118     /**
4119     * adds a Navigation item
4120     * @param {Roo.bootstrap.NavItem} the navitem to add
4121     */
4122     addItem : function(cfg)
4123     {
4124         var cn = new Roo.bootstrap.NavItem(cfg);
4125         this.register(cn);
4126         cn.parentId = this.id;
4127         cn.onRender(this.el, null);
4128         return cn;
4129     },
4130     /**
4131     * register a Navigation item
4132     * @param {Roo.bootstrap.NavItem} the navitem to add
4133     */
4134     register : function(item)
4135     {
4136         this.navItems.push( item);
4137         item.navId = this.navId;
4138     
4139     },
4140     
4141     /**
4142     * clear all the Navigation item
4143     */
4144    
4145     clearAll : function()
4146     {
4147         this.navItems = [];
4148         this.el.dom.innerHTML = '';
4149     },
4150     
4151     getNavItem: function(tabId)
4152     {
4153         var ret = false;
4154         Roo.each(this.navItems, function(e) {
4155             if (e.tabId == tabId) {
4156                ret =  e;
4157                return false;
4158             }
4159             return true;
4160             
4161         });
4162         return ret;
4163     },
4164     
4165     setActiveNext : function()
4166     {
4167         var i = this.indexOfNav(this.getActive());
4168         if (i > this.navItems.length) {
4169             return;
4170         }
4171         this.setActiveItem(this.navItems[i+1]);
4172     },
4173     setActivePrev : function()
4174     {
4175         var i = this.indexOfNav(this.getActive());
4176         if (i  < 1) {
4177             return;
4178         }
4179         this.setActiveItem(this.navItems[i-1]);
4180     },
4181     clearWasActive : function(except) {
4182         Roo.each(this.navItems, function(e) {
4183             if (e.tabId != except.tabId && e.was_active) {
4184                e.was_active = false;
4185                return false;
4186             }
4187             return true;
4188             
4189         });
4190     },
4191     getWasActive : function ()
4192     {
4193         var r = false;
4194         Roo.each(this.navItems, function(e) {
4195             if (e.was_active) {
4196                r = e;
4197                return false;
4198             }
4199             return true;
4200             
4201         });
4202         return r;
4203     }
4204     
4205     
4206 });
4207
4208  
4209 Roo.apply(Roo.bootstrap.NavGroup, {
4210     
4211     groups: {},
4212      /**
4213     * register a Navigation Group
4214     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4215     */
4216     register : function(navgrp)
4217     {
4218         this.groups[navgrp.navId] = navgrp;
4219         
4220     },
4221     /**
4222     * fetch a Navigation Group based on the navigation ID
4223     * @param {string} the navgroup to add
4224     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4225     */
4226     get: function(navId) {
4227         if (typeof(this.groups[navId]) == 'undefined') {
4228             return false;
4229             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4230         }
4231         return this.groups[navId] ;
4232     }
4233     
4234     
4235     
4236 });
4237
4238  /*
4239  * - LGPL
4240  *
4241  * row
4242  * 
4243  */
4244
4245 /**
4246  * @class Roo.bootstrap.NavItem
4247  * @extends Roo.bootstrap.Component
4248  * Bootstrap Navbar.NavItem class
4249  * @cfg {String} href  link to
4250  * @cfg {String} html content of button
4251  * @cfg {String} badge text inside badge
4252  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4253  * @cfg {String} glyphicon name of glyphicon
4254  * @cfg {String} icon name of font awesome icon
4255  * @cfg {Boolean} active Is item active
4256  * @cfg {Boolean} disabled Is item disabled
4257  
4258  * @cfg {Boolean} preventDefault (true | false) default false
4259  * @cfg {String} tabId the tab that this item activates.
4260  * @cfg {String} tagtype (a|span) render as a href or span?
4261  * @cfg {Boolean} animateRef (true|false) link to element default false  
4262   
4263  * @constructor
4264  * Create a new Navbar Item
4265  * @param {Object} config The config object
4266  */
4267 Roo.bootstrap.NavItem = function(config){
4268     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4269     this.addEvents({
4270         // raw events
4271         /**
4272          * @event click
4273          * The raw click event for the entire grid.
4274          * @param {Roo.EventObject} e
4275          */
4276         "click" : true,
4277          /**
4278             * @event changed
4279             * Fires when the active item active state changes
4280             * @param {Roo.bootstrap.NavItem} this
4281             * @param {boolean} state the new state
4282              
4283          */
4284         'changed': true,
4285         /**
4286             * @event scrollto
4287             * Fires when scroll to element
4288             * @param {Roo.bootstrap.NavItem} this
4289             * @param {Object} options
4290             * @param {Roo.EventObject} e
4291              
4292          */
4293         'scrollto': true
4294     });
4295    
4296 };
4297
4298 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4299     
4300     href: false,
4301     html: '',
4302     badge: '',
4303     icon: false,
4304     glyphicon: false,
4305     active: false,
4306     preventDefault : false,
4307     tabId : false,
4308     tagtype : 'a',
4309     disabled : false,
4310     animateRef : false,
4311     was_active : false,
4312     
4313     getAutoCreate : function(){
4314          
4315         var cfg = {
4316             tag: 'li',
4317             cls: 'nav-item'
4318             
4319         };
4320         
4321         if (this.active) {
4322             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4323         }
4324         if (this.disabled) {
4325             cfg.cls += ' disabled';
4326         }
4327         
4328         if (this.href || this.html || this.glyphicon || this.icon) {
4329             cfg.cn = [
4330                 {
4331                     tag: this.tagtype,
4332                     href : this.href || "#",
4333                     html: this.html || ''
4334                 }
4335             ];
4336             
4337             if (this.icon) {
4338                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4339             }
4340
4341             if(this.glyphicon) {
4342                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4343             }
4344             
4345             if (this.menu) {
4346                 
4347                 cfg.cn[0].html += " <span class='caret'></span>";
4348              
4349             }
4350             
4351             if (this.badge !== '') {
4352                  
4353                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4354             }
4355         }
4356         
4357         
4358         
4359         return cfg;
4360     },
4361     initEvents: function() 
4362     {
4363         if (typeof (this.menu) != 'undefined') {
4364             this.menu.parentType = this.xtype;
4365             this.menu.triggerEl = this.el;
4366             this.menu = this.addxtype(Roo.apply({}, this.menu));
4367         }
4368         
4369         this.el.select('a',true).on('click', this.onClick, this);
4370         
4371         if(this.tagtype == 'span'){
4372             this.el.select('span',true).on('click', this.onClick, this);
4373         }
4374        
4375         // at this point parent should be available..
4376         this.parent().register(this);
4377     },
4378     
4379     onClick : function(e)
4380     {
4381         if (e.getTarget('.dropdown-menu-item')) {
4382             // did you click on a menu itemm.... - then don't trigger onclick..
4383             return;
4384         }
4385         
4386         if(
4387                 this.preventDefault || 
4388                 this.href == '#' 
4389         ){
4390             Roo.log("NavItem - prevent Default?");
4391             e.preventDefault();
4392         }
4393         
4394         if (this.disabled) {
4395             return;
4396         }
4397         
4398         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4399         if (tg && tg.transition) {
4400             Roo.log("waiting for the transitionend");
4401             return;
4402         }
4403         
4404         
4405         
4406         //Roo.log("fire event clicked");
4407         if(this.fireEvent('click', this, e) === false){
4408             return;
4409         };
4410         
4411         if(this.tagtype == 'span'){
4412             return;
4413         }
4414         
4415         //Roo.log(this.href);
4416         var ael = this.el.select('a',true).first();
4417         //Roo.log(ael);
4418         
4419         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4420             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4421             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4422                 return; // ignore... - it's a 'hash' to another page.
4423             }
4424             Roo.log("NavItem - prevent Default?");
4425             e.preventDefault();
4426             this.scrollToElement(e);
4427         }
4428         
4429         
4430         var p =  this.parent();
4431    
4432         if (['tabs','pills'].indexOf(p.type)!==-1) {
4433             if (typeof(p.setActiveItem) !== 'undefined') {
4434                 p.setActiveItem(this);
4435             }
4436         }
4437         
4438         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4439         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4440             // remove the collapsed menu expand...
4441             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4442         }
4443     },
4444     
4445     isActive: function () {
4446         return this.active
4447     },
4448     setActive : function(state, fire, is_was_active)
4449     {
4450         if (this.active && !state && this.navId) {
4451             this.was_active = true;
4452             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4453             if (nv) {
4454                 nv.clearWasActive(this);
4455             }
4456             
4457         }
4458         this.active = state;
4459         
4460         if (!state ) {
4461             this.el.removeClass('active');
4462         } else if (!this.el.hasClass('active')) {
4463             this.el.addClass('active');
4464         }
4465         if (fire) {
4466             this.fireEvent('changed', this, state);
4467         }
4468         
4469         // show a panel if it's registered and related..
4470         
4471         if (!this.navId || !this.tabId || !state || is_was_active) {
4472             return;
4473         }
4474         
4475         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4476         if (!tg) {
4477             return;
4478         }
4479         var pan = tg.getPanelByName(this.tabId);
4480         if (!pan) {
4481             return;
4482         }
4483         // if we can not flip to new panel - go back to old nav highlight..
4484         if (false == tg.showPanel(pan)) {
4485             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4486             if (nv) {
4487                 var onav = nv.getWasActive();
4488                 if (onav) {
4489                     onav.setActive(true, false, true);
4490                 }
4491             }
4492             
4493         }
4494         
4495         
4496         
4497     },
4498      // this should not be here...
4499     setDisabled : function(state)
4500     {
4501         this.disabled = state;
4502         if (!state ) {
4503             this.el.removeClass('disabled');
4504         } else if (!this.el.hasClass('disabled')) {
4505             this.el.addClass('disabled');
4506         }
4507         
4508     },
4509     
4510     /**
4511      * Fetch the element to display the tooltip on.
4512      * @return {Roo.Element} defaults to this.el
4513      */
4514     tooltipEl : function()
4515     {
4516         return this.el.select('' + this.tagtype + '', true).first();
4517     },
4518     
4519     scrollToElement : function(e)
4520     {
4521         var c = document.body;
4522         
4523         /*
4524          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4525          */
4526         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4527             c = document.documentElement;
4528         }
4529         
4530         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4531         
4532         if(!target){
4533             return;
4534         }
4535
4536         var o = target.calcOffsetsTo(c);
4537         
4538         var options = {
4539             target : target,
4540             value : o[1]
4541         };
4542         
4543         this.fireEvent('scrollto', this, options, e);
4544         
4545         Roo.get(c).scrollTo('top', options.value, true);
4546         
4547         return;
4548     }
4549 });
4550  
4551
4552  /*
4553  * - LGPL
4554  *
4555  * sidebar item
4556  *
4557  *  li
4558  *    <span> icon </span>
4559  *    <span> text </span>
4560  *    <span>badge </span>
4561  */
4562
4563 /**
4564  * @class Roo.bootstrap.NavSidebarItem
4565  * @extends Roo.bootstrap.NavItem
4566  * Bootstrap Navbar.NavSidebarItem class
4567  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4568  * {Boolean} open is the menu open
4569  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4570  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4571  * {String} buttonSize (sm|md|lg)the extra classes for the button
4572  * {Boolean} showArrow show arrow next to the text (default true)
4573  * @constructor
4574  * Create a new Navbar Button
4575  * @param {Object} config The config object
4576  */
4577 Roo.bootstrap.NavSidebarItem = function(config){
4578     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4579     this.addEvents({
4580         // raw events
4581         /**
4582          * @event click
4583          * The raw click event for the entire grid.
4584          * @param {Roo.EventObject} e
4585          */
4586         "click" : true,
4587          /**
4588             * @event changed
4589             * Fires when the active item active state changes
4590             * @param {Roo.bootstrap.NavSidebarItem} this
4591             * @param {boolean} state the new state
4592              
4593          */
4594         'changed': true
4595     });
4596    
4597 };
4598
4599 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4600     
4601     badgeWeight : 'default',
4602     
4603     open: false,
4604     
4605     buttonView : false,
4606     
4607     buttonWeight : 'default',
4608     
4609     buttonSize : 'md',
4610     
4611     showArrow : true,
4612     
4613     getAutoCreate : function(){
4614         
4615         
4616         var a = {
4617                 tag: 'a',
4618                 href : this.href || '#',
4619                 cls: '',
4620                 html : '',
4621                 cn : []
4622         };
4623         
4624         if(this.buttonView){
4625             a = {
4626                 tag: 'button',
4627                 href : this.href || '#',
4628                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4629                 html : this.html,
4630                 cn : []
4631             };
4632         }
4633         
4634         var cfg = {
4635             tag: 'li',
4636             cls: '',
4637             cn: [ a ]
4638         };
4639         
4640         if (this.active) {
4641             cfg.cls += ' active';
4642         }
4643         
4644         if (this.disabled) {
4645             cfg.cls += ' disabled';
4646         }
4647         if (this.open) {
4648             cfg.cls += ' open x-open';
4649         }
4650         // left icon..
4651         if (this.glyphicon || this.icon) {
4652             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4653             a.cn.push({ tag : 'i', cls : c }) ;
4654         }
4655         
4656         if(!this.buttonView){
4657             var span = {
4658                 tag: 'span',
4659                 html : this.html || ''
4660             };
4661
4662             a.cn.push(span);
4663             
4664         }
4665         
4666         if (this.badge !== '') {
4667             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4668         }
4669         
4670         if (this.menu) {
4671             
4672             if(this.showArrow){
4673                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4674             }
4675             
4676             a.cls += ' dropdown-toggle treeview' ;
4677         }
4678         
4679         return cfg;
4680     },
4681     
4682     initEvents : function()
4683     { 
4684         if (typeof (this.menu) != 'undefined') {
4685             this.menu.parentType = this.xtype;
4686             this.menu.triggerEl = this.el;
4687             this.menu = this.addxtype(Roo.apply({}, this.menu));
4688         }
4689         
4690         this.el.on('click', this.onClick, this);
4691         
4692         if(this.badge !== ''){
4693             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4694         }
4695         
4696     },
4697     
4698     onClick : function(e)
4699     {
4700         if(this.disabled){
4701             e.preventDefault();
4702             return;
4703         }
4704         
4705         if(this.preventDefault){
4706             e.preventDefault();
4707         }
4708         
4709         this.fireEvent('click', this);
4710     },
4711     
4712     disable : function()
4713     {
4714         this.setDisabled(true);
4715     },
4716     
4717     enable : function()
4718     {
4719         this.setDisabled(false);
4720     },
4721     
4722     setDisabled : function(state)
4723     {
4724         if(this.disabled == state){
4725             return;
4726         }
4727         
4728         this.disabled = state;
4729         
4730         if (state) {
4731             this.el.addClass('disabled');
4732             return;
4733         }
4734         
4735         this.el.removeClass('disabled');
4736         
4737         return;
4738     },
4739     
4740     setActive : function(state)
4741     {
4742         if(this.active == state){
4743             return;
4744         }
4745         
4746         this.active = state;
4747         
4748         if (state) {
4749             this.el.addClass('active');
4750             return;
4751         }
4752         
4753         this.el.removeClass('active');
4754         
4755         return;
4756     },
4757     
4758     isActive: function () 
4759     {
4760         return this.active;
4761     },
4762     
4763     setBadge : function(str)
4764     {
4765         if(!this.badgeEl){
4766             return;
4767         }
4768         
4769         this.badgeEl.dom.innerHTML = str;
4770     }
4771     
4772    
4773      
4774  
4775 });
4776  
4777
4778  /*
4779  * - LGPL
4780  *
4781  * row
4782  * 
4783  */
4784
4785 /**
4786  * @class Roo.bootstrap.Row
4787  * @extends Roo.bootstrap.Component
4788  * Bootstrap Row class (contains columns...)
4789  * 
4790  * @constructor
4791  * Create a new Row
4792  * @param {Object} config The config object
4793  */
4794
4795 Roo.bootstrap.Row = function(config){
4796     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4797 };
4798
4799 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4800     
4801     getAutoCreate : function(){
4802        return {
4803             cls: 'row clearfix'
4804        };
4805     }
4806     
4807     
4808 });
4809
4810  
4811
4812  /*
4813  * - LGPL
4814  *
4815  * element
4816  * 
4817  */
4818
4819 /**
4820  * @class Roo.bootstrap.Element
4821  * @extends Roo.bootstrap.Component
4822  * Bootstrap Element class
4823  * @cfg {String} html contents of the element
4824  * @cfg {String} tag tag of the element
4825  * @cfg {String} cls class of the element
4826  * @cfg {Boolean} preventDefault (true|false) default false
4827  * @cfg {Boolean} clickable (true|false) default false
4828  * 
4829  * @constructor
4830  * Create a new Element
4831  * @param {Object} config The config object
4832  */
4833
4834 Roo.bootstrap.Element = function(config){
4835     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4836     
4837     this.addEvents({
4838         // raw events
4839         /**
4840          * @event click
4841          * When a element is chick
4842          * @param {Roo.bootstrap.Element} this
4843          * @param {Roo.EventObject} e
4844          */
4845         "click" : true
4846     });
4847 };
4848
4849 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4850     
4851     tag: 'div',
4852     cls: '',
4853     html: '',
4854     preventDefault: false, 
4855     clickable: false,
4856     
4857     getAutoCreate : function(){
4858         
4859         var cfg = {
4860             tag: this.tag,
4861             cls: this.cls,
4862             html: this.html
4863         };
4864         
4865         return cfg;
4866     },
4867     
4868     initEvents: function() 
4869     {
4870         Roo.bootstrap.Element.superclass.initEvents.call(this);
4871         
4872         if(this.clickable){
4873             this.el.on('click', this.onClick, this);
4874         }
4875         
4876     },
4877     
4878     onClick : function(e)
4879     {
4880         if(this.preventDefault){
4881             e.preventDefault();
4882         }
4883         
4884         this.fireEvent('click', this, e);
4885     },
4886     
4887     getValue : function()
4888     {
4889         return this.el.dom.innerHTML;
4890     },
4891     
4892     setValue : function(value)
4893     {
4894         this.el.dom.innerHTML = value;
4895     }
4896    
4897 });
4898
4899  
4900
4901  /*
4902  * - LGPL
4903  *
4904  * pagination
4905  * 
4906  */
4907
4908 /**
4909  * @class Roo.bootstrap.Pagination
4910  * @extends Roo.bootstrap.Component
4911  * Bootstrap Pagination class
4912  * @cfg {String} size xs | sm | md | lg
4913  * @cfg {Boolean} inverse false | true
4914  * 
4915  * @constructor
4916  * Create a new Pagination
4917  * @param {Object} config The config object
4918  */
4919
4920 Roo.bootstrap.Pagination = function(config){
4921     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4922 };
4923
4924 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4925     
4926     cls: false,
4927     size: false,
4928     inverse: false,
4929     
4930     getAutoCreate : function(){
4931         var cfg = {
4932             tag: 'ul',
4933                 cls: 'pagination'
4934         };
4935         if (this.inverse) {
4936             cfg.cls += ' inverse';
4937         }
4938         if (this.html) {
4939             cfg.html=this.html;
4940         }
4941         if (this.cls) {
4942             cfg.cls += " " + this.cls;
4943         }
4944         return cfg;
4945     }
4946    
4947 });
4948
4949  
4950
4951  /*
4952  * - LGPL
4953  *
4954  * Pagination item
4955  * 
4956  */
4957
4958
4959 /**
4960  * @class Roo.bootstrap.PaginationItem
4961  * @extends Roo.bootstrap.Component
4962  * Bootstrap PaginationItem class
4963  * @cfg {String} html text
4964  * @cfg {String} href the link
4965  * @cfg {Boolean} preventDefault (true | false) default true
4966  * @cfg {Boolean} active (true | false) default false
4967  * @cfg {Boolean} disabled default false
4968  * 
4969  * 
4970  * @constructor
4971  * Create a new PaginationItem
4972  * @param {Object} config The config object
4973  */
4974
4975
4976 Roo.bootstrap.PaginationItem = function(config){
4977     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4978     this.addEvents({
4979         // raw events
4980         /**
4981          * @event click
4982          * The raw click event for the entire grid.
4983          * @param {Roo.EventObject} e
4984          */
4985         "click" : true
4986     });
4987 };
4988
4989 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4990     
4991     href : false,
4992     html : false,
4993     preventDefault: true,
4994     active : false,
4995     cls : false,
4996     disabled: false,
4997     
4998     getAutoCreate : function(){
4999         var cfg= {
5000             tag: 'li',
5001             cn: [
5002                 {
5003                     tag : 'a',
5004                     href : this.href ? this.href : '#',
5005                     html : this.html ? this.html : ''
5006                 }
5007             ]
5008         };
5009         
5010         if(this.cls){
5011             cfg.cls = this.cls;
5012         }
5013         
5014         if(this.disabled){
5015             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5016         }
5017         
5018         if(this.active){
5019             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5020         }
5021         
5022         return cfg;
5023     },
5024     
5025     initEvents: function() {
5026         
5027         this.el.on('click', this.onClick, this);
5028         
5029     },
5030     onClick : function(e)
5031     {
5032         Roo.log('PaginationItem on click ');
5033         if(this.preventDefault){
5034             e.preventDefault();
5035         }
5036         
5037         if(this.disabled){
5038             return;
5039         }
5040         
5041         this.fireEvent('click', this, e);
5042     }
5043    
5044 });
5045
5046  
5047
5048  /*
5049  * - LGPL
5050  *
5051  * slider
5052  * 
5053  */
5054
5055
5056 /**
5057  * @class Roo.bootstrap.Slider
5058  * @extends Roo.bootstrap.Component
5059  * Bootstrap Slider class
5060  *    
5061  * @constructor
5062  * Create a new Slider
5063  * @param {Object} config The config object
5064  */
5065
5066 Roo.bootstrap.Slider = function(config){
5067     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5068 };
5069
5070 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5071     
5072     getAutoCreate : function(){
5073         
5074         var cfg = {
5075             tag: 'div',
5076             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5077             cn: [
5078                 {
5079                     tag: 'a',
5080                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5081                 }
5082             ]
5083         };
5084         
5085         return cfg;
5086     }
5087    
5088 });
5089
5090  /*
5091  * Based on:
5092  * Ext JS Library 1.1.1
5093  * Copyright(c) 2006-2007, Ext JS, LLC.
5094  *
5095  * Originally Released Under LGPL - original licence link has changed is not relivant.
5096  *
5097  * Fork - LGPL
5098  * <script type="text/javascript">
5099  */
5100  
5101
5102 /**
5103  * @class Roo.grid.ColumnModel
5104  * @extends Roo.util.Observable
5105  * This is the default implementation of a ColumnModel used by the Grid. It defines
5106  * the columns in the grid.
5107  * <br>Usage:<br>
5108  <pre><code>
5109  var colModel = new Roo.grid.ColumnModel([
5110         {header: "Ticker", width: 60, sortable: true, locked: true},
5111         {header: "Company Name", width: 150, sortable: true},
5112         {header: "Market Cap.", width: 100, sortable: true},
5113         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5114         {header: "Employees", width: 100, sortable: true, resizable: false}
5115  ]);
5116  </code></pre>
5117  * <p>
5118  
5119  * The config options listed for this class are options which may appear in each
5120  * individual column definition.
5121  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5122  * @constructor
5123  * @param {Object} config An Array of column config objects. See this class's
5124  * config objects for details.
5125 */
5126 Roo.grid.ColumnModel = function(config){
5127         /**
5128      * The config passed into the constructor
5129      */
5130     this.config = config;
5131     this.lookup = {};
5132
5133     // if no id, create one
5134     // if the column does not have a dataIndex mapping,
5135     // map it to the order it is in the config
5136     for(var i = 0, len = config.length; i < len; i++){
5137         var c = config[i];
5138         if(typeof c.dataIndex == "undefined"){
5139             c.dataIndex = i;
5140         }
5141         if(typeof c.renderer == "string"){
5142             c.renderer = Roo.util.Format[c.renderer];
5143         }
5144         if(typeof c.id == "undefined"){
5145             c.id = Roo.id();
5146         }
5147         if(c.editor && c.editor.xtype){
5148             c.editor  = Roo.factory(c.editor, Roo.grid);
5149         }
5150         if(c.editor && c.editor.isFormField){
5151             c.editor = new Roo.grid.GridEditor(c.editor);
5152         }
5153         this.lookup[c.id] = c;
5154     }
5155
5156     /**
5157      * The width of columns which have no width specified (defaults to 100)
5158      * @type Number
5159      */
5160     this.defaultWidth = 100;
5161
5162     /**
5163      * Default sortable of columns which have no sortable specified (defaults to false)
5164      * @type Boolean
5165      */
5166     this.defaultSortable = false;
5167
5168     this.addEvents({
5169         /**
5170              * @event widthchange
5171              * Fires when the width of a column changes.
5172              * @param {ColumnModel} this
5173              * @param {Number} columnIndex The column index
5174              * @param {Number} newWidth The new width
5175              */
5176             "widthchange": true,
5177         /**
5178              * @event headerchange
5179              * Fires when the text of a header changes.
5180              * @param {ColumnModel} this
5181              * @param {Number} columnIndex The column index
5182              * @param {Number} newText The new header text
5183              */
5184             "headerchange": true,
5185         /**
5186              * @event hiddenchange
5187              * Fires when a column is hidden or "unhidden".
5188              * @param {ColumnModel} this
5189              * @param {Number} columnIndex The column index
5190              * @param {Boolean} hidden true if hidden, false otherwise
5191              */
5192             "hiddenchange": true,
5193             /**
5194          * @event columnmoved
5195          * Fires when a column is moved.
5196          * @param {ColumnModel} this
5197          * @param {Number} oldIndex
5198          * @param {Number} newIndex
5199          */
5200         "columnmoved" : true,
5201         /**
5202          * @event columlockchange
5203          * Fires when a column's locked state is changed
5204          * @param {ColumnModel} this
5205          * @param {Number} colIndex
5206          * @param {Boolean} locked true if locked
5207          */
5208         "columnlockchange" : true
5209     });
5210     Roo.grid.ColumnModel.superclass.constructor.call(this);
5211 };
5212 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5213     /**
5214      * @cfg {String} header The header text to display in the Grid view.
5215      */
5216     /**
5217      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5218      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5219      * specified, the column's index is used as an index into the Record's data Array.
5220      */
5221     /**
5222      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5223      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5224      */
5225     /**
5226      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5227      * Defaults to the value of the {@link #defaultSortable} property.
5228      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5229      */
5230     /**
5231      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5232      */
5233     /**
5234      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5235      */
5236     /**
5237      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5238      */
5239     /**
5240      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5241      */
5242     /**
5243      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5244      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5245      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5246      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5247      */
5248        /**
5249      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5250      */
5251     /**
5252      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5253      */
5254     /**
5255      * @cfg {String} cursor (Optional)
5256      */
5257     /**
5258      * @cfg {String} tooltip (Optional)
5259      */
5260     /**
5261      * @cfg {Number} xs (Optional)
5262      */
5263     /**
5264      * @cfg {Number} sm (Optional)
5265      */
5266     /**
5267      * @cfg {Number} md (Optional)
5268      */
5269     /**
5270      * @cfg {Number} lg (Optional)
5271      */
5272     /**
5273      * Returns the id of the column at the specified index.
5274      * @param {Number} index The column index
5275      * @return {String} the id
5276      */
5277     getColumnId : function(index){
5278         return this.config[index].id;
5279     },
5280
5281     /**
5282      * Returns the column for a specified id.
5283      * @param {String} id The column id
5284      * @return {Object} the column
5285      */
5286     getColumnById : function(id){
5287         return this.lookup[id];
5288     },
5289
5290     
5291     /**
5292      * Returns the column for a specified dataIndex.
5293      * @param {String} dataIndex The column dataIndex
5294      * @return {Object|Boolean} the column or false if not found
5295      */
5296     getColumnByDataIndex: function(dataIndex){
5297         var index = this.findColumnIndex(dataIndex);
5298         return index > -1 ? this.config[index] : false;
5299     },
5300     
5301     /**
5302      * Returns the index for a specified column id.
5303      * @param {String} id The column id
5304      * @return {Number} the index, or -1 if not found
5305      */
5306     getIndexById : function(id){
5307         for(var i = 0, len = this.config.length; i < len; i++){
5308             if(this.config[i].id == id){
5309                 return i;
5310             }
5311         }
5312         return -1;
5313     },
5314     
5315     /**
5316      * Returns the index for a specified column dataIndex.
5317      * @param {String} dataIndex The column dataIndex
5318      * @return {Number} the index, or -1 if not found
5319      */
5320     
5321     findColumnIndex : function(dataIndex){
5322         for(var i = 0, len = this.config.length; i < len; i++){
5323             if(this.config[i].dataIndex == dataIndex){
5324                 return i;
5325             }
5326         }
5327         return -1;
5328     },
5329     
5330     
5331     moveColumn : function(oldIndex, newIndex){
5332         var c = this.config[oldIndex];
5333         this.config.splice(oldIndex, 1);
5334         this.config.splice(newIndex, 0, c);
5335         this.dataMap = null;
5336         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5337     },
5338
5339     isLocked : function(colIndex){
5340         return this.config[colIndex].locked === true;
5341     },
5342
5343     setLocked : function(colIndex, value, suppressEvent){
5344         if(this.isLocked(colIndex) == value){
5345             return;
5346         }
5347         this.config[colIndex].locked = value;
5348         if(!suppressEvent){
5349             this.fireEvent("columnlockchange", this, colIndex, value);
5350         }
5351     },
5352
5353     getTotalLockedWidth : function(){
5354         var totalWidth = 0;
5355         for(var i = 0; i < this.config.length; i++){
5356             if(this.isLocked(i) && !this.isHidden(i)){
5357                 this.totalWidth += this.getColumnWidth(i);
5358             }
5359         }
5360         return totalWidth;
5361     },
5362
5363     getLockedCount : function(){
5364         for(var i = 0, len = this.config.length; i < len; i++){
5365             if(!this.isLocked(i)){
5366                 return i;
5367             }
5368         }
5369         
5370         return this.config.length;
5371     },
5372
5373     /**
5374      * Returns the number of columns.
5375      * @return {Number}
5376      */
5377     getColumnCount : function(visibleOnly){
5378         if(visibleOnly === true){
5379             var c = 0;
5380             for(var i = 0, len = this.config.length; i < len; i++){
5381                 if(!this.isHidden(i)){
5382                     c++;
5383                 }
5384             }
5385             return c;
5386         }
5387         return this.config.length;
5388     },
5389
5390     /**
5391      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5392      * @param {Function} fn
5393      * @param {Object} scope (optional)
5394      * @return {Array} result
5395      */
5396     getColumnsBy : function(fn, scope){
5397         var r = [];
5398         for(var i = 0, len = this.config.length; i < len; i++){
5399             var c = this.config[i];
5400             if(fn.call(scope||this, c, i) === true){
5401                 r[r.length] = c;
5402             }
5403         }
5404         return r;
5405     },
5406
5407     /**
5408      * Returns true if the specified column is sortable.
5409      * @param {Number} col The column index
5410      * @return {Boolean}
5411      */
5412     isSortable : function(col){
5413         if(typeof this.config[col].sortable == "undefined"){
5414             return this.defaultSortable;
5415         }
5416         return this.config[col].sortable;
5417     },
5418
5419     /**
5420      * Returns the rendering (formatting) function defined for the column.
5421      * @param {Number} col The column index.
5422      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5423      */
5424     getRenderer : function(col){
5425         if(!this.config[col].renderer){
5426             return Roo.grid.ColumnModel.defaultRenderer;
5427         }
5428         return this.config[col].renderer;
5429     },
5430
5431     /**
5432      * Sets the rendering (formatting) function for a column.
5433      * @param {Number} col The column index
5434      * @param {Function} fn The function to use to process the cell's raw data
5435      * to return HTML markup for the grid view. The render function is called with
5436      * the following parameters:<ul>
5437      * <li>Data value.</li>
5438      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5439      * <li>css A CSS style string to apply to the table cell.</li>
5440      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5441      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5442      * <li>Row index</li>
5443      * <li>Column index</li>
5444      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5445      */
5446     setRenderer : function(col, fn){
5447         this.config[col].renderer = fn;
5448     },
5449
5450     /**
5451      * Returns the width for the specified column.
5452      * @param {Number} col The column index
5453      * @return {Number}
5454      */
5455     getColumnWidth : function(col){
5456         return this.config[col].width * 1 || this.defaultWidth;
5457     },
5458
5459     /**
5460      * Sets the width for a column.
5461      * @param {Number} col The column index
5462      * @param {Number} width The new width
5463      */
5464     setColumnWidth : function(col, width, suppressEvent){
5465         this.config[col].width = width;
5466         this.totalWidth = null;
5467         if(!suppressEvent){
5468              this.fireEvent("widthchange", this, col, width);
5469         }
5470     },
5471
5472     /**
5473      * Returns the total width of all columns.
5474      * @param {Boolean} includeHidden True to include hidden column widths
5475      * @return {Number}
5476      */
5477     getTotalWidth : function(includeHidden){
5478         if(!this.totalWidth){
5479             this.totalWidth = 0;
5480             for(var i = 0, len = this.config.length; i < len; i++){
5481                 if(includeHidden || !this.isHidden(i)){
5482                     this.totalWidth += this.getColumnWidth(i);
5483                 }
5484             }
5485         }
5486         return this.totalWidth;
5487     },
5488
5489     /**
5490      * Returns the header for the specified column.
5491      * @param {Number} col The column index
5492      * @return {String}
5493      */
5494     getColumnHeader : function(col){
5495         return this.config[col].header;
5496     },
5497
5498     /**
5499      * Sets the header for a column.
5500      * @param {Number} col The column index
5501      * @param {String} header The new header
5502      */
5503     setColumnHeader : function(col, header){
5504         this.config[col].header = header;
5505         this.fireEvent("headerchange", this, col, header);
5506     },
5507
5508     /**
5509      * Returns the tooltip for the specified column.
5510      * @param {Number} col The column index
5511      * @return {String}
5512      */
5513     getColumnTooltip : function(col){
5514             return this.config[col].tooltip;
5515     },
5516     /**
5517      * Sets the tooltip for a column.
5518      * @param {Number} col The column index
5519      * @param {String} tooltip The new tooltip
5520      */
5521     setColumnTooltip : function(col, tooltip){
5522             this.config[col].tooltip = tooltip;
5523     },
5524
5525     /**
5526      * Returns the dataIndex for the specified column.
5527      * @param {Number} col The column index
5528      * @return {Number}
5529      */
5530     getDataIndex : function(col){
5531         return this.config[col].dataIndex;
5532     },
5533
5534     /**
5535      * Sets the dataIndex for a column.
5536      * @param {Number} col The column index
5537      * @param {Number} dataIndex The new dataIndex
5538      */
5539     setDataIndex : function(col, dataIndex){
5540         this.config[col].dataIndex = dataIndex;
5541     },
5542
5543     
5544     
5545     /**
5546      * Returns true if the cell is editable.
5547      * @param {Number} colIndex The column index
5548      * @param {Number} rowIndex The row index - this is nto actually used..?
5549      * @return {Boolean}
5550      */
5551     isCellEditable : function(colIndex, rowIndex){
5552         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5553     },
5554
5555     /**
5556      * Returns the editor defined for the cell/column.
5557      * return false or null to disable editing.
5558      * @param {Number} colIndex The column index
5559      * @param {Number} rowIndex The row index
5560      * @return {Object}
5561      */
5562     getCellEditor : function(colIndex, rowIndex){
5563         return this.config[colIndex].editor;
5564     },
5565
5566     /**
5567      * Sets if a column is editable.
5568      * @param {Number} col The column index
5569      * @param {Boolean} editable True if the column is editable
5570      */
5571     setEditable : function(col, editable){
5572         this.config[col].editable = editable;
5573     },
5574
5575
5576     /**
5577      * Returns true if the column is hidden.
5578      * @param {Number} colIndex The column index
5579      * @return {Boolean}
5580      */
5581     isHidden : function(colIndex){
5582         return this.config[colIndex].hidden;
5583     },
5584
5585
5586     /**
5587      * Returns true if the column width cannot be changed
5588      */
5589     isFixed : function(colIndex){
5590         return this.config[colIndex].fixed;
5591     },
5592
5593     /**
5594      * Returns true if the column can be resized
5595      * @return {Boolean}
5596      */
5597     isResizable : function(colIndex){
5598         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5599     },
5600     /**
5601      * Sets if a column is hidden.
5602      * @param {Number} colIndex The column index
5603      * @param {Boolean} hidden True if the column is hidden
5604      */
5605     setHidden : function(colIndex, hidden){
5606         this.config[colIndex].hidden = hidden;
5607         this.totalWidth = null;
5608         this.fireEvent("hiddenchange", this, colIndex, hidden);
5609     },
5610
5611     /**
5612      * Sets the editor for a column.
5613      * @param {Number} col The column index
5614      * @param {Object} editor The editor object
5615      */
5616     setEditor : function(col, editor){
5617         this.config[col].editor = editor;
5618     }
5619 });
5620
5621 Roo.grid.ColumnModel.defaultRenderer = function(value)
5622 {
5623     if(typeof value == "object") {
5624         return value;
5625     }
5626         if(typeof value == "string" && value.length < 1){
5627             return "&#160;";
5628         }
5629     
5630         return String.format("{0}", value);
5631 };
5632
5633 // Alias for backwards compatibility
5634 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5635 /*
5636  * Based on:
5637  * Ext JS Library 1.1.1
5638  * Copyright(c) 2006-2007, Ext JS, LLC.
5639  *
5640  * Originally Released Under LGPL - original licence link has changed is not relivant.
5641  *
5642  * Fork - LGPL
5643  * <script type="text/javascript">
5644  */
5645  
5646 /**
5647  * @class Roo.LoadMask
5648  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5649  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5650  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5651  * element's UpdateManager load indicator and will be destroyed after the initial load.
5652  * @constructor
5653  * Create a new LoadMask
5654  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5655  * @param {Object} config The config object
5656  */
5657 Roo.LoadMask = function(el, config){
5658     this.el = Roo.get(el);
5659     Roo.apply(this, config);
5660     if(this.store){
5661         this.store.on('beforeload', this.onBeforeLoad, this);
5662         this.store.on('load', this.onLoad, this);
5663         this.store.on('loadexception', this.onLoadException, this);
5664         this.removeMask = false;
5665     }else{
5666         var um = this.el.getUpdateManager();
5667         um.showLoadIndicator = false; // disable the default indicator
5668         um.on('beforeupdate', this.onBeforeLoad, this);
5669         um.on('update', this.onLoad, this);
5670         um.on('failure', this.onLoad, this);
5671         this.removeMask = true;
5672     }
5673 };
5674
5675 Roo.LoadMask.prototype = {
5676     /**
5677      * @cfg {Boolean} removeMask
5678      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5679      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5680      */
5681     /**
5682      * @cfg {String} msg
5683      * The text to display in a centered loading message box (defaults to 'Loading...')
5684      */
5685     msg : 'Loading...',
5686     /**
5687      * @cfg {String} msgCls
5688      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5689      */
5690     msgCls : 'x-mask-loading',
5691
5692     /**
5693      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5694      * @type Boolean
5695      */
5696     disabled: false,
5697
5698     /**
5699      * Disables the mask to prevent it from being displayed
5700      */
5701     disable : function(){
5702        this.disabled = true;
5703     },
5704
5705     /**
5706      * Enables the mask so that it can be displayed
5707      */
5708     enable : function(){
5709         this.disabled = false;
5710     },
5711     
5712     onLoadException : function()
5713     {
5714         Roo.log(arguments);
5715         
5716         if (typeof(arguments[3]) != 'undefined') {
5717             Roo.MessageBox.alert("Error loading",arguments[3]);
5718         } 
5719         /*
5720         try {
5721             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5722                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5723             }   
5724         } catch(e) {
5725             
5726         }
5727         */
5728     
5729         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5730     },
5731     // private
5732     onLoad : function()
5733     {
5734         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5735     },
5736
5737     // private
5738     onBeforeLoad : function(){
5739         if(!this.disabled){
5740             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5741         }
5742     },
5743
5744     // private
5745     destroy : function(){
5746         if(this.store){
5747             this.store.un('beforeload', this.onBeforeLoad, this);
5748             this.store.un('load', this.onLoad, this);
5749             this.store.un('loadexception', this.onLoadException, this);
5750         }else{
5751             var um = this.el.getUpdateManager();
5752             um.un('beforeupdate', this.onBeforeLoad, this);
5753             um.un('update', this.onLoad, this);
5754             um.un('failure', this.onLoad, this);
5755         }
5756     }
5757 };/*
5758  * - LGPL
5759  *
5760  * table
5761  * 
5762  */
5763
5764 /**
5765  * @class Roo.bootstrap.Table
5766  * @extends Roo.bootstrap.Component
5767  * Bootstrap Table class
5768  * @cfg {String} cls table class
5769  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5770  * @cfg {String} bgcolor Specifies the background color for a table
5771  * @cfg {Number} border Specifies whether the table cells should have borders or not
5772  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5773  * @cfg {Number} cellspacing Specifies the space between cells
5774  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5775  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5776  * @cfg {String} sortable Specifies that the table should be sortable
5777  * @cfg {String} summary Specifies a summary of the content of a table
5778  * @cfg {Number} width Specifies the width of a table
5779  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5780  * 
5781  * @cfg {boolean} striped Should the rows be alternative striped
5782  * @cfg {boolean} bordered Add borders to the table
5783  * @cfg {boolean} hover Add hover highlighting
5784  * @cfg {boolean} condensed Format condensed
5785  * @cfg {boolean} responsive Format condensed
5786  * @cfg {Boolean} loadMask (true|false) default false
5787  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5788  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5789  * @cfg {Boolean} rowSelection (true|false) default false
5790  * @cfg {Boolean} cellSelection (true|false) default false
5791  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5792  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5793  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5794  
5795  * 
5796  * @constructor
5797  * Create a new Table
5798  * @param {Object} config The config object
5799  */
5800
5801 Roo.bootstrap.Table = function(config){
5802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5803     
5804   
5805     
5806     // BC...
5807     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5808     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5809     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5810     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5811     
5812     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5813     if (this.sm) {
5814         this.sm.grid = this;
5815         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5816         this.sm = this.selModel;
5817         this.sm.xmodule = this.xmodule || false;
5818     }
5819     
5820     if (this.cm && typeof(this.cm.config) == 'undefined') {
5821         this.colModel = new Roo.grid.ColumnModel(this.cm);
5822         this.cm = this.colModel;
5823         this.cm.xmodule = this.xmodule || false;
5824     }
5825     if (this.store) {
5826         this.store= Roo.factory(this.store, Roo.data);
5827         this.ds = this.store;
5828         this.ds.xmodule = this.xmodule || false;
5829          
5830     }
5831     if (this.footer && this.store) {
5832         this.footer.dataSource = this.ds;
5833         this.footer = Roo.factory(this.footer);
5834     }
5835     
5836     /** @private */
5837     this.addEvents({
5838         /**
5839          * @event cellclick
5840          * Fires when a cell is clicked
5841          * @param {Roo.bootstrap.Table} this
5842          * @param {Roo.Element} el
5843          * @param {Number} rowIndex
5844          * @param {Number} columnIndex
5845          * @param {Roo.EventObject} e
5846          */
5847         "cellclick" : true,
5848         /**
5849          * @event celldblclick
5850          * Fires when a cell is double clicked
5851          * @param {Roo.bootstrap.Table} this
5852          * @param {Roo.Element} el
5853          * @param {Number} rowIndex
5854          * @param {Number} columnIndex
5855          * @param {Roo.EventObject} e
5856          */
5857         "celldblclick" : true,
5858         /**
5859          * @event rowclick
5860          * Fires when a row is clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Roo.Element} el
5863          * @param {Number} rowIndex
5864          * @param {Roo.EventObject} e
5865          */
5866         "rowclick" : true,
5867         /**
5868          * @event rowdblclick
5869          * Fires when a row is double clicked
5870          * @param {Roo.bootstrap.Table} this
5871          * @param {Roo.Element} el
5872          * @param {Number} rowIndex
5873          * @param {Roo.EventObject} e
5874          */
5875         "rowdblclick" : true,
5876         /**
5877          * @event mouseover
5878          * Fires when a mouseover occur
5879          * @param {Roo.bootstrap.Table} this
5880          * @param {Roo.Element} el
5881          * @param {Number} rowIndex
5882          * @param {Number} columnIndex
5883          * @param {Roo.EventObject} e
5884          */
5885         "mouseover" : true,
5886         /**
5887          * @event mouseout
5888          * Fires when a mouseout occur
5889          * @param {Roo.bootstrap.Table} this
5890          * @param {Roo.Element} el
5891          * @param {Number} rowIndex
5892          * @param {Number} columnIndex
5893          * @param {Roo.EventObject} e
5894          */
5895         "mouseout" : true,
5896         /**
5897          * @event rowclass
5898          * Fires when a row is rendered, so you can change add a style to it.
5899          * @param {Roo.bootstrap.Table} this
5900          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5901          */
5902         'rowclass' : true,
5903           /**
5904          * @event rowsrendered
5905          * Fires when all the  rows have been rendered
5906          * @param {Roo.bootstrap.Table} this
5907          */
5908         'rowsrendered' : true,
5909         /**
5910          * @event contextmenu
5911          * The raw contextmenu event for the entire grid.
5912          * @param {Roo.EventObject} e
5913          */
5914         "contextmenu" : true,
5915         /**
5916          * @event rowcontextmenu
5917          * Fires when a row is right clicked
5918          * @param {Roo.bootstrap.Table} this
5919          * @param {Number} rowIndex
5920          * @param {Roo.EventObject} e
5921          */
5922         "rowcontextmenu" : true,
5923         /**
5924          * @event cellcontextmenu
5925          * Fires when a cell is right clicked
5926          * @param {Roo.bootstrap.Table} this
5927          * @param {Number} rowIndex
5928          * @param {Number} cellIndex
5929          * @param {Roo.EventObject} e
5930          */
5931          "cellcontextmenu" : true,
5932          /**
5933          * @event headercontextmenu
5934          * Fires when a header is right clicked
5935          * @param {Roo.bootstrap.Table} this
5936          * @param {Number} columnIndex
5937          * @param {Roo.EventObject} e
5938          */
5939         "headercontextmenu" : true
5940     });
5941 };
5942
5943 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5944     
5945     cls: false,
5946     align: false,
5947     bgcolor: false,
5948     border: false,
5949     cellpadding: false,
5950     cellspacing: false,
5951     frame: false,
5952     rules: false,
5953     sortable: false,
5954     summary: false,
5955     width: false,
5956     striped : false,
5957     scrollBody : false,
5958     bordered: false,
5959     hover:  false,
5960     condensed : false,
5961     responsive : false,
5962     sm : false,
5963     cm : false,
5964     store : false,
5965     loadMask : false,
5966     footerShow : true,
5967     headerShow : true,
5968   
5969     rowSelection : false,
5970     cellSelection : false,
5971     layout : false,
5972     
5973     // Roo.Element - the tbody
5974     mainBody: false,
5975     // Roo.Element - thead element
5976     mainHead: false,
5977     
5978     container: false, // used by gridpanel...
5979     
5980     lazyLoad : false,
5981     
5982     getAutoCreate : function()
5983     {
5984         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5985         
5986         cfg = {
5987             tag: 'table',
5988             cls : 'table',
5989             cn : []
5990         };
5991         if (this.scrollBody) {
5992             cfg.cls += ' table-body-fixed';
5993         }    
5994         if (this.striped) {
5995             cfg.cls += ' table-striped';
5996         }
5997         
5998         if (this.hover) {
5999             cfg.cls += ' table-hover';
6000         }
6001         if (this.bordered) {
6002             cfg.cls += ' table-bordered';
6003         }
6004         if (this.condensed) {
6005             cfg.cls += ' table-condensed';
6006         }
6007         if (this.responsive) {
6008             cfg.cls += ' table-responsive';
6009         }
6010         
6011         if (this.cls) {
6012             cfg.cls+=  ' ' +this.cls;
6013         }
6014         
6015         // this lot should be simplifed...
6016         
6017         if (this.align) {
6018             cfg.align=this.align;
6019         }
6020         if (this.bgcolor) {
6021             cfg.bgcolor=this.bgcolor;
6022         }
6023         if (this.border) {
6024             cfg.border=this.border;
6025         }
6026         if (this.cellpadding) {
6027             cfg.cellpadding=this.cellpadding;
6028         }
6029         if (this.cellspacing) {
6030             cfg.cellspacing=this.cellspacing;
6031         }
6032         if (this.frame) {
6033             cfg.frame=this.frame;
6034         }
6035         if (this.rules) {
6036             cfg.rules=this.rules;
6037         }
6038         if (this.sortable) {
6039             cfg.sortable=this.sortable;
6040         }
6041         if (this.summary) {
6042             cfg.summary=this.summary;
6043         }
6044         if (this.width) {
6045             cfg.width=this.width;
6046         }
6047         if (this.layout) {
6048             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6049         }
6050         
6051         if(this.store || this.cm){
6052             if(this.headerShow){
6053                 cfg.cn.push(this.renderHeader());
6054             }
6055             
6056             cfg.cn.push(this.renderBody());
6057             
6058             if(this.footerShow){
6059                 cfg.cn.push(this.renderFooter());
6060             }
6061             // where does this come from?
6062             //cfg.cls+=  ' TableGrid';
6063         }
6064         
6065         return { cn : [ cfg ] };
6066     },
6067     
6068     initEvents : function()
6069     {   
6070         if(!this.store || !this.cm){
6071             return;
6072         }
6073         if (this.selModel) {
6074             this.selModel.initEvents();
6075         }
6076         
6077         
6078         //Roo.log('initEvents with ds!!!!');
6079         
6080         this.mainBody = this.el.select('tbody', true).first();
6081         this.mainHead = this.el.select('thead', true).first();
6082         
6083         
6084         
6085         
6086         var _this = this;
6087         
6088         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6089             e.on('click', _this.sort, _this);
6090         });
6091         
6092         this.mainBody.on("click", this.onClick, this);
6093         this.mainBody.on("dblclick", this.onDblClick, this);
6094         
6095         // why is this done????? = it breaks dialogs??
6096         //this.parent().el.setStyle('position', 'relative');
6097         
6098         
6099         if (this.footer) {
6100             this.footer.parentId = this.id;
6101             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6102             
6103             if(this.lazyLoad){
6104                 this.el.select('tfoot tr td').first().addClass('hide');
6105             }
6106         } 
6107         
6108         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6109         
6110         this.store.on('load', this.onLoad, this);
6111         this.store.on('beforeload', this.onBeforeLoad, this);
6112         this.store.on('update', this.onUpdate, this);
6113         this.store.on('add', this.onAdd, this);
6114         this.store.on("clear", this.clear, this);
6115         
6116         this.el.on("contextmenu", this.onContextMenu, this);
6117         
6118         this.mainBody.on('scroll', this.onBodyScroll, this);
6119         
6120         
6121     },
6122     
6123     onContextMenu : function(e, t)
6124     {
6125         this.processEvent("contextmenu", e);
6126     },
6127     
6128     processEvent : function(name, e)
6129     {
6130         if (name != 'touchstart' ) {
6131             this.fireEvent(name, e);    
6132         }
6133         
6134         var t = e.getTarget();
6135         
6136         var cell = Roo.get(t);
6137         
6138         if(!cell){
6139             return;
6140         }
6141         
6142         if(cell.findParent('tfoot', false, true)){
6143             return;
6144         }
6145         
6146         if(cell.findParent('thead', false, true)){
6147             
6148             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6149                 cell = Roo.get(t).findParent('th', false, true);
6150                 if (!cell) {
6151                     Roo.log("failed to find th in thead?");
6152                     Roo.log(e.getTarget());
6153                     return;
6154                 }
6155             }
6156             
6157             var cellIndex = cell.dom.cellIndex;
6158             
6159             var ename = name == 'touchstart' ? 'click' : name;
6160             this.fireEvent("header" + ename, this, cellIndex, e);
6161             
6162             return;
6163         }
6164         
6165         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6166             cell = Roo.get(t).findParent('td', false, true);
6167             if (!cell) {
6168                 Roo.log("failed to find th in tbody?");
6169                 Roo.log(e.getTarget());
6170                 return;
6171             }
6172         }
6173         
6174         var row = cell.findParent('tr', false, true);
6175         var cellIndex = cell.dom.cellIndex;
6176         var rowIndex = row.dom.rowIndex - 1;
6177         
6178         if(row !== false){
6179             
6180             this.fireEvent("row" + name, this, rowIndex, e);
6181             
6182             if(cell !== false){
6183             
6184                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6185             }
6186         }
6187         
6188     },
6189     
6190     onMouseover : function(e, el)
6191     {
6192         var cell = Roo.get(el);
6193         
6194         if(!cell){
6195             return;
6196         }
6197         
6198         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6199             cell = cell.findParent('td', false, true);
6200         }
6201         
6202         var row = cell.findParent('tr', false, true);
6203         var cellIndex = cell.dom.cellIndex;
6204         var rowIndex = row.dom.rowIndex - 1; // start from 0
6205         
6206         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6207         
6208     },
6209     
6210     onMouseout : function(e, el)
6211     {
6212         var cell = Roo.get(el);
6213         
6214         if(!cell){
6215             return;
6216         }
6217         
6218         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6219             cell = cell.findParent('td', false, true);
6220         }
6221         
6222         var row = cell.findParent('tr', false, true);
6223         var cellIndex = cell.dom.cellIndex;
6224         var rowIndex = row.dom.rowIndex - 1; // start from 0
6225         
6226         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6227         
6228     },
6229     
6230     onClick : function(e, el)
6231     {
6232         var cell = Roo.get(el);
6233         
6234         if(!cell || (!this.cellSelection && !this.rowSelection)){
6235             return;
6236         }
6237         
6238         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6239             cell = cell.findParent('td', false, true);
6240         }
6241         
6242         if(!cell || typeof(cell) == 'undefined'){
6243             return;
6244         }
6245         
6246         var row = cell.findParent('tr', false, true);
6247         
6248         if(!row || typeof(row) == 'undefined'){
6249             return;
6250         }
6251         
6252         var cellIndex = cell.dom.cellIndex;
6253         var rowIndex = this.getRowIndex(row);
6254         
6255         // why??? - should these not be based on SelectionModel?
6256         if(this.cellSelection){
6257             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6258         }
6259         
6260         if(this.rowSelection){
6261             this.fireEvent('rowclick', this, row, rowIndex, e);
6262         }
6263         
6264         
6265     },
6266         
6267     onDblClick : function(e,el)
6268     {
6269         var cell = Roo.get(el);
6270         
6271         if(!cell || (!this.cellSelection && !this.rowSelection)){
6272             return;
6273         }
6274         
6275         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6276             cell = cell.findParent('td', false, true);
6277         }
6278         
6279         if(!cell || typeof(cell) == 'undefined'){
6280             return;
6281         }
6282         
6283         var row = cell.findParent('tr', false, true);
6284         
6285         if(!row || typeof(row) == 'undefined'){
6286             return;
6287         }
6288         
6289         var cellIndex = cell.dom.cellIndex;
6290         var rowIndex = this.getRowIndex(row);
6291         
6292         if(this.cellSelection){
6293             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6294         }
6295         
6296         if(this.rowSelection){
6297             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6298         }
6299     },
6300     
6301     sort : function(e,el)
6302     {
6303         var col = Roo.get(el);
6304         
6305         if(!col.hasClass('sortable')){
6306             return;
6307         }
6308         
6309         var sort = col.attr('sort');
6310         var dir = 'ASC';
6311         
6312         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6313             dir = 'DESC';
6314         }
6315         
6316         this.store.sortInfo = {field : sort, direction : dir};
6317         
6318         if (this.footer) {
6319             Roo.log("calling footer first");
6320             this.footer.onClick('first');
6321         } else {
6322         
6323             this.store.load({ params : { start : 0 } });
6324         }
6325     },
6326     
6327     renderHeader : function()
6328     {
6329         var header = {
6330             tag: 'thead',
6331             cn : []
6332         };
6333         
6334         var cm = this.cm;
6335         this.totalWidth = 0;
6336         
6337         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6338             
6339             var config = cm.config[i];
6340             
6341             var c = {
6342                 tag: 'th',
6343                 style : '',
6344                 html: cm.getColumnHeader(i)
6345             };
6346             
6347             var hh = '';
6348             
6349             if(typeof(config.sortable) != 'undefined' && config.sortable){
6350                 c.cls = 'sortable';
6351                 c.html = '<i class="glyphicon"></i>' + c.html;
6352             }
6353             
6354             if(typeof(config.lgHeader) != 'undefined'){
6355                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6356             }
6357             
6358             if(typeof(config.mdHeader) != 'undefined'){
6359                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6360             }
6361             
6362             if(typeof(config.smHeader) != 'undefined'){
6363                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6364             }
6365             
6366             if(typeof(config.xsHeader) != 'undefined'){
6367                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6368             }
6369             
6370             if(hh.length){
6371                 c.html = hh;
6372             }
6373             
6374             if(typeof(config.tooltip) != 'undefined'){
6375                 c.tooltip = config.tooltip;
6376             }
6377             
6378             if(typeof(config.colspan) != 'undefined'){
6379                 c.colspan = config.colspan;
6380             }
6381             
6382             if(typeof(config.hidden) != 'undefined' && config.hidden){
6383                 c.style += ' display:none;';
6384             }
6385             
6386             if(typeof(config.dataIndex) != 'undefined'){
6387                 c.sort = config.dataIndex;
6388             }
6389             
6390            
6391             
6392             if(typeof(config.align) != 'undefined' && config.align.length){
6393                 c.style += ' text-align:' + config.align + ';';
6394             }
6395             
6396             if(typeof(config.width) != 'undefined'){
6397                 c.style += ' width:' + config.width + 'px;';
6398                 this.totalWidth += config.width;
6399             } else {
6400                 this.totalWidth += 100; // assume minimum of 100 per column?
6401             }
6402             
6403             if(typeof(config.cls) != 'undefined'){
6404                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6405             }
6406             
6407             ['xs','sm','md','lg'].map(function(size){
6408                 
6409                 if(typeof(config[size]) == 'undefined'){
6410                     return;
6411                 }
6412                 
6413                 if (!config[size]) { // 0 = hidden
6414                     c.cls += ' hidden-' + size;
6415                     return;
6416                 }
6417                 
6418                 c.cls += ' col-' + size + '-' + config[size];
6419
6420             });
6421             
6422             header.cn.push(c)
6423         }
6424         
6425         return header;
6426     },
6427     
6428     renderBody : function()
6429     {
6430         var body = {
6431             tag: 'tbody',
6432             cn : [
6433                 {
6434                     tag: 'tr',
6435                     cn : [
6436                         {
6437                             tag : 'td',
6438                             colspan :  this.cm.getColumnCount()
6439                         }
6440                     ]
6441                 }
6442             ]
6443         };
6444         
6445         return body;
6446     },
6447     
6448     renderFooter : function()
6449     {
6450         var footer = {
6451             tag: 'tfoot',
6452             cn : [
6453                 {
6454                     tag: 'tr',
6455                     cn : [
6456                         {
6457                             tag : 'td',
6458                             colspan :  this.cm.getColumnCount()
6459                         }
6460                     ]
6461                 }
6462             ]
6463         };
6464         
6465         return footer;
6466     },
6467     
6468     
6469     
6470     onLoad : function()
6471     {
6472 //        Roo.log('ds onload');
6473         this.clear();
6474         
6475         var _this = this;
6476         var cm = this.cm;
6477         var ds = this.store;
6478         
6479         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6480             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6481             if (_this.store.sortInfo) {
6482                     
6483                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6484                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6485                 }
6486                 
6487                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6488                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6489                 }
6490             }
6491         });
6492         
6493         var tbody =  this.mainBody;
6494               
6495         if(ds.getCount() > 0){
6496             ds.data.each(function(d,rowIndex){
6497                 var row =  this.renderRow(cm, ds, rowIndex);
6498                 
6499                 tbody.createChild(row);
6500                 
6501                 var _this = this;
6502                 
6503                 if(row.cellObjects.length){
6504                     Roo.each(row.cellObjects, function(r){
6505                         _this.renderCellObject(r);
6506                     })
6507                 }
6508                 
6509             }, this);
6510         }
6511         
6512         Roo.each(this.el.select('tbody td', true).elements, function(e){
6513             e.on('mouseover', _this.onMouseover, _this);
6514         });
6515         
6516         Roo.each(this.el.select('tbody td', true).elements, function(e){
6517             e.on('mouseout', _this.onMouseout, _this);
6518         });
6519         this.fireEvent('rowsrendered', this);
6520         //if(this.loadMask){
6521         //    this.maskEl.hide();
6522         //}
6523         
6524         this.autoSize();
6525     },
6526     
6527     
6528     onUpdate : function(ds,record)
6529     {
6530         this.refreshRow(record);
6531         this.autoSize();
6532     },
6533     
6534     onRemove : function(ds, record, index, isUpdate){
6535         if(isUpdate !== true){
6536             this.fireEvent("beforerowremoved", this, index, record);
6537         }
6538         var bt = this.mainBody.dom;
6539         
6540         var rows = this.el.select('tbody > tr', true).elements;
6541         
6542         if(typeof(rows[index]) != 'undefined'){
6543             bt.removeChild(rows[index].dom);
6544         }
6545         
6546 //        if(bt.rows[index]){
6547 //            bt.removeChild(bt.rows[index]);
6548 //        }
6549         
6550         if(isUpdate !== true){
6551             //this.stripeRows(index);
6552             //this.syncRowHeights(index, index);
6553             //this.layout();
6554             this.fireEvent("rowremoved", this, index, record);
6555         }
6556     },
6557     
6558     onAdd : function(ds, records, rowIndex)
6559     {
6560         //Roo.log('on Add called');
6561         // - note this does not handle multiple adding very well..
6562         var bt = this.mainBody.dom;
6563         for (var i =0 ; i < records.length;i++) {
6564             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6565             //Roo.log(records[i]);
6566             //Roo.log(this.store.getAt(rowIndex+i));
6567             this.insertRow(this.store, rowIndex + i, false);
6568             return;
6569         }
6570         
6571     },
6572     
6573     
6574     refreshRow : function(record){
6575         var ds = this.store, index;
6576         if(typeof record == 'number'){
6577             index = record;
6578             record = ds.getAt(index);
6579         }else{
6580             index = ds.indexOf(record);
6581         }
6582         this.insertRow(ds, index, true);
6583         this.autoSize();
6584         this.onRemove(ds, record, index+1, true);
6585         this.autoSize();
6586         //this.syncRowHeights(index, index);
6587         //this.layout();
6588         this.fireEvent("rowupdated", this, index, record);
6589     },
6590     
6591     insertRow : function(dm, rowIndex, isUpdate){
6592         
6593         if(!isUpdate){
6594             this.fireEvent("beforerowsinserted", this, rowIndex);
6595         }
6596             //var s = this.getScrollState();
6597         var row = this.renderRow(this.cm, this.store, rowIndex);
6598         // insert before rowIndex..
6599         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6600         
6601         var _this = this;
6602                 
6603         if(row.cellObjects.length){
6604             Roo.each(row.cellObjects, function(r){
6605                 _this.renderCellObject(r);
6606             })
6607         }
6608             
6609         if(!isUpdate){
6610             this.fireEvent("rowsinserted", this, rowIndex);
6611             //this.syncRowHeights(firstRow, lastRow);
6612             //this.stripeRows(firstRow);
6613             //this.layout();
6614         }
6615         
6616     },
6617     
6618     
6619     getRowDom : function(rowIndex)
6620     {
6621         var rows = this.el.select('tbody > tr', true).elements;
6622         
6623         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6624         
6625     },
6626     // returns the object tree for a tr..
6627   
6628     
6629     renderRow : function(cm, ds, rowIndex) 
6630     {
6631         
6632         var d = ds.getAt(rowIndex);
6633         
6634         var row = {
6635             tag : 'tr',
6636             cn : []
6637         };
6638             
6639         var cellObjects = [];
6640         
6641         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6642             var config = cm.config[i];
6643             
6644             var renderer = cm.getRenderer(i);
6645             var value = '';
6646             var id = false;
6647             
6648             if(typeof(renderer) !== 'undefined'){
6649                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6650             }
6651             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6652             // and are rendered into the cells after the row is rendered - using the id for the element.
6653             
6654             if(typeof(value) === 'object'){
6655                 id = Roo.id();
6656                 cellObjects.push({
6657                     container : id,
6658                     cfg : value 
6659                 })
6660             }
6661             
6662             var rowcfg = {
6663                 record: d,
6664                 rowIndex : rowIndex,
6665                 colIndex : i,
6666                 rowClass : ''
6667             };
6668
6669             this.fireEvent('rowclass', this, rowcfg);
6670             
6671             var td = {
6672                 tag: 'td',
6673                 cls : rowcfg.rowClass,
6674                 style: '',
6675                 html: (typeof(value) === 'object') ? '' : value
6676             };
6677             
6678             if (id) {
6679                 td.id = id;
6680             }
6681             
6682             if(typeof(config.colspan) != 'undefined'){
6683                 td.colspan = config.colspan;
6684             }
6685             
6686             if(typeof(config.hidden) != 'undefined' && config.hidden){
6687                 td.style += ' display:none;';
6688             }
6689             
6690             if(typeof(config.align) != 'undefined' && config.align.length){
6691                 td.style += ' text-align:' + config.align + ';';
6692             }
6693             
6694             if(typeof(config.width) != 'undefined'){
6695                 td.style += ' width:' +  config.width + 'px;';
6696             }
6697             
6698             if(typeof(config.cursor) != 'undefined'){
6699                 td.style += ' cursor:' +  config.cursor + ';';
6700             }
6701             
6702             if(typeof(config.cls) != 'undefined'){
6703                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6704             }
6705             
6706             ['xs','sm','md','lg'].map(function(size){
6707                 
6708                 if(typeof(config[size]) == 'undefined'){
6709                     return;
6710                 }
6711                 
6712                 if (!config[size]) { // 0 = hidden
6713                     td.cls += ' hidden-' + size;
6714                     return;
6715                 }
6716                 
6717                 td.cls += ' col-' + size + '-' + config[size];
6718
6719             });
6720              
6721             row.cn.push(td);
6722            
6723         }
6724         
6725         row.cellObjects = cellObjects;
6726         
6727         return row;
6728           
6729     },
6730     
6731     
6732     
6733     onBeforeLoad : function()
6734     {
6735         //Roo.log('ds onBeforeLoad');
6736         
6737         //this.clear();
6738         
6739         //if(this.loadMask){
6740         //    this.maskEl.show();
6741         //}
6742     },
6743      /**
6744      * Remove all rows
6745      */
6746     clear : function()
6747     {
6748         this.el.select('tbody', true).first().dom.innerHTML = '';
6749     },
6750     /**
6751      * Show or hide a row.
6752      * @param {Number} rowIndex to show or hide
6753      * @param {Boolean} state hide
6754      */
6755     setRowVisibility : function(rowIndex, state)
6756     {
6757         var bt = this.mainBody.dom;
6758         
6759         var rows = this.el.select('tbody > tr', true).elements;
6760         
6761         if(typeof(rows[rowIndex]) == 'undefined'){
6762             return;
6763         }
6764         rows[rowIndex].dom.style.display = state ? '' : 'none';
6765     },
6766     
6767     
6768     getSelectionModel : function(){
6769         if(!this.selModel){
6770             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6771         }
6772         return this.selModel;
6773     },
6774     /*
6775      * Render the Roo.bootstrap object from renderder
6776      */
6777     renderCellObject : function(r)
6778     {
6779         var _this = this;
6780         
6781         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6782         
6783         var t = r.cfg.render(r.container);
6784         
6785         if(r.cfg.cn){
6786             Roo.each(r.cfg.cn, function(c){
6787                 var child = {
6788                     container: t.getChildContainer(),
6789                     cfg: c
6790                 };
6791                 _this.renderCellObject(child);
6792             })
6793         }
6794     },
6795     
6796     getRowIndex : function(row)
6797     {
6798         var rowIndex = -1;
6799         
6800         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6801             if(el != row){
6802                 return;
6803             }
6804             
6805             rowIndex = index;
6806         });
6807         
6808         return rowIndex;
6809     },
6810      /**
6811      * Returns the grid's underlying element = used by panel.Grid
6812      * @return {Element} The element
6813      */
6814     getGridEl : function(){
6815         return this.el;
6816     },
6817      /**
6818      * Forces a resize - used by panel.Grid
6819      * @return {Element} The element
6820      */
6821     autoSize : function()
6822     {
6823         //var ctr = Roo.get(this.container.dom.parentElement);
6824         var ctr = Roo.get(this.el.dom);
6825         
6826         var thd = this.getGridEl().select('thead',true).first();
6827         var tbd = this.getGridEl().select('tbody', true).first();
6828         var tfd = this.getGridEl().select('tfoot', true).first();
6829         
6830         var cw = ctr.getWidth();
6831         
6832         if (tbd) {
6833             
6834             tbd.setSize(ctr.getWidth(),
6835                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6836             );
6837             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6838             cw -= barsize;
6839         }
6840         cw = Math.max(cw, this.totalWidth);
6841         this.getGridEl().select('tr',true).setWidth(cw);
6842         // resize 'expandable coloumn?
6843         
6844         return; // we doe not have a view in this design..
6845         
6846     },
6847     onBodyScroll: function()
6848     {
6849         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6850         this.mainHead.setStyle({
6851             'position' : 'relative',
6852             'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6853         });
6854         
6855         if(this.lazyLoad){
6856             
6857             var scrollHeight = this.mainBody.dom.scrollHeight;
6858             
6859             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6860             
6861             var height = this.mainBody.getHeight();
6862             
6863             if(scrollHeight - height == scrollTop) {
6864                 
6865                 var total = this.ds.getTotalCount();
6866                 
6867                 if(this.footer.cursor + this.footer.pageSize < total){
6868                     
6869                     this.footer.ds.load({
6870                         params : {
6871                             start : this.footer.cursor + this.footer.pageSize,
6872                             limit : this.footer.pageSize
6873                         },
6874                         add : true
6875                     });
6876                 }
6877             }
6878             
6879         }
6880     }
6881 });
6882
6883  
6884
6885  /*
6886  * - LGPL
6887  *
6888  * table cell
6889  * 
6890  */
6891
6892 /**
6893  * @class Roo.bootstrap.TableCell
6894  * @extends Roo.bootstrap.Component
6895  * Bootstrap TableCell class
6896  * @cfg {String} html cell contain text
6897  * @cfg {String} cls cell class
6898  * @cfg {String} tag cell tag (td|th) default td
6899  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6900  * @cfg {String} align Aligns the content in a cell
6901  * @cfg {String} axis Categorizes cells
6902  * @cfg {String} bgcolor Specifies the background color of a cell
6903  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6904  * @cfg {Number} colspan Specifies the number of columns a cell should span
6905  * @cfg {String} headers Specifies one or more header cells a cell is related to
6906  * @cfg {Number} height Sets the height of a cell
6907  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6908  * @cfg {Number} rowspan Sets the number of rows a cell should span
6909  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6910  * @cfg {String} valign Vertical aligns the content in a cell
6911  * @cfg {Number} width Specifies the width of a cell
6912  * 
6913  * @constructor
6914  * Create a new TableCell
6915  * @param {Object} config The config object
6916  */
6917
6918 Roo.bootstrap.TableCell = function(config){
6919     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6920 };
6921
6922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6923     
6924     html: false,
6925     cls: false,
6926     tag: false,
6927     abbr: false,
6928     align: false,
6929     axis: false,
6930     bgcolor: false,
6931     charoff: false,
6932     colspan: false,
6933     headers: false,
6934     height: false,
6935     nowrap: false,
6936     rowspan: false,
6937     scope: false,
6938     valign: false,
6939     width: false,
6940     
6941     
6942     getAutoCreate : function(){
6943         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6944         
6945         cfg = {
6946             tag: 'td'
6947         };
6948         
6949         if(this.tag){
6950             cfg.tag = this.tag;
6951         }
6952         
6953         if (this.html) {
6954             cfg.html=this.html
6955         }
6956         if (this.cls) {
6957             cfg.cls=this.cls
6958         }
6959         if (this.abbr) {
6960             cfg.abbr=this.abbr
6961         }
6962         if (this.align) {
6963             cfg.align=this.align
6964         }
6965         if (this.axis) {
6966             cfg.axis=this.axis
6967         }
6968         if (this.bgcolor) {
6969             cfg.bgcolor=this.bgcolor
6970         }
6971         if (this.charoff) {
6972             cfg.charoff=this.charoff
6973         }
6974         if (this.colspan) {
6975             cfg.colspan=this.colspan
6976         }
6977         if (this.headers) {
6978             cfg.headers=this.headers
6979         }
6980         if (this.height) {
6981             cfg.height=this.height
6982         }
6983         if (this.nowrap) {
6984             cfg.nowrap=this.nowrap
6985         }
6986         if (this.rowspan) {
6987             cfg.rowspan=this.rowspan
6988         }
6989         if (this.scope) {
6990             cfg.scope=this.scope
6991         }
6992         if (this.valign) {
6993             cfg.valign=this.valign
6994         }
6995         if (this.width) {
6996             cfg.width=this.width
6997         }
6998         
6999         
7000         return cfg;
7001     }
7002    
7003 });
7004
7005  
7006
7007  /*
7008  * - LGPL
7009  *
7010  * table row
7011  * 
7012  */
7013
7014 /**
7015  * @class Roo.bootstrap.TableRow
7016  * @extends Roo.bootstrap.Component
7017  * Bootstrap TableRow class
7018  * @cfg {String} cls row class
7019  * @cfg {String} align Aligns the content in a table row
7020  * @cfg {String} bgcolor Specifies a background color for a table row
7021  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7022  * @cfg {String} valign Vertical aligns the content in a table row
7023  * 
7024  * @constructor
7025  * Create a new TableRow
7026  * @param {Object} config The config object
7027  */
7028
7029 Roo.bootstrap.TableRow = function(config){
7030     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7031 };
7032
7033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7034     
7035     cls: false,
7036     align: false,
7037     bgcolor: false,
7038     charoff: false,
7039     valign: false,
7040     
7041     getAutoCreate : function(){
7042         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7043         
7044         cfg = {
7045             tag: 'tr'
7046         };
7047             
7048         if(this.cls){
7049             cfg.cls = this.cls;
7050         }
7051         if(this.align){
7052             cfg.align = this.align;
7053         }
7054         if(this.bgcolor){
7055             cfg.bgcolor = this.bgcolor;
7056         }
7057         if(this.charoff){
7058             cfg.charoff = this.charoff;
7059         }
7060         if(this.valign){
7061             cfg.valign = this.valign;
7062         }
7063         
7064         return cfg;
7065     }
7066    
7067 });
7068
7069  
7070
7071  /*
7072  * - LGPL
7073  *
7074  * table body
7075  * 
7076  */
7077
7078 /**
7079  * @class Roo.bootstrap.TableBody
7080  * @extends Roo.bootstrap.Component
7081  * Bootstrap TableBody class
7082  * @cfg {String} cls element class
7083  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7084  * @cfg {String} align Aligns the content inside the element
7085  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7086  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7087  * 
7088  * @constructor
7089  * Create a new TableBody
7090  * @param {Object} config The config object
7091  */
7092
7093 Roo.bootstrap.TableBody = function(config){
7094     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7095 };
7096
7097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7098     
7099     cls: false,
7100     tag: false,
7101     align: false,
7102     charoff: false,
7103     valign: false,
7104     
7105     getAutoCreate : function(){
7106         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7107         
7108         cfg = {
7109             tag: 'tbody'
7110         };
7111             
7112         if (this.cls) {
7113             cfg.cls=this.cls
7114         }
7115         if(this.tag){
7116             cfg.tag = this.tag;
7117         }
7118         
7119         if(this.align){
7120             cfg.align = this.align;
7121         }
7122         if(this.charoff){
7123             cfg.charoff = this.charoff;
7124         }
7125         if(this.valign){
7126             cfg.valign = this.valign;
7127         }
7128         
7129         return cfg;
7130     }
7131     
7132     
7133 //    initEvents : function()
7134 //    {
7135 //        
7136 //        if(!this.store){
7137 //            return;
7138 //        }
7139 //        
7140 //        this.store = Roo.factory(this.store, Roo.data);
7141 //        this.store.on('load', this.onLoad, this);
7142 //        
7143 //        this.store.load();
7144 //        
7145 //    },
7146 //    
7147 //    onLoad: function () 
7148 //    {   
7149 //        this.fireEvent('load', this);
7150 //    }
7151 //    
7152 //   
7153 });
7154
7155  
7156
7157  /*
7158  * Based on:
7159  * Ext JS Library 1.1.1
7160  * Copyright(c) 2006-2007, Ext JS, LLC.
7161  *
7162  * Originally Released Under LGPL - original licence link has changed is not relivant.
7163  *
7164  * Fork - LGPL
7165  * <script type="text/javascript">
7166  */
7167
7168 // as we use this in bootstrap.
7169 Roo.namespace('Roo.form');
7170  /**
7171  * @class Roo.form.Action
7172  * Internal Class used to handle form actions
7173  * @constructor
7174  * @param {Roo.form.BasicForm} el The form element or its id
7175  * @param {Object} config Configuration options
7176  */
7177
7178  
7179  
7180 // define the action interface
7181 Roo.form.Action = function(form, options){
7182     this.form = form;
7183     this.options = options || {};
7184 };
7185 /**
7186  * Client Validation Failed
7187  * @const 
7188  */
7189 Roo.form.Action.CLIENT_INVALID = 'client';
7190 /**
7191  * Server Validation Failed
7192  * @const 
7193  */
7194 Roo.form.Action.SERVER_INVALID = 'server';
7195  /**
7196  * Connect to Server Failed
7197  * @const 
7198  */
7199 Roo.form.Action.CONNECT_FAILURE = 'connect';
7200 /**
7201  * Reading Data from Server Failed
7202  * @const 
7203  */
7204 Roo.form.Action.LOAD_FAILURE = 'load';
7205
7206 Roo.form.Action.prototype = {
7207     type : 'default',
7208     failureType : undefined,
7209     response : undefined,
7210     result : undefined,
7211
7212     // interface method
7213     run : function(options){
7214
7215     },
7216
7217     // interface method
7218     success : function(response){
7219
7220     },
7221
7222     // interface method
7223     handleResponse : function(response){
7224
7225     },
7226
7227     // default connection failure
7228     failure : function(response){
7229         
7230         this.response = response;
7231         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7232         this.form.afterAction(this, false);
7233     },
7234
7235     processResponse : function(response){
7236         this.response = response;
7237         if(!response.responseText){
7238             return true;
7239         }
7240         this.result = this.handleResponse(response);
7241         return this.result;
7242     },
7243
7244     // utility functions used internally
7245     getUrl : function(appendParams){
7246         var url = this.options.url || this.form.url || this.form.el.dom.action;
7247         if(appendParams){
7248             var p = this.getParams();
7249             if(p){
7250                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7251             }
7252         }
7253         return url;
7254     },
7255
7256     getMethod : function(){
7257         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7258     },
7259
7260     getParams : function(){
7261         var bp = this.form.baseParams;
7262         var p = this.options.params;
7263         if(p){
7264             if(typeof p == "object"){
7265                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7266             }else if(typeof p == 'string' && bp){
7267                 p += '&' + Roo.urlEncode(bp);
7268             }
7269         }else if(bp){
7270             p = Roo.urlEncode(bp);
7271         }
7272         return p;
7273     },
7274
7275     createCallback : function(){
7276         return {
7277             success: this.success,
7278             failure: this.failure,
7279             scope: this,
7280             timeout: (this.form.timeout*1000),
7281             upload: this.form.fileUpload ? this.success : undefined
7282         };
7283     }
7284 };
7285
7286 Roo.form.Action.Submit = function(form, options){
7287     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7288 };
7289
7290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7291     type : 'submit',
7292
7293     haveProgress : false,
7294     uploadComplete : false,
7295     
7296     // uploadProgress indicator.
7297     uploadProgress : function()
7298     {
7299         if (!this.form.progressUrl) {
7300             return;
7301         }
7302         
7303         if (!this.haveProgress) {
7304             Roo.MessageBox.progress("Uploading", "Uploading");
7305         }
7306         if (this.uploadComplete) {
7307            Roo.MessageBox.hide();
7308            return;
7309         }
7310         
7311         this.haveProgress = true;
7312    
7313         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7314         
7315         var c = new Roo.data.Connection();
7316         c.request({
7317             url : this.form.progressUrl,
7318             params: {
7319                 id : uid
7320             },
7321             method: 'GET',
7322             success : function(req){
7323                //console.log(data);
7324                 var rdata = false;
7325                 var edata;
7326                 try  {
7327                    rdata = Roo.decode(req.responseText)
7328                 } catch (e) {
7329                     Roo.log("Invalid data from server..");
7330                     Roo.log(edata);
7331                     return;
7332                 }
7333                 if (!rdata || !rdata.success) {
7334                     Roo.log(rdata);
7335                     Roo.MessageBox.alert(Roo.encode(rdata));
7336                     return;
7337                 }
7338                 var data = rdata.data;
7339                 
7340                 if (this.uploadComplete) {
7341                    Roo.MessageBox.hide();
7342                    return;
7343                 }
7344                    
7345                 if (data){
7346                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7347                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7348                     );
7349                 }
7350                 this.uploadProgress.defer(2000,this);
7351             },
7352        
7353             failure: function(data) {
7354                 Roo.log('progress url failed ');
7355                 Roo.log(data);
7356             },
7357             scope : this
7358         });
7359            
7360     },
7361     
7362     
7363     run : function()
7364     {
7365         // run get Values on the form, so it syncs any secondary forms.
7366         this.form.getValues();
7367         
7368         var o = this.options;
7369         var method = this.getMethod();
7370         var isPost = method == 'POST';
7371         if(o.clientValidation === false || this.form.isValid()){
7372             
7373             if (this.form.progressUrl) {
7374                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7375                     (new Date() * 1) + '' + Math.random());
7376                     
7377             } 
7378             
7379             
7380             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7381                 form:this.form.el.dom,
7382                 url:this.getUrl(!isPost),
7383                 method: method,
7384                 params:isPost ? this.getParams() : null,
7385                 isUpload: this.form.fileUpload
7386             }));
7387             
7388             this.uploadProgress();
7389
7390         }else if (o.clientValidation !== false){ // client validation failed
7391             this.failureType = Roo.form.Action.CLIENT_INVALID;
7392             this.form.afterAction(this, false);
7393         }
7394     },
7395
7396     success : function(response)
7397     {
7398         this.uploadComplete= true;
7399         if (this.haveProgress) {
7400             Roo.MessageBox.hide();
7401         }
7402         
7403         
7404         var result = this.processResponse(response);
7405         if(result === true || result.success){
7406             this.form.afterAction(this, true);
7407             return;
7408         }
7409         if(result.errors){
7410             this.form.markInvalid(result.errors);
7411             this.failureType = Roo.form.Action.SERVER_INVALID;
7412         }
7413         this.form.afterAction(this, false);
7414     },
7415     failure : function(response)
7416     {
7417         this.uploadComplete= true;
7418         if (this.haveProgress) {
7419             Roo.MessageBox.hide();
7420         }
7421         
7422         this.response = response;
7423         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7424         this.form.afterAction(this, false);
7425     },
7426     
7427     handleResponse : function(response){
7428         if(this.form.errorReader){
7429             var rs = this.form.errorReader.read(response);
7430             var errors = [];
7431             if(rs.records){
7432                 for(var i = 0, len = rs.records.length; i < len; i++) {
7433                     var r = rs.records[i];
7434                     errors[i] = r.data;
7435                 }
7436             }
7437             if(errors.length < 1){
7438                 errors = null;
7439             }
7440             return {
7441                 success : rs.success,
7442                 errors : errors
7443             };
7444         }
7445         var ret = false;
7446         try {
7447             ret = Roo.decode(response.responseText);
7448         } catch (e) {
7449             ret = {
7450                 success: false,
7451                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7452                 errors : []
7453             };
7454         }
7455         return ret;
7456         
7457     }
7458 });
7459
7460
7461 Roo.form.Action.Load = function(form, options){
7462     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7463     this.reader = this.form.reader;
7464 };
7465
7466 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7467     type : 'load',
7468
7469     run : function(){
7470         
7471         Roo.Ajax.request(Roo.apply(
7472                 this.createCallback(), {
7473                     method:this.getMethod(),
7474                     url:this.getUrl(false),
7475                     params:this.getParams()
7476         }));
7477     },
7478
7479     success : function(response){
7480         
7481         var result = this.processResponse(response);
7482         if(result === true || !result.success || !result.data){
7483             this.failureType = Roo.form.Action.LOAD_FAILURE;
7484             this.form.afterAction(this, false);
7485             return;
7486         }
7487         this.form.clearInvalid();
7488         this.form.setValues(result.data);
7489         this.form.afterAction(this, true);
7490     },
7491
7492     handleResponse : function(response){
7493         if(this.form.reader){
7494             var rs = this.form.reader.read(response);
7495             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7496             return {
7497                 success : rs.success,
7498                 data : data
7499             };
7500         }
7501         return Roo.decode(response.responseText);
7502     }
7503 });
7504
7505 Roo.form.Action.ACTION_TYPES = {
7506     'load' : Roo.form.Action.Load,
7507     'submit' : Roo.form.Action.Submit
7508 };/*
7509  * - LGPL
7510  *
7511  * form
7512  *
7513  */
7514
7515 /**
7516  * @class Roo.bootstrap.Form
7517  * @extends Roo.bootstrap.Component
7518  * Bootstrap Form class
7519  * @cfg {String} method  GET | POST (default POST)
7520  * @cfg {String} labelAlign top | left (default top)
7521  * @cfg {String} align left  | right - for navbars
7522  * @cfg {Boolean} loadMask load mask when submit (default true)
7523
7524  *
7525  * @constructor
7526  * Create a new Form
7527  * @param {Object} config The config object
7528  */
7529
7530
7531 Roo.bootstrap.Form = function(config){
7532     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7533     
7534     Roo.bootstrap.Form.popover.apply();
7535     
7536     this.addEvents({
7537         /**
7538          * @event clientvalidation
7539          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7540          * @param {Form} this
7541          * @param {Boolean} valid true if the form has passed client-side validation
7542          */
7543         clientvalidation: true,
7544         /**
7545          * @event beforeaction
7546          * Fires before any action is performed. Return false to cancel the action.
7547          * @param {Form} this
7548          * @param {Action} action The action to be performed
7549          */
7550         beforeaction: true,
7551         /**
7552          * @event actionfailed
7553          * Fires when an action fails.
7554          * @param {Form} this
7555          * @param {Action} action The action that failed
7556          */
7557         actionfailed : true,
7558         /**
7559          * @event actioncomplete
7560          * Fires when an action is completed.
7561          * @param {Form} this
7562          * @param {Action} action The action that completed
7563          */
7564         actioncomplete : true
7565     });
7566
7567 };
7568
7569 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7570
7571      /**
7572      * @cfg {String} method
7573      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7574      */
7575     method : 'POST',
7576     /**
7577      * @cfg {String} url
7578      * The URL to use for form actions if one isn't supplied in the action options.
7579      */
7580     /**
7581      * @cfg {Boolean} fileUpload
7582      * Set to true if this form is a file upload.
7583      */
7584
7585     /**
7586      * @cfg {Object} baseParams
7587      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7588      */
7589
7590     /**
7591      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7592      */
7593     timeout: 30,
7594     /**
7595      * @cfg {Sting} align (left|right) for navbar forms
7596      */
7597     align : 'left',
7598
7599     // private
7600     activeAction : null,
7601
7602     /**
7603      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7604      * element by passing it or its id or mask the form itself by passing in true.
7605      * @type Mixed
7606      */
7607     waitMsgTarget : false,
7608
7609     loadMask : true,
7610     
7611     /**
7612      * @cfg {Boolean} errorMask (true|false) default false
7613      */
7614     errorMask : false,
7615     
7616     /**
7617      * @cfg {Number} maskOffset Default 100
7618      */
7619     maskOffset : 100,
7620
7621     getAutoCreate : function(){
7622
7623         var cfg = {
7624             tag: 'form',
7625             method : this.method || 'POST',
7626             id : this.id || Roo.id(),
7627             cls : ''
7628         };
7629         if (this.parent().xtype.match(/^Nav/)) {
7630             cfg.cls = 'navbar-form navbar-' + this.align;
7631
7632         }
7633
7634         if (this.labelAlign == 'left' ) {
7635             cfg.cls += ' form-horizontal';
7636         }
7637
7638
7639         return cfg;
7640     },
7641     initEvents : function()
7642     {
7643         this.el.on('submit', this.onSubmit, this);
7644         // this was added as random key presses on the form where triggering form submit.
7645         this.el.on('keypress', function(e) {
7646             if (e.getCharCode() != 13) {
7647                 return true;
7648             }
7649             // we might need to allow it for textareas.. and some other items.
7650             // check e.getTarget().
7651
7652             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7653                 return true;
7654             }
7655
7656             Roo.log("keypress blocked");
7657
7658             e.preventDefault();
7659             return false;
7660         });
7661         
7662     },
7663     // private
7664     onSubmit : function(e){
7665         e.stopEvent();
7666     },
7667
7668      /**
7669      * Returns true if client-side validation on the form is successful.
7670      * @return Boolean
7671      */
7672     isValid : function(){
7673         var items = this.getItems();
7674         var valid = true;
7675         var target = false;
7676         
7677         items.each(function(f){
7678             Roo.log(f.name);
7679             if(f.validate()){
7680                 return;
7681             }
7682             valid = false;
7683
7684             if(!target && f.el.isVisible(true)){
7685                 target = f;
7686             }
7687            
7688         });
7689         
7690         if(this.errorMask && !valid){
7691             Roo.bootstrap.Form.popover.mask(this, target);
7692         }
7693         
7694         return valid;
7695     },
7696     
7697     /**
7698      * Returns true if any fields in this form have changed since their original load.
7699      * @return Boolean
7700      */
7701     isDirty : function(){
7702         var dirty = false;
7703         var items = this.getItems();
7704         items.each(function(f){
7705            if(f.isDirty()){
7706                dirty = true;
7707                return false;
7708            }
7709            return true;
7710         });
7711         return dirty;
7712     },
7713      /**
7714      * Performs a predefined action (submit or load) or custom actions you define on this form.
7715      * @param {String} actionName The name of the action type
7716      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7717      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7718      * accept other config options):
7719      * <pre>
7720 Property          Type             Description
7721 ----------------  ---------------  ----------------------------------------------------------------------------------
7722 url               String           The url for the action (defaults to the form's url)
7723 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7724 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7725 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7726                                    validate the form on the client (defaults to false)
7727      * </pre>
7728      * @return {BasicForm} this
7729      */
7730     doAction : function(action, options){
7731         if(typeof action == 'string'){
7732             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7733         }
7734         if(this.fireEvent('beforeaction', this, action) !== false){
7735             this.beforeAction(action);
7736             action.run.defer(100, action);
7737         }
7738         return this;
7739     },
7740
7741     // private
7742     beforeAction : function(action){
7743         var o = action.options;
7744
7745         if(this.loadMask){
7746             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7747         }
7748         // not really supported yet.. ??
7749
7750         //if(this.waitMsgTarget === true){
7751         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7752         //}else if(this.waitMsgTarget){
7753         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7754         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7755         //}else {
7756         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7757        // }
7758
7759     },
7760
7761     // private
7762     afterAction : function(action, success){
7763         this.activeAction = null;
7764         var o = action.options;
7765
7766         //if(this.waitMsgTarget === true){
7767             this.el.unmask();
7768         //}else if(this.waitMsgTarget){
7769         //    this.waitMsgTarget.unmask();
7770         //}else{
7771         //    Roo.MessageBox.updateProgress(1);
7772         //    Roo.MessageBox.hide();
7773        // }
7774         //
7775         if(success){
7776             if(o.reset){
7777                 this.reset();
7778             }
7779             Roo.callback(o.success, o.scope, [this, action]);
7780             this.fireEvent('actioncomplete', this, action);
7781
7782         }else{
7783
7784             // failure condition..
7785             // we have a scenario where updates need confirming.
7786             // eg. if a locking scenario exists..
7787             // we look for { errors : { needs_confirm : true }} in the response.
7788             if (
7789                 (typeof(action.result) != 'undefined')  &&
7790                 (typeof(action.result.errors) != 'undefined')  &&
7791                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7792            ){
7793                 var _t = this;
7794                 Roo.log("not supported yet");
7795                  /*
7796
7797                 Roo.MessageBox.confirm(
7798                     "Change requires confirmation",
7799                     action.result.errorMsg,
7800                     function(r) {
7801                         if (r != 'yes') {
7802                             return;
7803                         }
7804                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7805                     }
7806
7807                 );
7808                 */
7809
7810
7811                 return;
7812             }
7813
7814             Roo.callback(o.failure, o.scope, [this, action]);
7815             // show an error message if no failed handler is set..
7816             if (!this.hasListener('actionfailed')) {
7817                 Roo.log("need to add dialog support");
7818                 /*
7819                 Roo.MessageBox.alert("Error",
7820                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7821                         action.result.errorMsg :
7822                         "Saving Failed, please check your entries or try again"
7823                 );
7824                 */
7825             }
7826
7827             this.fireEvent('actionfailed', this, action);
7828         }
7829
7830     },
7831     /**
7832      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7833      * @param {String} id The value to search for
7834      * @return Field
7835      */
7836     findField : function(id){
7837         var items = this.getItems();
7838         var field = items.get(id);
7839         if(!field){
7840              items.each(function(f){
7841                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7842                     field = f;
7843                     return false;
7844                 }
7845                 return true;
7846             });
7847         }
7848         return field || null;
7849     },
7850      /**
7851      * Mark fields in this form invalid in bulk.
7852      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7853      * @return {BasicForm} this
7854      */
7855     markInvalid : function(errors){
7856         if(errors instanceof Array){
7857             for(var i = 0, len = errors.length; i < len; i++){
7858                 var fieldError = errors[i];
7859                 var f = this.findField(fieldError.id);
7860                 if(f){
7861                     f.markInvalid(fieldError.msg);
7862                 }
7863             }
7864         }else{
7865             var field, id;
7866             for(id in errors){
7867                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7868                     field.markInvalid(errors[id]);
7869                 }
7870             }
7871         }
7872         //Roo.each(this.childForms || [], function (f) {
7873         //    f.markInvalid(errors);
7874         //});
7875
7876         return this;
7877     },
7878
7879     /**
7880      * Set values for fields in this form in bulk.
7881      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7882      * @return {BasicForm} this
7883      */
7884     setValues : function(values){
7885         if(values instanceof Array){ // array of objects
7886             for(var i = 0, len = values.length; i < len; i++){
7887                 var v = values[i];
7888                 var f = this.findField(v.id);
7889                 if(f){
7890                     f.setValue(v.value);
7891                     if(this.trackResetOnLoad){
7892                         f.originalValue = f.getValue();
7893                     }
7894                 }
7895             }
7896         }else{ // object hash
7897             var field, id;
7898             for(id in values){
7899                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7900
7901                     if (field.setFromData &&
7902                         field.valueField &&
7903                         field.displayField &&
7904                         // combos' with local stores can
7905                         // be queried via setValue()
7906                         // to set their value..
7907                         (field.store && !field.store.isLocal)
7908                         ) {
7909                         // it's a combo
7910                         var sd = { };
7911                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7912                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7913                         field.setFromData(sd);
7914
7915                     } else {
7916                         field.setValue(values[id]);
7917                     }
7918
7919
7920                     if(this.trackResetOnLoad){
7921                         field.originalValue = field.getValue();
7922                     }
7923                 }
7924             }
7925         }
7926
7927         //Roo.each(this.childForms || [], function (f) {
7928         //    f.setValues(values);
7929         //});
7930
7931         return this;
7932     },
7933
7934     /**
7935      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7936      * they are returned as an array.
7937      * @param {Boolean} asString
7938      * @return {Object}
7939      */
7940     getValues : function(asString){
7941         //if (this.childForms) {
7942             // copy values from the child forms
7943         //    Roo.each(this.childForms, function (f) {
7944         //        this.setValues(f.getValues());
7945         //    }, this);
7946         //}
7947
7948
7949
7950         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7951         if(asString === true){
7952             return fs;
7953         }
7954         return Roo.urlDecode(fs);
7955     },
7956
7957     /**
7958      * Returns the fields in this form as an object with key/value pairs.
7959      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7960      * @return {Object}
7961      */
7962     getFieldValues : function(with_hidden)
7963     {
7964         var items = this.getItems();
7965         var ret = {};
7966         items.each(function(f){
7967             if (!f.getName()) {
7968                 return;
7969             }
7970             var v = f.getValue();
7971             if (f.inputType =='radio') {
7972                 if (typeof(ret[f.getName()]) == 'undefined') {
7973                     ret[f.getName()] = ''; // empty..
7974                 }
7975
7976                 if (!f.el.dom.checked) {
7977                     return;
7978
7979                 }
7980                 v = f.el.dom.value;
7981
7982             }
7983
7984             // not sure if this supported any more..
7985             if ((typeof(v) == 'object') && f.getRawValue) {
7986                 v = f.getRawValue() ; // dates..
7987             }
7988             // combo boxes where name != hiddenName...
7989             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7990                 ret[f.name] = f.getRawValue();
7991             }
7992             ret[f.getName()] = v;
7993         });
7994
7995         return ret;
7996     },
7997
7998     /**
7999      * Clears all invalid messages in this form.
8000      * @return {BasicForm} this
8001      */
8002     clearInvalid : function(){
8003         var items = this.getItems();
8004
8005         items.each(function(f){
8006            f.clearInvalid();
8007         });
8008
8009
8010
8011         return this;
8012     },
8013
8014     /**
8015      * Resets this form.
8016      * @return {BasicForm} this
8017      */
8018     reset : function(){
8019         var items = this.getItems();
8020         items.each(function(f){
8021             f.reset();
8022         });
8023
8024         Roo.each(this.childForms || [], function (f) {
8025             f.reset();
8026         });
8027
8028
8029         return this;
8030     },
8031     getItems : function()
8032     {
8033         var r=new Roo.util.MixedCollection(false, function(o){
8034             return o.id || (o.id = Roo.id());
8035         });
8036         var iter = function(el) {
8037             if (el.inputEl) {
8038                 r.add(el);
8039             }
8040             if (!el.items) {
8041                 return;
8042             }
8043             Roo.each(el.items,function(e) {
8044                 iter(e);
8045             });
8046
8047
8048         };
8049
8050         iter(this);
8051         return r;
8052
8053
8054
8055
8056     }
8057
8058 });
8059
8060 Roo.apply(Roo.bootstrap.Form, {
8061     
8062     popover : {
8063         
8064         padding : 5,
8065         
8066         isApplied : false,
8067         
8068         isMasked : false,
8069         
8070         form : false,
8071         
8072         target : false,
8073         
8074         toolTip : false,
8075         
8076         intervalID : false,
8077         
8078         maskEl : false,
8079         
8080         apply : function()
8081         {
8082             if(this.isApplied){
8083                 return;
8084             }
8085             
8086             this.maskEl = {
8087                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8088                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8089                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8090                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8091             };
8092             
8093             this.maskEl.top.enableDisplayMode("block");
8094             this.maskEl.left.enableDisplayMode("block");
8095             this.maskEl.bottom.enableDisplayMode("block");
8096             this.maskEl.right.enableDisplayMode("block");
8097             
8098             this.toolTip = new Roo.bootstrap.Tooltip({
8099                 cls : 'roo-form-error-popover',
8100                 alignment : {
8101                     'left' : ['r-l', [-2,0], 'right'],
8102                     'right' : ['l-r', [2,0], 'left'],
8103                     'bottom' : ['tl-bl', [0,2], 'top'],
8104                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8105                 }
8106             });
8107             
8108             this.toolTip.render(Roo.get(document.body));
8109
8110             this.toolTip.el.enableDisplayMode("block");
8111             
8112             Roo.get(document.body).on('click', function(){
8113                 this.unmask();
8114             }, this);
8115             
8116             Roo.get(document.body).on('touchstart', function(){
8117                 this.unmask();
8118             }, this);
8119             
8120             this.isApplied = true
8121         },
8122         
8123         mask : function(form, target)
8124         {
8125             this.form = form;
8126             
8127             this.target = target;
8128             
8129             if(!this.form.errorMask || !target.el){
8130                 return;
8131             }
8132             
8133             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8134             
8135             Roo.log(scrollable);
8136             
8137             var ot = this.target.el.calcOffsetsTo(scrollable);
8138             
8139             var scrollTo = ot[1] - this.form.maskOffset;
8140             
8141             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8142             
8143             scrollable.scrollTo('top', scrollTo);
8144             
8145             var box = this.target.el.getBox();
8146             Roo.log(box);
8147             var zIndex = Roo.bootstrap.Modal.zIndex++;
8148
8149             
8150             this.maskEl.top.setStyle('position', 'absolute');
8151             this.maskEl.top.setStyle('z-index', zIndex);
8152             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8153             this.maskEl.top.setLeft(0);
8154             this.maskEl.top.setTop(0);
8155             this.maskEl.top.show();
8156             
8157             this.maskEl.left.setStyle('position', 'absolute');
8158             this.maskEl.left.setStyle('z-index', zIndex);
8159             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8160             this.maskEl.left.setLeft(0);
8161             this.maskEl.left.setTop(box.y - this.padding);
8162             this.maskEl.left.show();
8163
8164             this.maskEl.bottom.setStyle('position', 'absolute');
8165             this.maskEl.bottom.setStyle('z-index', zIndex);
8166             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8167             this.maskEl.bottom.setLeft(0);
8168             this.maskEl.bottom.setTop(box.bottom + this.padding);
8169             this.maskEl.bottom.show();
8170
8171             this.maskEl.right.setStyle('position', 'absolute');
8172             this.maskEl.right.setStyle('z-index', zIndex);
8173             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8174             this.maskEl.right.setLeft(box.right + this.padding);
8175             this.maskEl.right.setTop(box.y - this.padding);
8176             this.maskEl.right.show();
8177
8178             this.toolTip.bindEl = this.target.el;
8179
8180             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8181
8182             var tip = this.target.blankText;
8183
8184             if(this.target.getValue() !== '' ) {
8185                 
8186                 if (this.target.invalidText.length) {
8187                     tip = this.target.invalidText;
8188                 } else if (this.target.regexText.length){
8189                     tip = this.target.regexText;
8190                 }
8191             }
8192
8193             this.toolTip.show(tip);
8194
8195             this.intervalID = window.setInterval(function() {
8196                 Roo.bootstrap.Form.popover.unmask();
8197             }, 10000);
8198
8199             window.onwheel = function(){ return false;};
8200             
8201             (function(){ this.isMasked = true; }).defer(500, this);
8202             
8203         },
8204         
8205         unmask : function()
8206         {
8207             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8208                 return;
8209             }
8210             
8211             this.maskEl.top.setStyle('position', 'absolute');
8212             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8213             this.maskEl.top.hide();
8214
8215             this.maskEl.left.setStyle('position', 'absolute');
8216             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8217             this.maskEl.left.hide();
8218
8219             this.maskEl.bottom.setStyle('position', 'absolute');
8220             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8221             this.maskEl.bottom.hide();
8222
8223             this.maskEl.right.setStyle('position', 'absolute');
8224             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8225             this.maskEl.right.hide();
8226             
8227             this.toolTip.hide();
8228             
8229             this.toolTip.el.hide();
8230             
8231             window.onwheel = function(){ return true;};
8232             
8233             if(this.intervalID){
8234                 window.clearInterval(this.intervalID);
8235                 this.intervalID = false;
8236             }
8237             
8238             this.isMasked = false;
8239             
8240         }
8241         
8242     }
8243     
8244 });
8245
8246 /*
8247  * Based on:
8248  * Ext JS Library 1.1.1
8249  * Copyright(c) 2006-2007, Ext JS, LLC.
8250  *
8251  * Originally Released Under LGPL - original licence link has changed is not relivant.
8252  *
8253  * Fork - LGPL
8254  * <script type="text/javascript">
8255  */
8256 /**
8257  * @class Roo.form.VTypes
8258  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8259  * @singleton
8260  */
8261 Roo.form.VTypes = function(){
8262     // closure these in so they are only created once.
8263     var alpha = /^[a-zA-Z_]+$/;
8264     var alphanum = /^[a-zA-Z0-9_]+$/;
8265     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8266     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8267
8268     // All these messages and functions are configurable
8269     return {
8270         /**
8271          * The function used to validate email addresses
8272          * @param {String} value The email address
8273          */
8274         'email' : function(v){
8275             return email.test(v);
8276         },
8277         /**
8278          * The error text to display when the email validation function returns false
8279          * @type String
8280          */
8281         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8282         /**
8283          * The keystroke filter mask to be applied on email input
8284          * @type RegExp
8285          */
8286         'emailMask' : /[a-z0-9_\.\-@]/i,
8287
8288         /**
8289          * The function used to validate URLs
8290          * @param {String} value The URL
8291          */
8292         'url' : function(v){
8293             return url.test(v);
8294         },
8295         /**
8296          * The error text to display when the url validation function returns false
8297          * @type String
8298          */
8299         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8300         
8301         /**
8302          * The function used to validate alpha values
8303          * @param {String} value The value
8304          */
8305         'alpha' : function(v){
8306             return alpha.test(v);
8307         },
8308         /**
8309          * The error text to display when the alpha validation function returns false
8310          * @type String
8311          */
8312         'alphaText' : 'This field should only contain letters and _',
8313         /**
8314          * The keystroke filter mask to be applied on alpha input
8315          * @type RegExp
8316          */
8317         'alphaMask' : /[a-z_]/i,
8318
8319         /**
8320          * The function used to validate alphanumeric values
8321          * @param {String} value The value
8322          */
8323         'alphanum' : function(v){
8324             return alphanum.test(v);
8325         },
8326         /**
8327          * The error text to display when the alphanumeric validation function returns false
8328          * @type String
8329          */
8330         'alphanumText' : 'This field should only contain letters, numbers and _',
8331         /**
8332          * The keystroke filter mask to be applied on alphanumeric input
8333          * @type RegExp
8334          */
8335         'alphanumMask' : /[a-z0-9_]/i
8336     };
8337 }();/*
8338  * - LGPL
8339  *
8340  * Input
8341  * 
8342  */
8343
8344 /**
8345  * @class Roo.bootstrap.Input
8346  * @extends Roo.bootstrap.Component
8347  * Bootstrap Input class
8348  * @cfg {Boolean} disabled is it disabled
8349  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8350  * @cfg {String} name name of the input
8351  * @cfg {string} fieldLabel - the label associated
8352  * @cfg {string} placeholder - placeholder to put in text.
8353  * @cfg {string}  before - input group add on before
8354  * @cfg {string} after - input group add on after
8355  * @cfg {string} size - (lg|sm) or leave empty..
8356  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8357  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8358  * @cfg {Number} md colspan out of 12 for computer-sized screens
8359  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8360  * @cfg {string} value default value of the input
8361  * @cfg {Number} labelWidth set the width of label 
8362  * @cfg {Number} labellg set the width of label (1-12)
8363  * @cfg {Number} labelmd set the width of label (1-12)
8364  * @cfg {Number} labelsm set the width of label (1-12)
8365  * @cfg {Number} labelxs set the width of label (1-12)
8366  * @cfg {String} labelAlign (top|left)
8367  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8368  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8369  * @cfg {String} indicatorpos (left|right) default left
8370
8371  * @cfg {String} align (left|center|right) Default left
8372  * @cfg {Boolean} forceFeedback (true|false) Default false
8373  * 
8374  * 
8375  * 
8376  * 
8377  * @constructor
8378  * Create a new Input
8379  * @param {Object} config The config object
8380  */
8381
8382 Roo.bootstrap.Input = function(config){
8383     
8384     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8385     
8386     this.addEvents({
8387         /**
8388          * @event focus
8389          * Fires when this field receives input focus.
8390          * @param {Roo.form.Field} this
8391          */
8392         focus : true,
8393         /**
8394          * @event blur
8395          * Fires when this field loses input focus.
8396          * @param {Roo.form.Field} this
8397          */
8398         blur : true,
8399         /**
8400          * @event specialkey
8401          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8402          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8403          * @param {Roo.form.Field} this
8404          * @param {Roo.EventObject} e The event object
8405          */
8406         specialkey : true,
8407         /**
8408          * @event change
8409          * Fires just before the field blurs if the field value has changed.
8410          * @param {Roo.form.Field} this
8411          * @param {Mixed} newValue The new value
8412          * @param {Mixed} oldValue The original value
8413          */
8414         change : true,
8415         /**
8416          * @event invalid
8417          * Fires after the field has been marked as invalid.
8418          * @param {Roo.form.Field} this
8419          * @param {String} msg The validation message
8420          */
8421         invalid : true,
8422         /**
8423          * @event valid
8424          * Fires after the field has been validated with no errors.
8425          * @param {Roo.form.Field} this
8426          */
8427         valid : true,
8428          /**
8429          * @event keyup
8430          * Fires after the key up
8431          * @param {Roo.form.Field} this
8432          * @param {Roo.EventObject}  e The event Object
8433          */
8434         keyup : true
8435     });
8436 };
8437
8438 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8439      /**
8440      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8441       automatic validation (defaults to "keyup").
8442      */
8443     validationEvent : "keyup",
8444      /**
8445      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8446      */
8447     validateOnBlur : true,
8448     /**
8449      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8450      */
8451     validationDelay : 250,
8452      /**
8453      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8454      */
8455     focusClass : "x-form-focus",  // not needed???
8456     
8457        
8458     /**
8459      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8460      */
8461     invalidClass : "has-warning",
8462     
8463     /**
8464      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8465      */
8466     validClass : "has-success",
8467     
8468     /**
8469      * @cfg {Boolean} hasFeedback (true|false) default true
8470      */
8471     hasFeedback : true,
8472     
8473     /**
8474      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8475      */
8476     invalidFeedbackClass : "glyphicon-warning-sign",
8477     
8478     /**
8479      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8480      */
8481     validFeedbackClass : "glyphicon-ok",
8482     
8483     /**
8484      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8485      */
8486     selectOnFocus : false,
8487     
8488      /**
8489      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8490      */
8491     maskRe : null,
8492        /**
8493      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8494      */
8495     vtype : null,
8496     
8497       /**
8498      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8499      */
8500     disableKeyFilter : false,
8501     
8502        /**
8503      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8504      */
8505     disabled : false,
8506      /**
8507      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8508      */
8509     allowBlank : true,
8510     /**
8511      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8512      */
8513     blankText : "Please complete this mandatory field",
8514     
8515      /**
8516      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8517      */
8518     minLength : 0,
8519     /**
8520      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8521      */
8522     maxLength : Number.MAX_VALUE,
8523     /**
8524      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8525      */
8526     minLengthText : "The minimum length for this field is {0}",
8527     /**
8528      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8529      */
8530     maxLengthText : "The maximum length for this field is {0}",
8531   
8532     
8533     /**
8534      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8535      * If available, this function will be called only after the basic validators all return true, and will be passed the
8536      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8537      */
8538     validator : null,
8539     /**
8540      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8541      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8542      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8543      */
8544     regex : null,
8545     /**
8546      * @cfg {String} regexText -- Depricated - use Invalid Text
8547      */
8548     regexText : "",
8549     
8550     /**
8551      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8552      */
8553     invalidText : "",
8554     
8555     
8556     
8557     autocomplete: false,
8558     
8559     
8560     fieldLabel : '',
8561     inputType : 'text',
8562     
8563     name : false,
8564     placeholder: false,
8565     before : false,
8566     after : false,
8567     size : false,
8568     hasFocus : false,
8569     preventMark: false,
8570     isFormField : true,
8571     value : '',
8572     labelWidth : 2,
8573     labelAlign : false,
8574     readOnly : false,
8575     align : false,
8576     formatedValue : false,
8577     forceFeedback : false,
8578     
8579     indicatorpos : 'left',
8580     
8581     labellg : 0,
8582     labelmd : 0,
8583     labelsm : 0,
8584     labelxs : 0,
8585     
8586     parentLabelAlign : function()
8587     {
8588         var parent = this;
8589         while (parent.parent()) {
8590             parent = parent.parent();
8591             if (typeof(parent.labelAlign) !='undefined') {
8592                 return parent.labelAlign;
8593             }
8594         }
8595         return 'left';
8596         
8597     },
8598     
8599     getAutoCreate : function()
8600     {
8601         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8602         
8603         var id = Roo.id();
8604         
8605         var cfg = {};
8606         
8607         if(this.inputType != 'hidden'){
8608             cfg.cls = 'form-group' //input-group
8609         }
8610         
8611         var input =  {
8612             tag: 'input',
8613             id : id,
8614             type : this.inputType,
8615             value : this.value,
8616             cls : 'form-control',
8617             placeholder : this.placeholder || '',
8618             autocomplete : this.autocomplete || 'new-password'
8619         };
8620         
8621         if(this.align){
8622             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8623         }
8624         
8625         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8626             input.maxLength = this.maxLength;
8627         }
8628         
8629         if (this.disabled) {
8630             input.disabled=true;
8631         }
8632         
8633         if (this.readOnly) {
8634             input.readonly=true;
8635         }
8636         
8637         if (this.name) {
8638             input.name = this.name;
8639         }
8640         
8641         if (this.size) {
8642             input.cls += ' input-' + this.size;
8643         }
8644         
8645         var settings=this;
8646         ['xs','sm','md','lg'].map(function(size){
8647             if (settings[size]) {
8648                 cfg.cls += ' col-' + size + '-' + settings[size];
8649             }
8650         });
8651         
8652         var inputblock = input;
8653         
8654         var feedback = {
8655             tag: 'span',
8656             cls: 'glyphicon form-control-feedback'
8657         };
8658             
8659         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8660             
8661             inputblock = {
8662                 cls : 'has-feedback',
8663                 cn :  [
8664                     input,
8665                     feedback
8666                 ] 
8667             };  
8668         }
8669         
8670         if (this.before || this.after) {
8671             
8672             inputblock = {
8673                 cls : 'input-group',
8674                 cn :  [] 
8675             };
8676             
8677             if (this.before && typeof(this.before) == 'string') {
8678                 
8679                 inputblock.cn.push({
8680                     tag :'span',
8681                     cls : 'roo-input-before input-group-addon',
8682                     html : this.before
8683                 });
8684             }
8685             if (this.before && typeof(this.before) == 'object') {
8686                 this.before = Roo.factory(this.before);
8687                 
8688                 inputblock.cn.push({
8689                     tag :'span',
8690                     cls : 'roo-input-before input-group-' +
8691                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8692                 });
8693             }
8694             
8695             inputblock.cn.push(input);
8696             
8697             if (this.after && typeof(this.after) == 'string') {
8698                 inputblock.cn.push({
8699                     tag :'span',
8700                     cls : 'roo-input-after input-group-addon',
8701                     html : this.after
8702                 });
8703             }
8704             if (this.after && typeof(this.after) == 'object') {
8705                 this.after = Roo.factory(this.after);
8706                 
8707                 inputblock.cn.push({
8708                     tag :'span',
8709                     cls : 'roo-input-after input-group-' +
8710                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8711                 });
8712             }
8713             
8714             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8715                 inputblock.cls += ' has-feedback';
8716                 inputblock.cn.push(feedback);
8717             }
8718         };
8719         
8720         if (align ==='left' && this.fieldLabel.length) {
8721             
8722             cfg.cls += ' roo-form-group-label-left';
8723             
8724             cfg.cn = [
8725                 {
8726                     tag : 'i',
8727                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8728                     tooltip : 'This field is required'
8729                 },
8730                 {
8731                     tag: 'label',
8732                     'for' :  id,
8733                     cls : 'control-label',
8734                     html : this.fieldLabel
8735
8736                 },
8737                 {
8738                     cls : "", 
8739                     cn: [
8740                         inputblock
8741                     ]
8742                 }
8743             ];
8744             
8745             var labelCfg = cfg.cn[1];
8746             var contentCfg = cfg.cn[2];
8747             
8748             if(this.indicatorpos == 'right'){
8749                 cfg.cn = [
8750                     {
8751                         tag: 'label',
8752                         'for' :  id,
8753                         cls : 'control-label',
8754                         cn : [
8755                             {
8756                                 tag : 'span',
8757                                 html : this.fieldLabel
8758                             },
8759                             {
8760                                 tag : 'i',
8761                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8762                                 tooltip : 'This field is required'
8763                             }
8764                         ]
8765                     },
8766                     {
8767                         cls : "",
8768                         cn: [
8769                             inputblock
8770                         ]
8771                     }
8772
8773                 ];
8774                 
8775                 labelCfg = cfg.cn[0];
8776                 contentCfg = cfg.cn[1];
8777             
8778             }
8779             
8780             if(this.labelWidth > 12){
8781                 labelCfg.style = "width: " + this.labelWidth + 'px';
8782             }
8783             
8784             if(this.labelWidth < 13 && this.labelmd == 0){
8785                 this.labelmd = this.labelWidth;
8786             }
8787             
8788             if(this.labellg > 0){
8789                 labelCfg.cls += ' col-lg-' + this.labellg;
8790                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8791             }
8792             
8793             if(this.labelmd > 0){
8794                 labelCfg.cls += ' col-md-' + this.labelmd;
8795                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8796             }
8797             
8798             if(this.labelsm > 0){
8799                 labelCfg.cls += ' col-sm-' + this.labelsm;
8800                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8801             }
8802             
8803             if(this.labelxs > 0){
8804                 labelCfg.cls += ' col-xs-' + this.labelxs;
8805                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8806             }
8807             
8808             
8809         } else if ( this.fieldLabel.length) {
8810                 
8811             cfg.cn = [
8812                 {
8813                     tag : 'i',
8814                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8815                     tooltip : 'This field is required'
8816                 },
8817                 {
8818                     tag: 'label',
8819                    //cls : 'input-group-addon',
8820                     html : this.fieldLabel
8821
8822                 },
8823
8824                inputblock
8825
8826            ];
8827            
8828            if(this.indicatorpos == 'right'){
8829                 
8830                 cfg.cn = [
8831                     {
8832                         tag: 'label',
8833                        //cls : 'input-group-addon',
8834                         html : this.fieldLabel
8835
8836                     },
8837                     {
8838                         tag : 'i',
8839                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8840                         tooltip : 'This field is required'
8841                     },
8842
8843                    inputblock
8844
8845                ];
8846
8847             }
8848
8849         } else {
8850             
8851             cfg.cn = [
8852
8853                     inputblock
8854
8855             ];
8856                 
8857                 
8858         };
8859         
8860         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8861            cfg.cls += ' navbar-form';
8862         }
8863         
8864         if (this.parentType === 'NavGroup') {
8865            cfg.cls += ' navbar-form';
8866            cfg.tag = 'li';
8867         }
8868         
8869         return cfg;
8870         
8871     },
8872     /**
8873      * return the real input element.
8874      */
8875     inputEl: function ()
8876     {
8877         return this.el.select('input.form-control',true).first();
8878     },
8879     
8880     tooltipEl : function()
8881     {
8882         return this.inputEl();
8883     },
8884     
8885     indicatorEl : function()
8886     {
8887         var indicator = this.el.select('i.roo-required-indicator',true).first();
8888         
8889         if(!indicator){
8890             return false;
8891         }
8892         
8893         return indicator;
8894         
8895     },
8896     
8897     setDisabled : function(v)
8898     {
8899         var i  = this.inputEl().dom;
8900         if (!v) {
8901             i.removeAttribute('disabled');
8902             return;
8903             
8904         }
8905         i.setAttribute('disabled','true');
8906     },
8907     initEvents : function()
8908     {
8909           
8910         this.inputEl().on("keydown" , this.fireKey,  this);
8911         this.inputEl().on("focus", this.onFocus,  this);
8912         this.inputEl().on("blur", this.onBlur,  this);
8913         
8914         this.inputEl().relayEvent('keyup', this);
8915         
8916         this.indicator = this.indicatorEl();
8917         
8918         if(this.indicator){
8919             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8920             this.indicator.hide();
8921         }
8922  
8923         // reference to original value for reset
8924         this.originalValue = this.getValue();
8925         //Roo.form.TextField.superclass.initEvents.call(this);
8926         if(this.validationEvent == 'keyup'){
8927             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8928             this.inputEl().on('keyup', this.filterValidation, this);
8929         }
8930         else if(this.validationEvent !== false){
8931             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8932         }
8933         
8934         if(this.selectOnFocus){
8935             this.on("focus", this.preFocus, this);
8936             
8937         }
8938         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8939             this.inputEl().on("keypress", this.filterKeys, this);
8940         } else {
8941             this.inputEl().relayEvent('keypress', this);
8942         }
8943        /* if(this.grow){
8944             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8945             this.el.on("click", this.autoSize,  this);
8946         }
8947         */
8948         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8949             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8950         }
8951         
8952         if (typeof(this.before) == 'object') {
8953             this.before.render(this.el.select('.roo-input-before',true).first());
8954         }
8955         if (typeof(this.after) == 'object') {
8956             this.after.render(this.el.select('.roo-input-after',true).first());
8957         }
8958         
8959         
8960     },
8961     filterValidation : function(e){
8962         if(!e.isNavKeyPress()){
8963             this.validationTask.delay(this.validationDelay);
8964         }
8965     },
8966      /**
8967      * Validates the field value
8968      * @return {Boolean} True if the value is valid, else false
8969      */
8970     validate : function(){
8971         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8972         if(this.disabled || this.validateValue(this.getRawValue())){
8973             this.markValid();
8974             return true;
8975         }
8976         
8977         this.markInvalid();
8978         return false;
8979     },
8980     
8981     
8982     /**
8983      * Validates a value according to the field's validation rules and marks the field as invalid
8984      * if the validation fails
8985      * @param {Mixed} value The value to validate
8986      * @return {Boolean} True if the value is valid, else false
8987      */
8988     validateValue : function(value){
8989         if(value.length < 1)  { // if it's blank
8990             if(this.allowBlank){
8991                 return true;
8992             }            
8993             return this.inputEl().hasClass('hide') ? true : false;
8994         }
8995         
8996         if(value.length < this.minLength){
8997             return false;
8998         }
8999         if(value.length > this.maxLength){
9000             return false;
9001         }
9002         if(this.vtype){
9003             var vt = Roo.form.VTypes;
9004             if(!vt[this.vtype](value, this)){
9005                 return false;
9006             }
9007         }
9008         if(typeof this.validator == "function"){
9009             var msg = this.validator(value);
9010             if(msg !== true){
9011                 return false;
9012             }
9013             if (typeof(msg) == 'string') {
9014                 this.invalidText = msg;
9015             }
9016         }
9017         
9018         if(this.regex && !this.regex.test(value)){
9019             return false;
9020         }
9021         
9022         return true;
9023     },
9024
9025     
9026     
9027      // private
9028     fireKey : function(e){
9029         //Roo.log('field ' + e.getKey());
9030         if(e.isNavKeyPress()){
9031             this.fireEvent("specialkey", this, e);
9032         }
9033     },
9034     focus : function (selectText){
9035         if(this.rendered){
9036             this.inputEl().focus();
9037             if(selectText === true){
9038                 this.inputEl().dom.select();
9039             }
9040         }
9041         return this;
9042     } ,
9043     
9044     onFocus : function(){
9045         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9046            // this.el.addClass(this.focusClass);
9047         }
9048         if(!this.hasFocus){
9049             this.hasFocus = true;
9050             this.startValue = this.getValue();
9051             this.fireEvent("focus", this);
9052         }
9053     },
9054     
9055     beforeBlur : Roo.emptyFn,
9056
9057     
9058     // private
9059     onBlur : function(){
9060         this.beforeBlur();
9061         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9062             //this.el.removeClass(this.focusClass);
9063         }
9064         this.hasFocus = false;
9065         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9066             this.validate();
9067         }
9068         var v = this.getValue();
9069         if(String(v) !== String(this.startValue)){
9070             this.fireEvent('change', this, v, this.startValue);
9071         }
9072         this.fireEvent("blur", this);
9073     },
9074     
9075     /**
9076      * Resets the current field value to the originally loaded value and clears any validation messages
9077      */
9078     reset : function(){
9079         this.setValue(this.originalValue);
9080         this.validate();
9081     },
9082      /**
9083      * Returns the name of the field
9084      * @return {Mixed} name The name field
9085      */
9086     getName: function(){
9087         return this.name;
9088     },
9089      /**
9090      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9091      * @return {Mixed} value The field value
9092      */
9093     getValue : function(){
9094         
9095         var v = this.inputEl().getValue();
9096         
9097         return v;
9098     },
9099     /**
9100      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9101      * @return {Mixed} value The field value
9102      */
9103     getRawValue : function(){
9104         var v = this.inputEl().getValue();
9105         
9106         return v;
9107     },
9108     
9109     /**
9110      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9111      * @param {Mixed} value The value to set
9112      */
9113     setRawValue : function(v){
9114         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9115     },
9116     
9117     selectText : function(start, end){
9118         var v = this.getRawValue();
9119         if(v.length > 0){
9120             start = start === undefined ? 0 : start;
9121             end = end === undefined ? v.length : end;
9122             var d = this.inputEl().dom;
9123             if(d.setSelectionRange){
9124                 d.setSelectionRange(start, end);
9125             }else if(d.createTextRange){
9126                 var range = d.createTextRange();
9127                 range.moveStart("character", start);
9128                 range.moveEnd("character", v.length-end);
9129                 range.select();
9130             }
9131         }
9132     },
9133     
9134     /**
9135      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9136      * @param {Mixed} value The value to set
9137      */
9138     setValue : function(v){
9139         this.value = v;
9140         if(this.rendered){
9141             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9142             this.validate();
9143         }
9144     },
9145     
9146     /*
9147     processValue : function(value){
9148         if(this.stripCharsRe){
9149             var newValue = value.replace(this.stripCharsRe, '');
9150             if(newValue !== value){
9151                 this.setRawValue(newValue);
9152                 return newValue;
9153             }
9154         }
9155         return value;
9156     },
9157   */
9158     preFocus : function(){
9159         
9160         if(this.selectOnFocus){
9161             this.inputEl().dom.select();
9162         }
9163     },
9164     filterKeys : function(e){
9165         var k = e.getKey();
9166         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9167             return;
9168         }
9169         var c = e.getCharCode(), cc = String.fromCharCode(c);
9170         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9171             return;
9172         }
9173         if(!this.maskRe.test(cc)){
9174             e.stopEvent();
9175         }
9176     },
9177      /**
9178      * Clear any invalid styles/messages for this field
9179      */
9180     clearInvalid : function(){
9181         
9182         if(!this.el || this.preventMark){ // not rendered
9183             return;
9184         }
9185         
9186      
9187         this.el.removeClass(this.invalidClass);
9188         
9189         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9190             
9191             var feedback = this.el.select('.form-control-feedback', true).first();
9192             
9193             if(feedback){
9194                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9195             }
9196             
9197         }
9198         
9199         this.fireEvent('valid', this);
9200     },
9201     
9202      /**
9203      * Mark this field as valid
9204      */
9205     markValid : function()
9206     {
9207         if(!this.el  || this.preventMark){ // not rendered...
9208             return;
9209         }
9210         
9211         this.el.removeClass([this.invalidClass, this.validClass]);
9212         
9213         var feedback = this.el.select('.form-control-feedback', true).first();
9214             
9215         if(feedback){
9216             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9217         }
9218
9219         if(this.disabled){
9220             return;
9221         }
9222         
9223         if(this.allowBlank && !this.getRawValue().length){
9224             return;
9225         }
9226         
9227         if(this.indicator){
9228             this.indicator.hide();
9229         }
9230         
9231         this.el.addClass(this.validClass);
9232         
9233         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9234             
9235             var feedback = this.el.select('.form-control-feedback', true).first();
9236             
9237             if(feedback){
9238                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9239                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9240             }
9241             
9242         }
9243         
9244         this.fireEvent('valid', this);
9245     },
9246     
9247      /**
9248      * Mark this field as invalid
9249      * @param {String} msg The validation message
9250      */
9251     markInvalid : function(msg)
9252     {
9253         if(!this.el  || this.preventMark){ // not rendered
9254             return;
9255         }
9256         
9257         this.el.removeClass([this.invalidClass, this.validClass]);
9258         
9259         var feedback = this.el.select('.form-control-feedback', true).first();
9260             
9261         if(feedback){
9262             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9263         }
9264
9265         if(this.disabled){
9266             return;
9267         }
9268         
9269         if(this.allowBlank && !this.getRawValue().length){
9270             return;
9271         }
9272         
9273         if(this.indicator){
9274             this.indicator.show();
9275         }
9276         
9277         this.el.addClass(this.invalidClass);
9278         
9279         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9280             
9281             var feedback = this.el.select('.form-control-feedback', true).first();
9282             
9283             if(feedback){
9284                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9285                 
9286                 if(this.getValue().length || this.forceFeedback){
9287                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9288                 }
9289                 
9290             }
9291             
9292         }
9293         
9294         this.fireEvent('invalid', this, msg);
9295     },
9296     // private
9297     SafariOnKeyDown : function(event)
9298     {
9299         // this is a workaround for a password hang bug on chrome/ webkit.
9300         if (this.inputEl().dom.type != 'password') {
9301             return;
9302         }
9303         
9304         var isSelectAll = false;
9305         
9306         if(this.inputEl().dom.selectionEnd > 0){
9307             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9308         }
9309         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9310             event.preventDefault();
9311             this.setValue('');
9312             return;
9313         }
9314         
9315         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9316             
9317             event.preventDefault();
9318             // this is very hacky as keydown always get's upper case.
9319             //
9320             var cc = String.fromCharCode(event.getCharCode());
9321             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9322             
9323         }
9324     },
9325     adjustWidth : function(tag, w){
9326         tag = tag.toLowerCase();
9327         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9328             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9329                 if(tag == 'input'){
9330                     return w + 2;
9331                 }
9332                 if(tag == 'textarea'){
9333                     return w-2;
9334                 }
9335             }else if(Roo.isOpera){
9336                 if(tag == 'input'){
9337                     return w + 2;
9338                 }
9339                 if(tag == 'textarea'){
9340                     return w-2;
9341                 }
9342             }
9343         }
9344         return w;
9345     },
9346     
9347     setFieldLabel : function(v)
9348     {
9349         this.fieldLabel = v;
9350         
9351         if(this.rendered){
9352             this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9353         }
9354     }
9355 });
9356
9357  
9358 /*
9359  * - LGPL
9360  *
9361  * Input
9362  * 
9363  */
9364
9365 /**
9366  * @class Roo.bootstrap.TextArea
9367  * @extends Roo.bootstrap.Input
9368  * Bootstrap TextArea class
9369  * @cfg {Number} cols Specifies the visible width of a text area
9370  * @cfg {Number} rows Specifies the visible number of lines in a text area
9371  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9372  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9373  * @cfg {string} html text
9374  * 
9375  * @constructor
9376  * Create a new TextArea
9377  * @param {Object} config The config object
9378  */
9379
9380 Roo.bootstrap.TextArea = function(config){
9381     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9382    
9383 };
9384
9385 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9386      
9387     cols : false,
9388     rows : 5,
9389     readOnly : false,
9390     warp : 'soft',
9391     resize : false,
9392     value: false,
9393     html: false,
9394     
9395     getAutoCreate : function(){
9396         
9397         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9398         
9399         var id = Roo.id();
9400         
9401         var cfg = {};
9402         
9403         if(this.inputType != 'hidden'){
9404             cfg.cls = 'form-group' //input-group
9405         }
9406         
9407         var input =  {
9408             tag: 'textarea',
9409             id : id,
9410             warp : this.warp,
9411             rows : this.rows,
9412             value : this.value || '',
9413             html: this.html || '',
9414             cls : 'form-control',
9415             placeholder : this.placeholder || '' 
9416             
9417         };
9418         
9419         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9420             input.maxLength = this.maxLength;
9421         }
9422         
9423         if(this.resize){
9424             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9425         }
9426         
9427         if(this.cols){
9428             input.cols = this.cols;
9429         }
9430         
9431         if (this.readOnly) {
9432             input.readonly = true;
9433         }
9434         
9435         if (this.name) {
9436             input.name = this.name;
9437         }
9438         
9439         if (this.size) {
9440             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9441         }
9442         
9443         var settings=this;
9444         ['xs','sm','md','lg'].map(function(size){
9445             if (settings[size]) {
9446                 cfg.cls += ' col-' + size + '-' + settings[size];
9447             }
9448         });
9449         
9450         var inputblock = input;
9451         
9452         if(this.hasFeedback && !this.allowBlank){
9453             
9454             var feedback = {
9455                 tag: 'span',
9456                 cls: 'glyphicon form-control-feedback'
9457             };
9458
9459             inputblock = {
9460                 cls : 'has-feedback',
9461                 cn :  [
9462                     input,
9463                     feedback
9464                 ] 
9465             };  
9466         }
9467         
9468         
9469         if (this.before || this.after) {
9470             
9471             inputblock = {
9472                 cls : 'input-group',
9473                 cn :  [] 
9474             };
9475             if (this.before) {
9476                 inputblock.cn.push({
9477                     tag :'span',
9478                     cls : 'input-group-addon',
9479                     html : this.before
9480                 });
9481             }
9482             
9483             inputblock.cn.push(input);
9484             
9485             if(this.hasFeedback && !this.allowBlank){
9486                 inputblock.cls += ' has-feedback';
9487                 inputblock.cn.push(feedback);
9488             }
9489             
9490             if (this.after) {
9491                 inputblock.cn.push({
9492                     tag :'span',
9493                     cls : 'input-group-addon',
9494                     html : this.after
9495                 });
9496             }
9497             
9498         }
9499         
9500         if (align ==='left' && this.fieldLabel.length) {
9501             cfg.cn = [
9502                 {
9503                     tag: 'label',
9504                     'for' :  id,
9505                     cls : 'control-label',
9506                     html : this.fieldLabel
9507                 },
9508                 {
9509                     cls : "",
9510                     cn: [
9511                         inputblock
9512                     ]
9513                 }
9514
9515             ];
9516             
9517             if(this.labelWidth > 12){
9518                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9519             }
9520
9521             if(this.labelWidth < 13 && this.labelmd == 0){
9522                 this.labelmd = this.labelWidth;
9523             }
9524
9525             if(this.labellg > 0){
9526                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9527                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9528             }
9529
9530             if(this.labelmd > 0){
9531                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9532                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9533             }
9534
9535             if(this.labelsm > 0){
9536                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9537                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9538             }
9539
9540             if(this.labelxs > 0){
9541                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9542                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9543             }
9544             
9545         } else if ( this.fieldLabel.length) {
9546             cfg.cn = [
9547
9548                {
9549                    tag: 'label',
9550                    //cls : 'input-group-addon',
9551                    html : this.fieldLabel
9552
9553                },
9554
9555                inputblock
9556
9557            ];
9558
9559         } else {
9560
9561             cfg.cn = [
9562
9563                 inputblock
9564
9565             ];
9566                 
9567         }
9568         
9569         if (this.disabled) {
9570             input.disabled=true;
9571         }
9572         
9573         return cfg;
9574         
9575     },
9576     /**
9577      * return the real textarea element.
9578      */
9579     inputEl: function ()
9580     {
9581         return this.el.select('textarea.form-control',true).first();
9582     },
9583     
9584     /**
9585      * Clear any invalid styles/messages for this field
9586      */
9587     clearInvalid : function()
9588     {
9589         
9590         if(!this.el || this.preventMark){ // not rendered
9591             return;
9592         }
9593         
9594         var label = this.el.select('label', true).first();
9595         var icon = this.el.select('i.fa-star', true).first();
9596         
9597         if(label && icon){
9598             icon.remove();
9599         }
9600         
9601         this.el.removeClass(this.invalidClass);
9602         
9603         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9604             
9605             var feedback = this.el.select('.form-control-feedback', true).first();
9606             
9607             if(feedback){
9608                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9609             }
9610             
9611         }
9612         
9613         this.fireEvent('valid', this);
9614     },
9615     
9616      /**
9617      * Mark this field as valid
9618      */
9619     markValid : function()
9620     {
9621         if(!this.el  || this.preventMark){ // not rendered
9622             return;
9623         }
9624         
9625         this.el.removeClass([this.invalidClass, this.validClass]);
9626         
9627         var feedback = this.el.select('.form-control-feedback', true).first();
9628             
9629         if(feedback){
9630             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9631         }
9632
9633         if(this.disabled || this.allowBlank){
9634             return;
9635         }
9636         
9637         var label = this.el.select('label', true).first();
9638         var icon = this.el.select('i.fa-star', true).first();
9639         
9640         if(label && icon){
9641             icon.remove();
9642         }
9643         
9644         this.el.addClass(this.validClass);
9645         
9646         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9647             
9648             var feedback = this.el.select('.form-control-feedback', true).first();
9649             
9650             if(feedback){
9651                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9652                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9653             }
9654             
9655         }
9656         
9657         this.fireEvent('valid', this);
9658     },
9659     
9660      /**
9661      * Mark this field as invalid
9662      * @param {String} msg The validation message
9663      */
9664     markInvalid : function(msg)
9665     {
9666         if(!this.el  || this.preventMark){ // not rendered
9667             return;
9668         }
9669         
9670         this.el.removeClass([this.invalidClass, this.validClass]);
9671         
9672         var feedback = this.el.select('.form-control-feedback', true).first();
9673             
9674         if(feedback){
9675             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9676         }
9677
9678         if(this.disabled || this.allowBlank){
9679             return;
9680         }
9681         
9682         var label = this.el.select('label', true).first();
9683         var icon = this.el.select('i.fa-star', true).first();
9684         
9685         if(!this.getValue().length && label && !icon){
9686             this.el.createChild({
9687                 tag : 'i',
9688                 cls : 'text-danger fa fa-lg fa-star',
9689                 tooltip : 'This field is required',
9690                 style : 'margin-right:5px;'
9691             }, label, true);
9692         }
9693
9694         this.el.addClass(this.invalidClass);
9695         
9696         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9697             
9698             var feedback = this.el.select('.form-control-feedback', true).first();
9699             
9700             if(feedback){
9701                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9702                 
9703                 if(this.getValue().length || this.forceFeedback){
9704                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9705                 }
9706                 
9707             }
9708             
9709         }
9710         
9711         this.fireEvent('invalid', this, msg);
9712     }
9713 });
9714
9715  
9716 /*
9717  * - LGPL
9718  *
9719  * trigger field - base class for combo..
9720  * 
9721  */
9722  
9723 /**
9724  * @class Roo.bootstrap.TriggerField
9725  * @extends Roo.bootstrap.Input
9726  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9727  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9728  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9729  * for which you can provide a custom implementation.  For example:
9730  * <pre><code>
9731 var trigger = new Roo.bootstrap.TriggerField();
9732 trigger.onTriggerClick = myTriggerFn;
9733 trigger.applyTo('my-field');
9734 </code></pre>
9735  *
9736  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9737  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9738  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9739  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9740  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9741
9742  * @constructor
9743  * Create a new TriggerField.
9744  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9745  * to the base TextField)
9746  */
9747 Roo.bootstrap.TriggerField = function(config){
9748     this.mimicing = false;
9749     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9750 };
9751
9752 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9753     /**
9754      * @cfg {String} triggerClass A CSS class to apply to the trigger
9755      */
9756      /**
9757      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9758      */
9759     hideTrigger:false,
9760
9761     /**
9762      * @cfg {Boolean} removable (true|false) special filter default false
9763      */
9764     removable : false,
9765     
9766     /** @cfg {Boolean} grow @hide */
9767     /** @cfg {Number} growMin @hide */
9768     /** @cfg {Number} growMax @hide */
9769
9770     /**
9771      * @hide 
9772      * @method
9773      */
9774     autoSize: Roo.emptyFn,
9775     // private
9776     monitorTab : true,
9777     // private
9778     deferHeight : true,
9779
9780     
9781     actionMode : 'wrap',
9782     
9783     caret : false,
9784     
9785     
9786     getAutoCreate : function(){
9787        
9788         var align = this.labelAlign || this.parentLabelAlign();
9789         
9790         var id = Roo.id();
9791         
9792         var cfg = {
9793             cls: 'form-group' //input-group
9794         };
9795         
9796         
9797         var input =  {
9798             tag: 'input',
9799             id : id,
9800             type : this.inputType,
9801             cls : 'form-control',
9802             autocomplete: 'new-password',
9803             placeholder : this.placeholder || '' 
9804             
9805         };
9806         if (this.name) {
9807             input.name = this.name;
9808         }
9809         if (this.size) {
9810             input.cls += ' input-' + this.size;
9811         }
9812         
9813         if (this.disabled) {
9814             input.disabled=true;
9815         }
9816         
9817         var inputblock = input;
9818         
9819         if(this.hasFeedback && !this.allowBlank){
9820             
9821             var feedback = {
9822                 tag: 'span',
9823                 cls: 'glyphicon form-control-feedback'
9824             };
9825             
9826             if(this.removable && !this.editable && !this.tickable){
9827                 inputblock = {
9828                     cls : 'has-feedback',
9829                     cn :  [
9830                         inputblock,
9831                         {
9832                             tag: 'button',
9833                             html : 'x',
9834                             cls : 'roo-combo-removable-btn close'
9835                         },
9836                         feedback
9837                     ] 
9838                 };
9839             } else {
9840                 inputblock = {
9841                     cls : 'has-feedback',
9842                     cn :  [
9843                         inputblock,
9844                         feedback
9845                     ] 
9846                 };
9847             }
9848
9849         } else {
9850             if(this.removable && !this.editable && !this.tickable){
9851                 inputblock = {
9852                     cls : 'roo-removable',
9853                     cn :  [
9854                         inputblock,
9855                         {
9856                             tag: 'button',
9857                             html : 'x',
9858                             cls : 'roo-combo-removable-btn close'
9859                         }
9860                     ] 
9861                 };
9862             }
9863         }
9864         
9865         if (this.before || this.after) {
9866             
9867             inputblock = {
9868                 cls : 'input-group',
9869                 cn :  [] 
9870             };
9871             if (this.before) {
9872                 inputblock.cn.push({
9873                     tag :'span',
9874                     cls : 'input-group-addon',
9875                     html : this.before
9876                 });
9877             }
9878             
9879             inputblock.cn.push(input);
9880             
9881             if(this.hasFeedback && !this.allowBlank){
9882                 inputblock.cls += ' has-feedback';
9883                 inputblock.cn.push(feedback);
9884             }
9885             
9886             if (this.after) {
9887                 inputblock.cn.push({
9888                     tag :'span',
9889                     cls : 'input-group-addon',
9890                     html : this.after
9891                 });
9892             }
9893             
9894         };
9895         
9896         var box = {
9897             tag: 'div',
9898             cn: [
9899                 {
9900                     tag: 'input',
9901                     type : 'hidden',
9902                     cls: 'form-hidden-field'
9903                 },
9904                 inputblock
9905             ]
9906             
9907         };
9908         
9909         if(this.multiple){
9910             box = {
9911                 tag: 'div',
9912                 cn: [
9913                     {
9914                         tag: 'input',
9915                         type : 'hidden',
9916                         cls: 'form-hidden-field'
9917                     },
9918                     {
9919                         tag: 'ul',
9920                         cls: 'roo-select2-choices',
9921                         cn:[
9922                             {
9923                                 tag: 'li',
9924                                 cls: 'roo-select2-search-field',
9925                                 cn: [
9926
9927                                     inputblock
9928                                 ]
9929                             }
9930                         ]
9931                     }
9932                 ]
9933             }
9934         };
9935         
9936         var combobox = {
9937             cls: 'roo-select2-container input-group',
9938             cn: [
9939                 box
9940 //                {
9941 //                    tag: 'ul',
9942 //                    cls: 'typeahead typeahead-long dropdown-menu',
9943 //                    style: 'display:none'
9944 //                }
9945             ]
9946         };
9947         
9948         if(!this.multiple && this.showToggleBtn){
9949             
9950             var caret = {
9951                         tag: 'span',
9952                         cls: 'caret'
9953              };
9954             if (this.caret != false) {
9955                 caret = {
9956                      tag: 'i',
9957                      cls: 'fa fa-' + this.caret
9958                 };
9959                 
9960             }
9961             
9962             combobox.cn.push({
9963                 tag :'span',
9964                 cls : 'input-group-addon btn dropdown-toggle',
9965                 cn : [
9966                     caret,
9967                     {
9968                         tag: 'span',
9969                         cls: 'combobox-clear',
9970                         cn  : [
9971                             {
9972                                 tag : 'i',
9973                                 cls: 'icon-remove'
9974                             }
9975                         ]
9976                     }
9977                 ]
9978
9979             })
9980         }
9981         
9982         if(this.multiple){
9983             combobox.cls += ' roo-select2-container-multi';
9984         }
9985         
9986         if (align ==='left' && this.fieldLabel.length) {
9987             
9988             cfg.cls += ' roo-form-group-label-left';
9989
9990             cfg.cn = [
9991                 {
9992                     tag : 'i',
9993                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9994                     tooltip : 'This field is required'
9995                 },
9996                 {
9997                     tag: 'label',
9998                     'for' :  id,
9999                     cls : 'control-label',
10000                     html : this.fieldLabel
10001
10002                 },
10003                 {
10004                     cls : "", 
10005                     cn: [
10006                         combobox
10007                     ]
10008                 }
10009
10010             ];
10011             
10012             var labelCfg = cfg.cn[1];
10013             var contentCfg = cfg.cn[2];
10014             
10015             if(this.indicatorpos == 'right'){
10016                 cfg.cn = [
10017                     {
10018                         tag: 'label',
10019                         'for' :  id,
10020                         cls : 'control-label',
10021                         cn : [
10022                             {
10023                                 tag : 'span',
10024                                 html : this.fieldLabel
10025                             },
10026                             {
10027                                 tag : 'i',
10028                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10029                                 tooltip : 'This field is required'
10030                             }
10031                         ]
10032                     },
10033                     {
10034                         cls : "", 
10035                         cn: [
10036                             combobox
10037                         ]
10038                     }
10039
10040                 ];
10041                 
10042                 labelCfg = cfg.cn[0];
10043                 contentCfg = cfg.cn[1];
10044             }
10045             
10046             if(this.labelWidth > 12){
10047                 labelCfg.style = "width: " + this.labelWidth + 'px';
10048             }
10049             
10050             if(this.labelWidth < 13 && this.labelmd == 0){
10051                 this.labelmd = this.labelWidth;
10052             }
10053             
10054             if(this.labellg > 0){
10055                 labelCfg.cls += ' col-lg-' + this.labellg;
10056                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10057             }
10058             
10059             if(this.labelmd > 0){
10060                 labelCfg.cls += ' col-md-' + this.labelmd;
10061                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10062             }
10063             
10064             if(this.labelsm > 0){
10065                 labelCfg.cls += ' col-sm-' + this.labelsm;
10066                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10067             }
10068             
10069             if(this.labelxs > 0){
10070                 labelCfg.cls += ' col-xs-' + this.labelxs;
10071                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10072             }
10073             
10074         } else if ( this.fieldLabel.length) {
10075 //                Roo.log(" label");
10076             cfg.cn = [
10077                 {
10078                    tag : 'i',
10079                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10080                    tooltip : 'This field is required'
10081                },
10082                {
10083                    tag: 'label',
10084                    //cls : 'input-group-addon',
10085                    html : this.fieldLabel
10086
10087                },
10088
10089                combobox
10090
10091             ];
10092             
10093             if(this.indicatorpos == 'right'){
10094                 
10095                 cfg.cn = [
10096                     {
10097                        tag: 'label',
10098                        cn : [
10099                            {
10100                                tag : 'span',
10101                                html : this.fieldLabel
10102                            },
10103                            {
10104                               tag : 'i',
10105                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10106                               tooltip : 'This field is required'
10107                            }
10108                        ]
10109
10110                     },
10111                     combobox
10112
10113                 ];
10114
10115             }
10116
10117         } else {
10118             
10119 //                Roo.log(" no label && no align");
10120                 cfg = combobox
10121                      
10122                 
10123         }
10124         
10125         var settings=this;
10126         ['xs','sm','md','lg'].map(function(size){
10127             if (settings[size]) {
10128                 cfg.cls += ' col-' + size + '-' + settings[size];
10129             }
10130         });
10131         
10132         return cfg;
10133         
10134     },
10135     
10136     
10137     
10138     // private
10139     onResize : function(w, h){
10140 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10141 //        if(typeof w == 'number'){
10142 //            var x = w - this.trigger.getWidth();
10143 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10144 //            this.trigger.setStyle('left', x+'px');
10145 //        }
10146     },
10147
10148     // private
10149     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10150
10151     // private
10152     getResizeEl : function(){
10153         return this.inputEl();
10154     },
10155
10156     // private
10157     getPositionEl : function(){
10158         return this.inputEl();
10159     },
10160
10161     // private
10162     alignErrorIcon : function(){
10163         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10164     },
10165
10166     // private
10167     initEvents : function(){
10168         
10169         this.createList();
10170         
10171         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10172         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10173         if(!this.multiple && this.showToggleBtn){
10174             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10175             if(this.hideTrigger){
10176                 this.trigger.setDisplayed(false);
10177             }
10178             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10179         }
10180         
10181         if(this.multiple){
10182             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10183         }
10184         
10185         if(this.removable && !this.editable && !this.tickable){
10186             var close = this.closeTriggerEl();
10187             
10188             if(close){
10189                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10190                 close.on('click', this.removeBtnClick, this, close);
10191             }
10192         }
10193         
10194         //this.trigger.addClassOnOver('x-form-trigger-over');
10195         //this.trigger.addClassOnClick('x-form-trigger-click');
10196         
10197         //if(!this.width){
10198         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10199         //}
10200     },
10201     
10202     closeTriggerEl : function()
10203     {
10204         var close = this.el.select('.roo-combo-removable-btn', true).first();
10205         return close ? close : false;
10206     },
10207     
10208     removeBtnClick : function(e, h, el)
10209     {
10210         e.preventDefault();
10211         
10212         if(this.fireEvent("remove", this) !== false){
10213             this.reset();
10214             this.fireEvent("afterremove", this)
10215         }
10216     },
10217     
10218     createList : function()
10219     {
10220         this.list = Roo.get(document.body).createChild({
10221             tag: 'ul',
10222             cls: 'typeahead typeahead-long dropdown-menu',
10223             style: 'display:none'
10224         });
10225         
10226         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10227         
10228     },
10229
10230     // private
10231     initTrigger : function(){
10232        
10233     },
10234
10235     // private
10236     onDestroy : function(){
10237         if(this.trigger){
10238             this.trigger.removeAllListeners();
10239           //  this.trigger.remove();
10240         }
10241         //if(this.wrap){
10242         //    this.wrap.remove();
10243         //}
10244         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10245     },
10246
10247     // private
10248     onFocus : function(){
10249         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10250         /*
10251         if(!this.mimicing){
10252             this.wrap.addClass('x-trigger-wrap-focus');
10253             this.mimicing = true;
10254             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10255             if(this.monitorTab){
10256                 this.el.on("keydown", this.checkTab, this);
10257             }
10258         }
10259         */
10260     },
10261
10262     // private
10263     checkTab : function(e){
10264         if(e.getKey() == e.TAB){
10265             this.triggerBlur();
10266         }
10267     },
10268
10269     // private
10270     onBlur : function(){
10271         // do nothing
10272     },
10273
10274     // private
10275     mimicBlur : function(e, t){
10276         /*
10277         if(!this.wrap.contains(t) && this.validateBlur()){
10278             this.triggerBlur();
10279         }
10280         */
10281     },
10282
10283     // private
10284     triggerBlur : function(){
10285         this.mimicing = false;
10286         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10287         if(this.monitorTab){
10288             this.el.un("keydown", this.checkTab, this);
10289         }
10290         //this.wrap.removeClass('x-trigger-wrap-focus');
10291         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10292     },
10293
10294     // private
10295     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10296     validateBlur : function(e, t){
10297         return true;
10298     },
10299
10300     // private
10301     onDisable : function(){
10302         this.inputEl().dom.disabled = true;
10303         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10304         //if(this.wrap){
10305         //    this.wrap.addClass('x-item-disabled');
10306         //}
10307     },
10308
10309     // private
10310     onEnable : function(){
10311         this.inputEl().dom.disabled = false;
10312         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10313         //if(this.wrap){
10314         //    this.el.removeClass('x-item-disabled');
10315         //}
10316     },
10317
10318     // private
10319     onShow : function(){
10320         var ae = this.getActionEl();
10321         
10322         if(ae){
10323             ae.dom.style.display = '';
10324             ae.dom.style.visibility = 'visible';
10325         }
10326     },
10327
10328     // private
10329     
10330     onHide : function(){
10331         var ae = this.getActionEl();
10332         ae.dom.style.display = 'none';
10333     },
10334
10335     /**
10336      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10337      * by an implementing function.
10338      * @method
10339      * @param {EventObject} e
10340      */
10341     onTriggerClick : Roo.emptyFn
10342 });
10343  /*
10344  * Based on:
10345  * Ext JS Library 1.1.1
10346  * Copyright(c) 2006-2007, Ext JS, LLC.
10347  *
10348  * Originally Released Under LGPL - original licence link has changed is not relivant.
10349  *
10350  * Fork - LGPL
10351  * <script type="text/javascript">
10352  */
10353
10354
10355 /**
10356  * @class Roo.data.SortTypes
10357  * @singleton
10358  * Defines the default sorting (casting?) comparison functions used when sorting data.
10359  */
10360 Roo.data.SortTypes = {
10361     /**
10362      * Default sort that does nothing
10363      * @param {Mixed} s The value being converted
10364      * @return {Mixed} The comparison value
10365      */
10366     none : function(s){
10367         return s;
10368     },
10369     
10370     /**
10371      * The regular expression used to strip tags
10372      * @type {RegExp}
10373      * @property
10374      */
10375     stripTagsRE : /<\/?[^>]+>/gi,
10376     
10377     /**
10378      * Strips all HTML tags to sort on text only
10379      * @param {Mixed} s The value being converted
10380      * @return {String} The comparison value
10381      */
10382     asText : function(s){
10383         return String(s).replace(this.stripTagsRE, "");
10384     },
10385     
10386     /**
10387      * Strips all HTML tags to sort on text only - Case insensitive
10388      * @param {Mixed} s The value being converted
10389      * @return {String} The comparison value
10390      */
10391     asUCText : function(s){
10392         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10393     },
10394     
10395     /**
10396      * Case insensitive string
10397      * @param {Mixed} s The value being converted
10398      * @return {String} The comparison value
10399      */
10400     asUCString : function(s) {
10401         return String(s).toUpperCase();
10402     },
10403     
10404     /**
10405      * Date sorting
10406      * @param {Mixed} s The value being converted
10407      * @return {Number} The comparison value
10408      */
10409     asDate : function(s) {
10410         if(!s){
10411             return 0;
10412         }
10413         if(s instanceof Date){
10414             return s.getTime();
10415         }
10416         return Date.parse(String(s));
10417     },
10418     
10419     /**
10420      * Float sorting
10421      * @param {Mixed} s The value being converted
10422      * @return {Float} The comparison value
10423      */
10424     asFloat : function(s) {
10425         var val = parseFloat(String(s).replace(/,/g, ""));
10426         if(isNaN(val)) {
10427             val = 0;
10428         }
10429         return val;
10430     },
10431     
10432     /**
10433      * Integer sorting
10434      * @param {Mixed} s The value being converted
10435      * @return {Number} The comparison value
10436      */
10437     asInt : function(s) {
10438         var val = parseInt(String(s).replace(/,/g, ""));
10439         if(isNaN(val)) {
10440             val = 0;
10441         }
10442         return val;
10443     }
10444 };/*
10445  * Based on:
10446  * Ext JS Library 1.1.1
10447  * Copyright(c) 2006-2007, Ext JS, LLC.
10448  *
10449  * Originally Released Under LGPL - original licence link has changed is not relivant.
10450  *
10451  * Fork - LGPL
10452  * <script type="text/javascript">
10453  */
10454
10455 /**
10456 * @class Roo.data.Record
10457  * Instances of this class encapsulate both record <em>definition</em> information, and record
10458  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10459  * to access Records cached in an {@link Roo.data.Store} object.<br>
10460  * <p>
10461  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10462  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10463  * objects.<br>
10464  * <p>
10465  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10466  * @constructor
10467  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10468  * {@link #create}. The parameters are the same.
10469  * @param {Array} data An associative Array of data values keyed by the field name.
10470  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10471  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10472  * not specified an integer id is generated.
10473  */
10474 Roo.data.Record = function(data, id){
10475     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10476     this.data = data;
10477 };
10478
10479 /**
10480  * Generate a constructor for a specific record layout.
10481  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10482  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10483  * Each field definition object may contain the following properties: <ul>
10484  * <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,
10485  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10486  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10487  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10488  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10489  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10490  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10491  * this may be omitted.</p></li>
10492  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10493  * <ul><li>auto (Default, implies no conversion)</li>
10494  * <li>string</li>
10495  * <li>int</li>
10496  * <li>float</li>
10497  * <li>boolean</li>
10498  * <li>date</li></ul></p></li>
10499  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10500  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10501  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10502  * by the Reader into an object that will be stored in the Record. It is passed the
10503  * following parameters:<ul>
10504  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10505  * </ul></p></li>
10506  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10507  * </ul>
10508  * <br>usage:<br><pre><code>
10509 var TopicRecord = Roo.data.Record.create(
10510     {name: 'title', mapping: 'topic_title'},
10511     {name: 'author', mapping: 'username'},
10512     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10513     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10514     {name: 'lastPoster', mapping: 'user2'},
10515     {name: 'excerpt', mapping: 'post_text'}
10516 );
10517
10518 var myNewRecord = new TopicRecord({
10519     title: 'Do my job please',
10520     author: 'noobie',
10521     totalPosts: 1,
10522     lastPost: new Date(),
10523     lastPoster: 'Animal',
10524     excerpt: 'No way dude!'
10525 });
10526 myStore.add(myNewRecord);
10527 </code></pre>
10528  * @method create
10529  * @static
10530  */
10531 Roo.data.Record.create = function(o){
10532     var f = function(){
10533         f.superclass.constructor.apply(this, arguments);
10534     };
10535     Roo.extend(f, Roo.data.Record);
10536     var p = f.prototype;
10537     p.fields = new Roo.util.MixedCollection(false, function(field){
10538         return field.name;
10539     });
10540     for(var i = 0, len = o.length; i < len; i++){
10541         p.fields.add(new Roo.data.Field(o[i]));
10542     }
10543     f.getField = function(name){
10544         return p.fields.get(name);  
10545     };
10546     return f;
10547 };
10548
10549 Roo.data.Record.AUTO_ID = 1000;
10550 Roo.data.Record.EDIT = 'edit';
10551 Roo.data.Record.REJECT = 'reject';
10552 Roo.data.Record.COMMIT = 'commit';
10553
10554 Roo.data.Record.prototype = {
10555     /**
10556      * Readonly flag - true if this record has been modified.
10557      * @type Boolean
10558      */
10559     dirty : false,
10560     editing : false,
10561     error: null,
10562     modified: null,
10563
10564     // private
10565     join : function(store){
10566         this.store = store;
10567     },
10568
10569     /**
10570      * Set the named field to the specified value.
10571      * @param {String} name The name of the field to set.
10572      * @param {Object} value The value to set the field to.
10573      */
10574     set : function(name, value){
10575         if(this.data[name] == value){
10576             return;
10577         }
10578         this.dirty = true;
10579         if(!this.modified){
10580             this.modified = {};
10581         }
10582         if(typeof this.modified[name] == 'undefined'){
10583             this.modified[name] = this.data[name];
10584         }
10585         this.data[name] = value;
10586         if(!this.editing && this.store){
10587             this.store.afterEdit(this);
10588         }       
10589     },
10590
10591     /**
10592      * Get the value of the named field.
10593      * @param {String} name The name of the field to get the value of.
10594      * @return {Object} The value of the field.
10595      */
10596     get : function(name){
10597         return this.data[name]; 
10598     },
10599
10600     // private
10601     beginEdit : function(){
10602         this.editing = true;
10603         this.modified = {}; 
10604     },
10605
10606     // private
10607     cancelEdit : function(){
10608         this.editing = false;
10609         delete this.modified;
10610     },
10611
10612     // private
10613     endEdit : function(){
10614         this.editing = false;
10615         if(this.dirty && this.store){
10616             this.store.afterEdit(this);
10617         }
10618     },
10619
10620     /**
10621      * Usually called by the {@link Roo.data.Store} which owns the Record.
10622      * Rejects all changes made to the Record since either creation, or the last commit operation.
10623      * Modified fields are reverted to their original values.
10624      * <p>
10625      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10626      * of reject operations.
10627      */
10628     reject : function(){
10629         var m = this.modified;
10630         for(var n in m){
10631             if(typeof m[n] != "function"){
10632                 this.data[n] = m[n];
10633             }
10634         }
10635         this.dirty = false;
10636         delete this.modified;
10637         this.editing = false;
10638         if(this.store){
10639             this.store.afterReject(this);
10640         }
10641     },
10642
10643     /**
10644      * Usually called by the {@link Roo.data.Store} which owns the Record.
10645      * Commits all changes made to the Record since either creation, or the last commit operation.
10646      * <p>
10647      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10648      * of commit operations.
10649      */
10650     commit : function(){
10651         this.dirty = false;
10652         delete this.modified;
10653         this.editing = false;
10654         if(this.store){
10655             this.store.afterCommit(this);
10656         }
10657     },
10658
10659     // private
10660     hasError : function(){
10661         return this.error != null;
10662     },
10663
10664     // private
10665     clearError : function(){
10666         this.error = null;
10667     },
10668
10669     /**
10670      * Creates a copy of this record.
10671      * @param {String} id (optional) A new record id if you don't want to use this record's id
10672      * @return {Record}
10673      */
10674     copy : function(newId) {
10675         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10676     }
10677 };/*
10678  * Based on:
10679  * Ext JS Library 1.1.1
10680  * Copyright(c) 2006-2007, Ext JS, LLC.
10681  *
10682  * Originally Released Under LGPL - original licence link has changed is not relivant.
10683  *
10684  * Fork - LGPL
10685  * <script type="text/javascript">
10686  */
10687
10688
10689
10690 /**
10691  * @class Roo.data.Store
10692  * @extends Roo.util.Observable
10693  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10694  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10695  * <p>
10696  * 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
10697  * has no knowledge of the format of the data returned by the Proxy.<br>
10698  * <p>
10699  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10700  * instances from the data object. These records are cached and made available through accessor functions.
10701  * @constructor
10702  * Creates a new Store.
10703  * @param {Object} config A config object containing the objects needed for the Store to access data,
10704  * and read the data into Records.
10705  */
10706 Roo.data.Store = function(config){
10707     this.data = new Roo.util.MixedCollection(false);
10708     this.data.getKey = function(o){
10709         return o.id;
10710     };
10711     this.baseParams = {};
10712     // private
10713     this.paramNames = {
10714         "start" : "start",
10715         "limit" : "limit",
10716         "sort" : "sort",
10717         "dir" : "dir",
10718         "multisort" : "_multisort"
10719     };
10720
10721     if(config && config.data){
10722         this.inlineData = config.data;
10723         delete config.data;
10724     }
10725
10726     Roo.apply(this, config);
10727     
10728     if(this.reader){ // reader passed
10729         this.reader = Roo.factory(this.reader, Roo.data);
10730         this.reader.xmodule = this.xmodule || false;
10731         if(!this.recordType){
10732             this.recordType = this.reader.recordType;
10733         }
10734         if(this.reader.onMetaChange){
10735             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10736         }
10737     }
10738
10739     if(this.recordType){
10740         this.fields = this.recordType.prototype.fields;
10741     }
10742     this.modified = [];
10743
10744     this.addEvents({
10745         /**
10746          * @event datachanged
10747          * Fires when the data cache has changed, and a widget which is using this Store
10748          * as a Record cache should refresh its view.
10749          * @param {Store} this
10750          */
10751         datachanged : true,
10752         /**
10753          * @event metachange
10754          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10755          * @param {Store} this
10756          * @param {Object} meta The JSON metadata
10757          */
10758         metachange : true,
10759         /**
10760          * @event add
10761          * Fires when Records have been added to the Store
10762          * @param {Store} this
10763          * @param {Roo.data.Record[]} records The array of Records added
10764          * @param {Number} index The index at which the record(s) were added
10765          */
10766         add : true,
10767         /**
10768          * @event remove
10769          * Fires when a Record has been removed from the Store
10770          * @param {Store} this
10771          * @param {Roo.data.Record} record The Record that was removed
10772          * @param {Number} index The index at which the record was removed
10773          */
10774         remove : true,
10775         /**
10776          * @event update
10777          * Fires when a Record has been updated
10778          * @param {Store} this
10779          * @param {Roo.data.Record} record The Record that was updated
10780          * @param {String} operation The update operation being performed.  Value may be one of:
10781          * <pre><code>
10782  Roo.data.Record.EDIT
10783  Roo.data.Record.REJECT
10784  Roo.data.Record.COMMIT
10785          * </code></pre>
10786          */
10787         update : true,
10788         /**
10789          * @event clear
10790          * Fires when the data cache has been cleared.
10791          * @param {Store} this
10792          */
10793         clear : true,
10794         /**
10795          * @event beforeload
10796          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10797          * the load action will be canceled.
10798          * @param {Store} this
10799          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10800          */
10801         beforeload : true,
10802         /**
10803          * @event beforeloadadd
10804          * Fires after a new set of Records has been loaded.
10805          * @param {Store} this
10806          * @param {Roo.data.Record[]} records The Records that were loaded
10807          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10808          */
10809         beforeloadadd : true,
10810         /**
10811          * @event load
10812          * Fires after a new set of Records has been loaded, before they are added to the store.
10813          * @param {Store} this
10814          * @param {Roo.data.Record[]} records The Records that were loaded
10815          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10816          * @params {Object} return from reader
10817          */
10818         load : true,
10819         /**
10820          * @event loadexception
10821          * Fires if an exception occurs in the Proxy during loading.
10822          * Called with the signature of the Proxy's "loadexception" event.
10823          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10824          * 
10825          * @param {Proxy} 
10826          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10827          * @param {Object} load options 
10828          * @param {Object} jsonData from your request (normally this contains the Exception)
10829          */
10830         loadexception : true
10831     });
10832     
10833     if(this.proxy){
10834         this.proxy = Roo.factory(this.proxy, Roo.data);
10835         this.proxy.xmodule = this.xmodule || false;
10836         this.relayEvents(this.proxy,  ["loadexception"]);
10837     }
10838     this.sortToggle = {};
10839     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10840
10841     Roo.data.Store.superclass.constructor.call(this);
10842
10843     if(this.inlineData){
10844         this.loadData(this.inlineData);
10845         delete this.inlineData;
10846     }
10847 };
10848
10849 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10850      /**
10851     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10852     * without a remote query - used by combo/forms at present.
10853     */
10854     
10855     /**
10856     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10857     */
10858     /**
10859     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10860     */
10861     /**
10862     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10863     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10864     */
10865     /**
10866     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10867     * on any HTTP request
10868     */
10869     /**
10870     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10871     */
10872     /**
10873     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10874     */
10875     multiSort: false,
10876     /**
10877     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10878     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10879     */
10880     remoteSort : false,
10881
10882     /**
10883     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10884      * loaded or when a record is removed. (defaults to false).
10885     */
10886     pruneModifiedRecords : false,
10887
10888     // private
10889     lastOptions : null,
10890
10891     /**
10892      * Add Records to the Store and fires the add event.
10893      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10894      */
10895     add : function(records){
10896         records = [].concat(records);
10897         for(var i = 0, len = records.length; i < len; i++){
10898             records[i].join(this);
10899         }
10900         var index = this.data.length;
10901         this.data.addAll(records);
10902         this.fireEvent("add", this, records, index);
10903     },
10904
10905     /**
10906      * Remove a Record from the Store and fires the remove event.
10907      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10908      */
10909     remove : function(record){
10910         var index = this.data.indexOf(record);
10911         this.data.removeAt(index);
10912         if(this.pruneModifiedRecords){
10913             this.modified.remove(record);
10914         }
10915         this.fireEvent("remove", this, record, index);
10916     },
10917
10918     /**
10919      * Remove all Records from the Store and fires the clear event.
10920      */
10921     removeAll : function(){
10922         this.data.clear();
10923         if(this.pruneModifiedRecords){
10924             this.modified = [];
10925         }
10926         this.fireEvent("clear", this);
10927     },
10928
10929     /**
10930      * Inserts Records to the Store at the given index and fires the add event.
10931      * @param {Number} index The start index at which to insert the passed Records.
10932      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10933      */
10934     insert : function(index, records){
10935         records = [].concat(records);
10936         for(var i = 0, len = records.length; i < len; i++){
10937             this.data.insert(index, records[i]);
10938             records[i].join(this);
10939         }
10940         this.fireEvent("add", this, records, index);
10941     },
10942
10943     /**
10944      * Get the index within the cache of the passed Record.
10945      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10946      * @return {Number} The index of the passed Record. Returns -1 if not found.
10947      */
10948     indexOf : function(record){
10949         return this.data.indexOf(record);
10950     },
10951
10952     /**
10953      * Get the index within the cache of the Record with the passed id.
10954      * @param {String} id The id of the Record to find.
10955      * @return {Number} The index of the Record. Returns -1 if not found.
10956      */
10957     indexOfId : function(id){
10958         return this.data.indexOfKey(id);
10959     },
10960
10961     /**
10962      * Get the Record with the specified id.
10963      * @param {String} id The id of the Record to find.
10964      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10965      */
10966     getById : function(id){
10967         return this.data.key(id);
10968     },
10969
10970     /**
10971      * Get the Record at the specified index.
10972      * @param {Number} index The index of the Record to find.
10973      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10974      */
10975     getAt : function(index){
10976         return this.data.itemAt(index);
10977     },
10978
10979     /**
10980      * Returns a range of Records between specified indices.
10981      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10982      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10983      * @return {Roo.data.Record[]} An array of Records
10984      */
10985     getRange : function(start, end){
10986         return this.data.getRange(start, end);
10987     },
10988
10989     // private
10990     storeOptions : function(o){
10991         o = Roo.apply({}, o);
10992         delete o.callback;
10993         delete o.scope;
10994         this.lastOptions = o;
10995     },
10996
10997     /**
10998      * Loads the Record cache from the configured Proxy using the configured Reader.
10999      * <p>
11000      * If using remote paging, then the first load call must specify the <em>start</em>
11001      * and <em>limit</em> properties in the options.params property to establish the initial
11002      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11003      * <p>
11004      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11005      * and this call will return before the new data has been loaded. Perform any post-processing
11006      * in a callback function, or in a "load" event handler.</strong>
11007      * <p>
11008      * @param {Object} options An object containing properties which control loading options:<ul>
11009      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11010      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11011      * passed the following arguments:<ul>
11012      * <li>r : Roo.data.Record[]</li>
11013      * <li>options: Options object from the load call</li>
11014      * <li>success: Boolean success indicator</li></ul></li>
11015      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11016      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11017      * </ul>
11018      */
11019     load : function(options){
11020         options = options || {};
11021         if(this.fireEvent("beforeload", this, options) !== false){
11022             this.storeOptions(options);
11023             var p = Roo.apply(options.params || {}, this.baseParams);
11024             // if meta was not loaded from remote source.. try requesting it.
11025             if (!this.reader.metaFromRemote) {
11026                 p._requestMeta = 1;
11027             }
11028             if(this.sortInfo && this.remoteSort){
11029                 var pn = this.paramNames;
11030                 p[pn["sort"]] = this.sortInfo.field;
11031                 p[pn["dir"]] = this.sortInfo.direction;
11032             }
11033             if (this.multiSort) {
11034                 var pn = this.paramNames;
11035                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11036             }
11037             
11038             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11039         }
11040     },
11041
11042     /**
11043      * Reloads the Record cache from the configured Proxy using the configured Reader and
11044      * the options from the last load operation performed.
11045      * @param {Object} options (optional) An object containing properties which may override the options
11046      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11047      * the most recently used options are reused).
11048      */
11049     reload : function(options){
11050         this.load(Roo.applyIf(options||{}, this.lastOptions));
11051     },
11052
11053     // private
11054     // Called as a callback by the Reader during a load operation.
11055     loadRecords : function(o, options, success){
11056         if(!o || success === false){
11057             if(success !== false){
11058                 this.fireEvent("load", this, [], options, o);
11059             }
11060             if(options.callback){
11061                 options.callback.call(options.scope || this, [], options, false);
11062             }
11063             return;
11064         }
11065         // if data returned failure - throw an exception.
11066         if (o.success === false) {
11067             // show a message if no listener is registered.
11068             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11069                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11070             }
11071             // loadmask wil be hooked into this..
11072             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11073             return;
11074         }
11075         var r = o.records, t = o.totalRecords || r.length;
11076         
11077         this.fireEvent("beforeloadadd", this, r, options, o);
11078         
11079         if(!options || options.add !== true){
11080             if(this.pruneModifiedRecords){
11081                 this.modified = [];
11082             }
11083             for(var i = 0, len = r.length; i < len; i++){
11084                 r[i].join(this);
11085             }
11086             if(this.snapshot){
11087                 this.data = this.snapshot;
11088                 delete this.snapshot;
11089             }
11090             this.data.clear();
11091             this.data.addAll(r);
11092             this.totalLength = t;
11093             this.applySort();
11094             this.fireEvent("datachanged", this);
11095         }else{
11096             this.totalLength = Math.max(t, this.data.length+r.length);
11097             this.add(r);
11098         }
11099         
11100         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11101                 
11102             var e = new Roo.data.Record({});
11103
11104             e.set(this.parent.displayField, this.parent.emptyTitle);
11105             e.set(this.parent.valueField, '');
11106
11107             this.insert(0, e);
11108         }
11109             
11110         this.fireEvent("load", this, r, options, o);
11111         if(options.callback){
11112             options.callback.call(options.scope || this, r, options, true);
11113         }
11114     },
11115
11116
11117     /**
11118      * Loads data from a passed data block. A Reader which understands the format of the data
11119      * must have been configured in the constructor.
11120      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11121      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11122      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11123      */
11124     loadData : function(o, append){
11125         var r = this.reader.readRecords(o);
11126         this.loadRecords(r, {add: append}, true);
11127     },
11128
11129     /**
11130      * Gets the number of cached records.
11131      * <p>
11132      * <em>If using paging, this may not be the total size of the dataset. If the data object
11133      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11134      * the data set size</em>
11135      */
11136     getCount : function(){
11137         return this.data.length || 0;
11138     },
11139
11140     /**
11141      * Gets the total number of records in the dataset as returned by the server.
11142      * <p>
11143      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11144      * the dataset size</em>
11145      */
11146     getTotalCount : function(){
11147         return this.totalLength || 0;
11148     },
11149
11150     /**
11151      * Returns the sort state of the Store as an object with two properties:
11152      * <pre><code>
11153  field {String} The name of the field by which the Records are sorted
11154  direction {String} The sort order, "ASC" or "DESC"
11155      * </code></pre>
11156      */
11157     getSortState : function(){
11158         return this.sortInfo;
11159     },
11160
11161     // private
11162     applySort : function(){
11163         if(this.sortInfo && !this.remoteSort){
11164             var s = this.sortInfo, f = s.field;
11165             var st = this.fields.get(f).sortType;
11166             var fn = function(r1, r2){
11167                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11168                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11169             };
11170             this.data.sort(s.direction, fn);
11171             if(this.snapshot && this.snapshot != this.data){
11172                 this.snapshot.sort(s.direction, fn);
11173             }
11174         }
11175     },
11176
11177     /**
11178      * Sets the default sort column and order to be used by the next load operation.
11179      * @param {String} fieldName The name of the field to sort by.
11180      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11181      */
11182     setDefaultSort : function(field, dir){
11183         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11184     },
11185
11186     /**
11187      * Sort the Records.
11188      * If remote sorting is used, the sort is performed on the server, and the cache is
11189      * reloaded. If local sorting is used, the cache is sorted internally.
11190      * @param {String} fieldName The name of the field to sort by.
11191      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11192      */
11193     sort : function(fieldName, dir){
11194         var f = this.fields.get(fieldName);
11195         if(!dir){
11196             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11197             
11198             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11199                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11200             }else{
11201                 dir = f.sortDir;
11202             }
11203         }
11204         this.sortToggle[f.name] = dir;
11205         this.sortInfo = {field: f.name, direction: dir};
11206         if(!this.remoteSort){
11207             this.applySort();
11208             this.fireEvent("datachanged", this);
11209         }else{
11210             this.load(this.lastOptions);
11211         }
11212     },
11213
11214     /**
11215      * Calls the specified function for each of the Records in the cache.
11216      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11217      * Returning <em>false</em> aborts and exits the iteration.
11218      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11219      */
11220     each : function(fn, scope){
11221         this.data.each(fn, scope);
11222     },
11223
11224     /**
11225      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11226      * (e.g., during paging).
11227      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11228      */
11229     getModifiedRecords : function(){
11230         return this.modified;
11231     },
11232
11233     // private
11234     createFilterFn : function(property, value, anyMatch){
11235         if(!value.exec){ // not a regex
11236             value = String(value);
11237             if(value.length == 0){
11238                 return false;
11239             }
11240             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11241         }
11242         return function(r){
11243             return value.test(r.data[property]);
11244         };
11245     },
11246
11247     /**
11248      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11249      * @param {String} property A field on your records
11250      * @param {Number} start The record index to start at (defaults to 0)
11251      * @param {Number} end The last record index to include (defaults to length - 1)
11252      * @return {Number} The sum
11253      */
11254     sum : function(property, start, end){
11255         var rs = this.data.items, v = 0;
11256         start = start || 0;
11257         end = (end || end === 0) ? end : rs.length-1;
11258
11259         for(var i = start; i <= end; i++){
11260             v += (rs[i].data[property] || 0);
11261         }
11262         return v;
11263     },
11264
11265     /**
11266      * Filter the records by a specified property.
11267      * @param {String} field A field on your records
11268      * @param {String/RegExp} value Either a string that the field
11269      * should start with or a RegExp to test against the field
11270      * @param {Boolean} anyMatch True to match any part not just the beginning
11271      */
11272     filter : function(property, value, anyMatch){
11273         var fn = this.createFilterFn(property, value, anyMatch);
11274         return fn ? this.filterBy(fn) : this.clearFilter();
11275     },
11276
11277     /**
11278      * Filter by a function. The specified function will be called with each
11279      * record in this data source. If the function returns true the record is included,
11280      * otherwise it is filtered.
11281      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11282      * @param {Object} scope (optional) The scope of the function (defaults to this)
11283      */
11284     filterBy : function(fn, scope){
11285         this.snapshot = this.snapshot || this.data;
11286         this.data = this.queryBy(fn, scope||this);
11287         this.fireEvent("datachanged", this);
11288     },
11289
11290     /**
11291      * Query the records by a specified property.
11292      * @param {String} field A field on your records
11293      * @param {String/RegExp} value Either a string that the field
11294      * should start with or a RegExp to test against the field
11295      * @param {Boolean} anyMatch True to match any part not just the beginning
11296      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11297      */
11298     query : function(property, value, anyMatch){
11299         var fn = this.createFilterFn(property, value, anyMatch);
11300         return fn ? this.queryBy(fn) : this.data.clone();
11301     },
11302
11303     /**
11304      * Query by a function. The specified function will be called with each
11305      * record in this data source. If the function returns true the record is included
11306      * in the results.
11307      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11308      * @param {Object} scope (optional) The scope of the function (defaults to this)
11309       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11310      **/
11311     queryBy : function(fn, scope){
11312         var data = this.snapshot || this.data;
11313         return data.filterBy(fn, scope||this);
11314     },
11315
11316     /**
11317      * Collects unique values for a particular dataIndex from this store.
11318      * @param {String} dataIndex The property to collect
11319      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11320      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11321      * @return {Array} An array of the unique values
11322      **/
11323     collect : function(dataIndex, allowNull, bypassFilter){
11324         var d = (bypassFilter === true && this.snapshot) ?
11325                 this.snapshot.items : this.data.items;
11326         var v, sv, r = [], l = {};
11327         for(var i = 0, len = d.length; i < len; i++){
11328             v = d[i].data[dataIndex];
11329             sv = String(v);
11330             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11331                 l[sv] = true;
11332                 r[r.length] = v;
11333             }
11334         }
11335         return r;
11336     },
11337
11338     /**
11339      * Revert to a view of the Record cache with no filtering applied.
11340      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11341      */
11342     clearFilter : function(suppressEvent){
11343         if(this.snapshot && this.snapshot != this.data){
11344             this.data = this.snapshot;
11345             delete this.snapshot;
11346             if(suppressEvent !== true){
11347                 this.fireEvent("datachanged", this);
11348             }
11349         }
11350     },
11351
11352     // private
11353     afterEdit : function(record){
11354         if(this.modified.indexOf(record) == -1){
11355             this.modified.push(record);
11356         }
11357         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11358     },
11359     
11360     // private
11361     afterReject : function(record){
11362         this.modified.remove(record);
11363         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11364     },
11365
11366     // private
11367     afterCommit : function(record){
11368         this.modified.remove(record);
11369         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11370     },
11371
11372     /**
11373      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11374      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11375      */
11376     commitChanges : function(){
11377         var m = this.modified.slice(0);
11378         this.modified = [];
11379         for(var i = 0, len = m.length; i < len; i++){
11380             m[i].commit();
11381         }
11382     },
11383
11384     /**
11385      * Cancel outstanding changes on all changed records.
11386      */
11387     rejectChanges : function(){
11388         var m = this.modified.slice(0);
11389         this.modified = [];
11390         for(var i = 0, len = m.length; i < len; i++){
11391             m[i].reject();
11392         }
11393     },
11394
11395     onMetaChange : function(meta, rtype, o){
11396         this.recordType = rtype;
11397         this.fields = rtype.prototype.fields;
11398         delete this.snapshot;
11399         this.sortInfo = meta.sortInfo || this.sortInfo;
11400         this.modified = [];
11401         this.fireEvent('metachange', this, this.reader.meta);
11402     },
11403     
11404     moveIndex : function(data, type)
11405     {
11406         var index = this.indexOf(data);
11407         
11408         var newIndex = index + type;
11409         
11410         this.remove(data);
11411         
11412         this.insert(newIndex, data);
11413         
11414     }
11415 });/*
11416  * Based on:
11417  * Ext JS Library 1.1.1
11418  * Copyright(c) 2006-2007, Ext JS, LLC.
11419  *
11420  * Originally Released Under LGPL - original licence link has changed is not relivant.
11421  *
11422  * Fork - LGPL
11423  * <script type="text/javascript">
11424  */
11425
11426 /**
11427  * @class Roo.data.SimpleStore
11428  * @extends Roo.data.Store
11429  * Small helper class to make creating Stores from Array data easier.
11430  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11431  * @cfg {Array} fields An array of field definition objects, or field name strings.
11432  * @cfg {Array} data The multi-dimensional array of data
11433  * @constructor
11434  * @param {Object} config
11435  */
11436 Roo.data.SimpleStore = function(config){
11437     Roo.data.SimpleStore.superclass.constructor.call(this, {
11438         isLocal : true,
11439         reader: new Roo.data.ArrayReader({
11440                 id: config.id
11441             },
11442             Roo.data.Record.create(config.fields)
11443         ),
11444         proxy : new Roo.data.MemoryProxy(config.data)
11445     });
11446     this.load();
11447 };
11448 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11449  * Based on:
11450  * Ext JS Library 1.1.1
11451  * Copyright(c) 2006-2007, Ext JS, LLC.
11452  *
11453  * Originally Released Under LGPL - original licence link has changed is not relivant.
11454  *
11455  * Fork - LGPL
11456  * <script type="text/javascript">
11457  */
11458
11459 /**
11460 /**
11461  * @extends Roo.data.Store
11462  * @class Roo.data.JsonStore
11463  * Small helper class to make creating Stores for JSON data easier. <br/>
11464 <pre><code>
11465 var store = new Roo.data.JsonStore({
11466     url: 'get-images.php',
11467     root: 'images',
11468     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11469 });
11470 </code></pre>
11471  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11472  * JsonReader and HttpProxy (unless inline data is provided).</b>
11473  * @cfg {Array} fields An array of field definition objects, or field name strings.
11474  * @constructor
11475  * @param {Object} config
11476  */
11477 Roo.data.JsonStore = function(c){
11478     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11479         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11480         reader: new Roo.data.JsonReader(c, c.fields)
11481     }));
11482 };
11483 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11484  * Based on:
11485  * Ext JS Library 1.1.1
11486  * Copyright(c) 2006-2007, Ext JS, LLC.
11487  *
11488  * Originally Released Under LGPL - original licence link has changed is not relivant.
11489  *
11490  * Fork - LGPL
11491  * <script type="text/javascript">
11492  */
11493
11494  
11495 Roo.data.Field = function(config){
11496     if(typeof config == "string"){
11497         config = {name: config};
11498     }
11499     Roo.apply(this, config);
11500     
11501     if(!this.type){
11502         this.type = "auto";
11503     }
11504     
11505     var st = Roo.data.SortTypes;
11506     // named sortTypes are supported, here we look them up
11507     if(typeof this.sortType == "string"){
11508         this.sortType = st[this.sortType];
11509     }
11510     
11511     // set default sortType for strings and dates
11512     if(!this.sortType){
11513         switch(this.type){
11514             case "string":
11515                 this.sortType = st.asUCString;
11516                 break;
11517             case "date":
11518                 this.sortType = st.asDate;
11519                 break;
11520             default:
11521                 this.sortType = st.none;
11522         }
11523     }
11524
11525     // define once
11526     var stripRe = /[\$,%]/g;
11527
11528     // prebuilt conversion function for this field, instead of
11529     // switching every time we're reading a value
11530     if(!this.convert){
11531         var cv, dateFormat = this.dateFormat;
11532         switch(this.type){
11533             case "":
11534             case "auto":
11535             case undefined:
11536                 cv = function(v){ return v; };
11537                 break;
11538             case "string":
11539                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11540                 break;
11541             case "int":
11542                 cv = function(v){
11543                     return v !== undefined && v !== null && v !== '' ?
11544                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11545                     };
11546                 break;
11547             case "float":
11548                 cv = function(v){
11549                     return v !== undefined && v !== null && v !== '' ?
11550                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11551                     };
11552                 break;
11553             case "bool":
11554             case "boolean":
11555                 cv = function(v){ return v === true || v === "true" || v == 1; };
11556                 break;
11557             case "date":
11558                 cv = function(v){
11559                     if(!v){
11560                         return '';
11561                     }
11562                     if(v instanceof Date){
11563                         return v;
11564                     }
11565                     if(dateFormat){
11566                         if(dateFormat == "timestamp"){
11567                             return new Date(v*1000);
11568                         }
11569                         return Date.parseDate(v, dateFormat);
11570                     }
11571                     var parsed = Date.parse(v);
11572                     return parsed ? new Date(parsed) : null;
11573                 };
11574              break;
11575             
11576         }
11577         this.convert = cv;
11578     }
11579 };
11580
11581 Roo.data.Field.prototype = {
11582     dateFormat: null,
11583     defaultValue: "",
11584     mapping: null,
11585     sortType : null,
11586     sortDir : "ASC"
11587 };/*
11588  * Based on:
11589  * Ext JS Library 1.1.1
11590  * Copyright(c) 2006-2007, Ext JS, LLC.
11591  *
11592  * Originally Released Under LGPL - original licence link has changed is not relivant.
11593  *
11594  * Fork - LGPL
11595  * <script type="text/javascript">
11596  */
11597  
11598 // Base class for reading structured data from a data source.  This class is intended to be
11599 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11600
11601 /**
11602  * @class Roo.data.DataReader
11603  * Base class for reading structured data from a data source.  This class is intended to be
11604  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11605  */
11606
11607 Roo.data.DataReader = function(meta, recordType){
11608     
11609     this.meta = meta;
11610     
11611     this.recordType = recordType instanceof Array ? 
11612         Roo.data.Record.create(recordType) : recordType;
11613 };
11614
11615 Roo.data.DataReader.prototype = {
11616      /**
11617      * Create an empty record
11618      * @param {Object} data (optional) - overlay some values
11619      * @return {Roo.data.Record} record created.
11620      */
11621     newRow :  function(d) {
11622         var da =  {};
11623         this.recordType.prototype.fields.each(function(c) {
11624             switch( c.type) {
11625                 case 'int' : da[c.name] = 0; break;
11626                 case 'date' : da[c.name] = new Date(); break;
11627                 case 'float' : da[c.name] = 0.0; break;
11628                 case 'boolean' : da[c.name] = false; break;
11629                 default : da[c.name] = ""; break;
11630             }
11631             
11632         });
11633         return new this.recordType(Roo.apply(da, d));
11634     }
11635     
11636 };/*
11637  * Based on:
11638  * Ext JS Library 1.1.1
11639  * Copyright(c) 2006-2007, Ext JS, LLC.
11640  *
11641  * Originally Released Under LGPL - original licence link has changed is not relivant.
11642  *
11643  * Fork - LGPL
11644  * <script type="text/javascript">
11645  */
11646
11647 /**
11648  * @class Roo.data.DataProxy
11649  * @extends Roo.data.Observable
11650  * This class is an abstract base class for implementations which provide retrieval of
11651  * unformatted data objects.<br>
11652  * <p>
11653  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11654  * (of the appropriate type which knows how to parse the data object) to provide a block of
11655  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11656  * <p>
11657  * Custom implementations must implement the load method as described in
11658  * {@link Roo.data.HttpProxy#load}.
11659  */
11660 Roo.data.DataProxy = function(){
11661     this.addEvents({
11662         /**
11663          * @event beforeload
11664          * Fires before a network request is made to retrieve a data object.
11665          * @param {Object} This DataProxy object.
11666          * @param {Object} params The params parameter to the load function.
11667          */
11668         beforeload : true,
11669         /**
11670          * @event load
11671          * Fires before the load method's callback is called.
11672          * @param {Object} This DataProxy object.
11673          * @param {Object} o The data object.
11674          * @param {Object} arg The callback argument object passed to the load function.
11675          */
11676         load : true,
11677         /**
11678          * @event loadexception
11679          * Fires if an Exception occurs during data retrieval.
11680          * @param {Object} This DataProxy object.
11681          * @param {Object} o The data object.
11682          * @param {Object} arg The callback argument object passed to the load function.
11683          * @param {Object} e The Exception.
11684          */
11685         loadexception : true
11686     });
11687     Roo.data.DataProxy.superclass.constructor.call(this);
11688 };
11689
11690 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11691
11692     /**
11693      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11694      */
11695 /*
11696  * Based on:
11697  * Ext JS Library 1.1.1
11698  * Copyright(c) 2006-2007, Ext JS, LLC.
11699  *
11700  * Originally Released Under LGPL - original licence link has changed is not relivant.
11701  *
11702  * Fork - LGPL
11703  * <script type="text/javascript">
11704  */
11705 /**
11706  * @class Roo.data.MemoryProxy
11707  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11708  * to the Reader when its load method is called.
11709  * @constructor
11710  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11711  */
11712 Roo.data.MemoryProxy = function(data){
11713     if (data.data) {
11714         data = data.data;
11715     }
11716     Roo.data.MemoryProxy.superclass.constructor.call(this);
11717     this.data = data;
11718 };
11719
11720 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11721     
11722     /**
11723      * Load data from the requested source (in this case an in-memory
11724      * data object passed to the constructor), read the data object into
11725      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11726      * process that block using the passed callback.
11727      * @param {Object} params This parameter is not used by the MemoryProxy class.
11728      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11729      * object into a block of Roo.data.Records.
11730      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11731      * The function must be passed <ul>
11732      * <li>The Record block object</li>
11733      * <li>The "arg" argument from the load function</li>
11734      * <li>A boolean success indicator</li>
11735      * </ul>
11736      * @param {Object} scope The scope in which to call the callback
11737      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11738      */
11739     load : function(params, reader, callback, scope, arg){
11740         params = params || {};
11741         var result;
11742         try {
11743             result = reader.readRecords(this.data);
11744         }catch(e){
11745             this.fireEvent("loadexception", this, arg, null, e);
11746             callback.call(scope, null, arg, false);
11747             return;
11748         }
11749         callback.call(scope, result, arg, true);
11750     },
11751     
11752     // private
11753     update : function(params, records){
11754         
11755     }
11756 });/*
11757  * Based on:
11758  * Ext JS Library 1.1.1
11759  * Copyright(c) 2006-2007, Ext JS, LLC.
11760  *
11761  * Originally Released Under LGPL - original licence link has changed is not relivant.
11762  *
11763  * Fork - LGPL
11764  * <script type="text/javascript">
11765  */
11766 /**
11767  * @class Roo.data.HttpProxy
11768  * @extends Roo.data.DataProxy
11769  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11770  * configured to reference a certain URL.<br><br>
11771  * <p>
11772  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11773  * from which the running page was served.<br><br>
11774  * <p>
11775  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11776  * <p>
11777  * Be aware that to enable the browser to parse an XML document, the server must set
11778  * the Content-Type header in the HTTP response to "text/xml".
11779  * @constructor
11780  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11781  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11782  * will be used to make the request.
11783  */
11784 Roo.data.HttpProxy = function(conn){
11785     Roo.data.HttpProxy.superclass.constructor.call(this);
11786     // is conn a conn config or a real conn?
11787     this.conn = conn;
11788     this.useAjax = !conn || !conn.events;
11789   
11790 };
11791
11792 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11793     // thse are take from connection...
11794     
11795     /**
11796      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11797      */
11798     /**
11799      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11800      * extra parameters to each request made by this object. (defaults to undefined)
11801      */
11802     /**
11803      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11804      *  to each request made by this object. (defaults to undefined)
11805      */
11806     /**
11807      * @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)
11808      */
11809     /**
11810      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11811      */
11812      /**
11813      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11814      * @type Boolean
11815      */
11816   
11817
11818     /**
11819      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11820      * @type Boolean
11821      */
11822     /**
11823      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11824      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11825      * a finer-grained basis than the DataProxy events.
11826      */
11827     getConnection : function(){
11828         return this.useAjax ? Roo.Ajax : this.conn;
11829     },
11830
11831     /**
11832      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11833      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11834      * process that block using the passed callback.
11835      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11836      * for the request to the remote server.
11837      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11838      * object into a block of Roo.data.Records.
11839      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11840      * The function must be passed <ul>
11841      * <li>The Record block object</li>
11842      * <li>The "arg" argument from the load function</li>
11843      * <li>A boolean success indicator</li>
11844      * </ul>
11845      * @param {Object} scope The scope in which to call the callback
11846      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11847      */
11848     load : function(params, reader, callback, scope, arg){
11849         if(this.fireEvent("beforeload", this, params) !== false){
11850             var  o = {
11851                 params : params || {},
11852                 request: {
11853                     callback : callback,
11854                     scope : scope,
11855                     arg : arg
11856                 },
11857                 reader: reader,
11858                 callback : this.loadResponse,
11859                 scope: this
11860             };
11861             if(this.useAjax){
11862                 Roo.applyIf(o, this.conn);
11863                 if(this.activeRequest){
11864                     Roo.Ajax.abort(this.activeRequest);
11865                 }
11866                 this.activeRequest = Roo.Ajax.request(o);
11867             }else{
11868                 this.conn.request(o);
11869             }
11870         }else{
11871             callback.call(scope||this, null, arg, false);
11872         }
11873     },
11874
11875     // private
11876     loadResponse : function(o, success, response){
11877         delete this.activeRequest;
11878         if(!success){
11879             this.fireEvent("loadexception", this, o, response);
11880             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11881             return;
11882         }
11883         var result;
11884         try {
11885             result = o.reader.read(response);
11886         }catch(e){
11887             this.fireEvent("loadexception", this, o, response, e);
11888             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11889             return;
11890         }
11891         
11892         this.fireEvent("load", this, o, o.request.arg);
11893         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11894     },
11895
11896     // private
11897     update : function(dataSet){
11898
11899     },
11900
11901     // private
11902     updateResponse : function(dataSet){
11903
11904     }
11905 });/*
11906  * Based on:
11907  * Ext JS Library 1.1.1
11908  * Copyright(c) 2006-2007, Ext JS, LLC.
11909  *
11910  * Originally Released Under LGPL - original licence link has changed is not relivant.
11911  *
11912  * Fork - LGPL
11913  * <script type="text/javascript">
11914  */
11915
11916 /**
11917  * @class Roo.data.ScriptTagProxy
11918  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11919  * other than the originating domain of the running page.<br><br>
11920  * <p>
11921  * <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
11922  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11923  * <p>
11924  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11925  * source code that is used as the source inside a &lt;script> tag.<br><br>
11926  * <p>
11927  * In order for the browser to process the returned data, the server must wrap the data object
11928  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11929  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11930  * depending on whether the callback name was passed:
11931  * <p>
11932  * <pre><code>
11933 boolean scriptTag = false;
11934 String cb = request.getParameter("callback");
11935 if (cb != null) {
11936     scriptTag = true;
11937     response.setContentType("text/javascript");
11938 } else {
11939     response.setContentType("application/x-json");
11940 }
11941 Writer out = response.getWriter();
11942 if (scriptTag) {
11943     out.write(cb + "(");
11944 }
11945 out.print(dataBlock.toJsonString());
11946 if (scriptTag) {
11947     out.write(");");
11948 }
11949 </pre></code>
11950  *
11951  * @constructor
11952  * @param {Object} config A configuration object.
11953  */
11954 Roo.data.ScriptTagProxy = function(config){
11955     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11956     Roo.apply(this, config);
11957     this.head = document.getElementsByTagName("head")[0];
11958 };
11959
11960 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11961
11962 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11963     /**
11964      * @cfg {String} url The URL from which to request the data object.
11965      */
11966     /**
11967      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11968      */
11969     timeout : 30000,
11970     /**
11971      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11972      * the server the name of the callback function set up by the load call to process the returned data object.
11973      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11974      * javascript output which calls this named function passing the data object as its only parameter.
11975      */
11976     callbackParam : "callback",
11977     /**
11978      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11979      * name to the request.
11980      */
11981     nocache : true,
11982
11983     /**
11984      * Load data from the configured URL, read the data object into
11985      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11986      * process that block using the passed callback.
11987      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11988      * for the request to the remote server.
11989      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11990      * object into a block of Roo.data.Records.
11991      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11992      * The function must be passed <ul>
11993      * <li>The Record block object</li>
11994      * <li>The "arg" argument from the load function</li>
11995      * <li>A boolean success indicator</li>
11996      * </ul>
11997      * @param {Object} scope The scope in which to call the callback
11998      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11999      */
12000     load : function(params, reader, callback, scope, arg){
12001         if(this.fireEvent("beforeload", this, params) !== false){
12002
12003             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12004
12005             var url = this.url;
12006             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12007             if(this.nocache){
12008                 url += "&_dc=" + (new Date().getTime());
12009             }
12010             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12011             var trans = {
12012                 id : transId,
12013                 cb : "stcCallback"+transId,
12014                 scriptId : "stcScript"+transId,
12015                 params : params,
12016                 arg : arg,
12017                 url : url,
12018                 callback : callback,
12019                 scope : scope,
12020                 reader : reader
12021             };
12022             var conn = this;
12023
12024             window[trans.cb] = function(o){
12025                 conn.handleResponse(o, trans);
12026             };
12027
12028             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12029
12030             if(this.autoAbort !== false){
12031                 this.abort();
12032             }
12033
12034             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12035
12036             var script = document.createElement("script");
12037             script.setAttribute("src", url);
12038             script.setAttribute("type", "text/javascript");
12039             script.setAttribute("id", trans.scriptId);
12040             this.head.appendChild(script);
12041
12042             this.trans = trans;
12043         }else{
12044             callback.call(scope||this, null, arg, false);
12045         }
12046     },
12047
12048     // private
12049     isLoading : function(){
12050         return this.trans ? true : false;
12051     },
12052
12053     /**
12054      * Abort the current server request.
12055      */
12056     abort : function(){
12057         if(this.isLoading()){
12058             this.destroyTrans(this.trans);
12059         }
12060     },
12061
12062     // private
12063     destroyTrans : function(trans, isLoaded){
12064         this.head.removeChild(document.getElementById(trans.scriptId));
12065         clearTimeout(trans.timeoutId);
12066         if(isLoaded){
12067             window[trans.cb] = undefined;
12068             try{
12069                 delete window[trans.cb];
12070             }catch(e){}
12071         }else{
12072             // if hasn't been loaded, wait for load to remove it to prevent script error
12073             window[trans.cb] = function(){
12074                 window[trans.cb] = undefined;
12075                 try{
12076                     delete window[trans.cb];
12077                 }catch(e){}
12078             };
12079         }
12080     },
12081
12082     // private
12083     handleResponse : function(o, trans){
12084         this.trans = false;
12085         this.destroyTrans(trans, true);
12086         var result;
12087         try {
12088             result = trans.reader.readRecords(o);
12089         }catch(e){
12090             this.fireEvent("loadexception", this, o, trans.arg, e);
12091             trans.callback.call(trans.scope||window, null, trans.arg, false);
12092             return;
12093         }
12094         this.fireEvent("load", this, o, trans.arg);
12095         trans.callback.call(trans.scope||window, result, trans.arg, true);
12096     },
12097
12098     // private
12099     handleFailure : function(trans){
12100         this.trans = false;
12101         this.destroyTrans(trans, false);
12102         this.fireEvent("loadexception", this, null, trans.arg);
12103         trans.callback.call(trans.scope||window, null, trans.arg, false);
12104     }
12105 });/*
12106  * Based on:
12107  * Ext JS Library 1.1.1
12108  * Copyright(c) 2006-2007, Ext JS, LLC.
12109  *
12110  * Originally Released Under LGPL - original licence link has changed is not relivant.
12111  *
12112  * Fork - LGPL
12113  * <script type="text/javascript">
12114  */
12115
12116 /**
12117  * @class Roo.data.JsonReader
12118  * @extends Roo.data.DataReader
12119  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12120  * based on mappings in a provided Roo.data.Record constructor.
12121  * 
12122  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12123  * in the reply previously. 
12124  * 
12125  * <p>
12126  * Example code:
12127  * <pre><code>
12128 var RecordDef = Roo.data.Record.create([
12129     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12130     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12131 ]);
12132 var myReader = new Roo.data.JsonReader({
12133     totalProperty: "results",    // The property which contains the total dataset size (optional)
12134     root: "rows",                // The property which contains an Array of row objects
12135     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12136 }, RecordDef);
12137 </code></pre>
12138  * <p>
12139  * This would consume a JSON file like this:
12140  * <pre><code>
12141 { 'results': 2, 'rows': [
12142     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12143     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12144 }
12145 </code></pre>
12146  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12147  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12148  * paged from the remote server.
12149  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12150  * @cfg {String} root name of the property which contains the Array of row objects.
12151  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12152  * @cfg {Array} fields Array of field definition objects
12153  * @constructor
12154  * Create a new JsonReader
12155  * @param {Object} meta Metadata configuration options
12156  * @param {Object} recordType Either an Array of field definition objects,
12157  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12158  */
12159 Roo.data.JsonReader = function(meta, recordType){
12160     
12161     meta = meta || {};
12162     // set some defaults:
12163     Roo.applyIf(meta, {
12164         totalProperty: 'total',
12165         successProperty : 'success',
12166         root : 'data',
12167         id : 'id'
12168     });
12169     
12170     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12171 };
12172 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12173     
12174     /**
12175      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12176      * Used by Store query builder to append _requestMeta to params.
12177      * 
12178      */
12179     metaFromRemote : false,
12180     /**
12181      * This method is only used by a DataProxy which has retrieved data from a remote server.
12182      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12183      * @return {Object} data A data block which is used by an Roo.data.Store object as
12184      * a cache of Roo.data.Records.
12185      */
12186     read : function(response){
12187         var json = response.responseText;
12188        
12189         var o = /* eval:var:o */ eval("("+json+")");
12190         if(!o) {
12191             throw {message: "JsonReader.read: Json object not found"};
12192         }
12193         
12194         if(o.metaData){
12195             
12196             delete this.ef;
12197             this.metaFromRemote = true;
12198             this.meta = o.metaData;
12199             this.recordType = Roo.data.Record.create(o.metaData.fields);
12200             this.onMetaChange(this.meta, this.recordType, o);
12201         }
12202         return this.readRecords(o);
12203     },
12204
12205     // private function a store will implement
12206     onMetaChange : function(meta, recordType, o){
12207
12208     },
12209
12210     /**
12211          * @ignore
12212          */
12213     simpleAccess: function(obj, subsc) {
12214         return obj[subsc];
12215     },
12216
12217         /**
12218          * @ignore
12219          */
12220     getJsonAccessor: function(){
12221         var re = /[\[\.]/;
12222         return function(expr) {
12223             try {
12224                 return(re.test(expr))
12225                     ? new Function("obj", "return obj." + expr)
12226                     : function(obj){
12227                         return obj[expr];
12228                     };
12229             } catch(e){}
12230             return Roo.emptyFn;
12231         };
12232     }(),
12233
12234     /**
12235      * Create a data block containing Roo.data.Records from an XML document.
12236      * @param {Object} o An object which contains an Array of row objects in the property specified
12237      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12238      * which contains the total size of the dataset.
12239      * @return {Object} data A data block which is used by an Roo.data.Store object as
12240      * a cache of Roo.data.Records.
12241      */
12242     readRecords : function(o){
12243         /**
12244          * After any data loads, the raw JSON data is available for further custom processing.
12245          * @type Object
12246          */
12247         this.o = o;
12248         var s = this.meta, Record = this.recordType,
12249             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12250
12251 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12252         if (!this.ef) {
12253             if(s.totalProperty) {
12254                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12255                 }
12256                 if(s.successProperty) {
12257                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12258                 }
12259                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12260                 if (s.id) {
12261                         var g = this.getJsonAccessor(s.id);
12262                         this.getId = function(rec) {
12263                                 var r = g(rec);  
12264                                 return (r === undefined || r === "") ? null : r;
12265                         };
12266                 } else {
12267                         this.getId = function(){return null;};
12268                 }
12269             this.ef = [];
12270             for(var jj = 0; jj < fl; jj++){
12271                 f = fi[jj];
12272                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12273                 this.ef[jj] = this.getJsonAccessor(map);
12274             }
12275         }
12276
12277         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12278         if(s.totalProperty){
12279             var vt = parseInt(this.getTotal(o), 10);
12280             if(!isNaN(vt)){
12281                 totalRecords = vt;
12282             }
12283         }
12284         if(s.successProperty){
12285             var vs = this.getSuccess(o);
12286             if(vs === false || vs === 'false'){
12287                 success = false;
12288             }
12289         }
12290         var records = [];
12291         for(var i = 0; i < c; i++){
12292                 var n = root[i];
12293             var values = {};
12294             var id = this.getId(n);
12295             for(var j = 0; j < fl; j++){
12296                 f = fi[j];
12297             var v = this.ef[j](n);
12298             if (!f.convert) {
12299                 Roo.log('missing convert for ' + f.name);
12300                 Roo.log(f);
12301                 continue;
12302             }
12303             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12304             }
12305             var record = new Record(values, id);
12306             record.json = n;
12307             records[i] = record;
12308         }
12309         return {
12310             raw : o,
12311             success : success,
12312             records : records,
12313             totalRecords : totalRecords
12314         };
12315     }
12316 });/*
12317  * Based on:
12318  * Ext JS Library 1.1.1
12319  * Copyright(c) 2006-2007, Ext JS, LLC.
12320  *
12321  * Originally Released Under LGPL - original licence link has changed is not relivant.
12322  *
12323  * Fork - LGPL
12324  * <script type="text/javascript">
12325  */
12326
12327 /**
12328  * @class Roo.data.ArrayReader
12329  * @extends Roo.data.DataReader
12330  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12331  * Each element of that Array represents a row of data fields. The
12332  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12333  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12334  * <p>
12335  * Example code:.
12336  * <pre><code>
12337 var RecordDef = Roo.data.Record.create([
12338     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12339     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12340 ]);
12341 var myReader = new Roo.data.ArrayReader({
12342     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12343 }, RecordDef);
12344 </code></pre>
12345  * <p>
12346  * This would consume an Array like this:
12347  * <pre><code>
12348 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12349   </code></pre>
12350  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12351  * @constructor
12352  * Create a new JsonReader
12353  * @param {Object} meta Metadata configuration options.
12354  * @param {Object} recordType Either an Array of field definition objects
12355  * as specified to {@link Roo.data.Record#create},
12356  * or an {@link Roo.data.Record} object
12357  * created using {@link Roo.data.Record#create}.
12358  */
12359 Roo.data.ArrayReader = function(meta, recordType){
12360     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12361 };
12362
12363 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12364     /**
12365      * Create a data block containing Roo.data.Records from an XML document.
12366      * @param {Object} o An Array of row objects which represents the dataset.
12367      * @return {Object} data A data block which is used by an Roo.data.Store object as
12368      * a cache of Roo.data.Records.
12369      */
12370     readRecords : function(o){
12371         var sid = this.meta ? this.meta.id : null;
12372         var recordType = this.recordType, fields = recordType.prototype.fields;
12373         var records = [];
12374         var root = o;
12375             for(var i = 0; i < root.length; i++){
12376                     var n = root[i];
12377                 var values = {};
12378                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12379                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12380                 var f = fields.items[j];
12381                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12382                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12383                 v = f.convert(v);
12384                 values[f.name] = v;
12385             }
12386                 var record = new recordType(values, id);
12387                 record.json = n;
12388                 records[records.length] = record;
12389             }
12390             return {
12391                 records : records,
12392                 totalRecords : records.length
12393             };
12394     }
12395 });/*
12396  * - LGPL
12397  * * 
12398  */
12399
12400 /**
12401  * @class Roo.bootstrap.ComboBox
12402  * @extends Roo.bootstrap.TriggerField
12403  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12404  * @cfg {Boolean} append (true|false) default false
12405  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12406  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12407  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12408  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12409  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12410  * @cfg {Boolean} animate default true
12411  * @cfg {Boolean} emptyResultText only for touch device
12412  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12413  * @cfg {String} emptyTitle default ''
12414  * @constructor
12415  * Create a new ComboBox.
12416  * @param {Object} config Configuration options
12417  */
12418 Roo.bootstrap.ComboBox = function(config){
12419     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12420     this.addEvents({
12421         /**
12422          * @event expand
12423          * Fires when the dropdown list is expanded
12424         * @param {Roo.bootstrap.ComboBox} combo This combo box
12425         */
12426         'expand' : true,
12427         /**
12428          * @event collapse
12429          * Fires when the dropdown list is collapsed
12430         * @param {Roo.bootstrap.ComboBox} combo This combo box
12431         */
12432         'collapse' : true,
12433         /**
12434          * @event beforeselect
12435          * Fires before a list item is selected. Return false to cancel the selection.
12436         * @param {Roo.bootstrap.ComboBox} combo This combo box
12437         * @param {Roo.data.Record} record The data record returned from the underlying store
12438         * @param {Number} index The index of the selected item in the dropdown list
12439         */
12440         'beforeselect' : true,
12441         /**
12442          * @event select
12443          * Fires when a list item is selected
12444         * @param {Roo.bootstrap.ComboBox} combo This combo box
12445         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12446         * @param {Number} index The index of the selected item in the dropdown list
12447         */
12448         'select' : true,
12449         /**
12450          * @event beforequery
12451          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12452          * The event object passed has these properties:
12453         * @param {Roo.bootstrap.ComboBox} combo This combo box
12454         * @param {String} query The query
12455         * @param {Boolean} forceAll true to force "all" query
12456         * @param {Boolean} cancel true to cancel the query
12457         * @param {Object} e The query event object
12458         */
12459         'beforequery': true,
12460          /**
12461          * @event add
12462          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12463         * @param {Roo.bootstrap.ComboBox} combo This combo box
12464         */
12465         'add' : true,
12466         /**
12467          * @event edit
12468          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12469         * @param {Roo.bootstrap.ComboBox} combo This combo box
12470         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12471         */
12472         'edit' : true,
12473         /**
12474          * @event remove
12475          * Fires when the remove value from the combobox array
12476         * @param {Roo.bootstrap.ComboBox} combo This combo box
12477         */
12478         'remove' : true,
12479         /**
12480          * @event afterremove
12481          * Fires when the remove value from the combobox array
12482         * @param {Roo.bootstrap.ComboBox} combo This combo box
12483         */
12484         'afterremove' : true,
12485         /**
12486          * @event specialfilter
12487          * Fires when specialfilter
12488             * @param {Roo.bootstrap.ComboBox} combo This combo box
12489             */
12490         'specialfilter' : true,
12491         /**
12492          * @event tick
12493          * Fires when tick the element
12494             * @param {Roo.bootstrap.ComboBox} combo This combo box
12495             */
12496         'tick' : true,
12497         /**
12498          * @event touchviewdisplay
12499          * Fires when touch view require special display (default is using displayField)
12500             * @param {Roo.bootstrap.ComboBox} combo This combo box
12501             * @param {Object} cfg set html .
12502             */
12503         'touchviewdisplay' : true
12504         
12505     });
12506     
12507     this.item = [];
12508     this.tickItems = [];
12509     
12510     this.selectedIndex = -1;
12511     if(this.mode == 'local'){
12512         if(config.queryDelay === undefined){
12513             this.queryDelay = 10;
12514         }
12515         if(config.minChars === undefined){
12516             this.minChars = 0;
12517         }
12518     }
12519 };
12520
12521 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12522      
12523     /**
12524      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12525      * rendering into an Roo.Editor, defaults to false)
12526      */
12527     /**
12528      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12529      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12530      */
12531     /**
12532      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12533      */
12534     /**
12535      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12536      * the dropdown list (defaults to undefined, with no header element)
12537      */
12538
12539      /**
12540      * @cfg {String/Roo.Template} tpl The template to use to render the output
12541      */
12542      
12543      /**
12544      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12545      */
12546     listWidth: undefined,
12547     /**
12548      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12549      * mode = 'remote' or 'text' if mode = 'local')
12550      */
12551     displayField: undefined,
12552     
12553     /**
12554      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12555      * mode = 'remote' or 'value' if mode = 'local'). 
12556      * Note: use of a valueField requires the user make a selection
12557      * in order for a value to be mapped.
12558      */
12559     valueField: undefined,
12560     /**
12561      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12562      */
12563     modalTitle : '',
12564     
12565     /**
12566      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12567      * field's data value (defaults to the underlying DOM element's name)
12568      */
12569     hiddenName: undefined,
12570     /**
12571      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12572      */
12573     listClass: '',
12574     /**
12575      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12576      */
12577     selectedClass: 'active',
12578     
12579     /**
12580      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12581      */
12582     shadow:'sides',
12583     /**
12584      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12585      * anchor positions (defaults to 'tl-bl')
12586      */
12587     listAlign: 'tl-bl?',
12588     /**
12589      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12590      */
12591     maxHeight: 300,
12592     /**
12593      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12594      * query specified by the allQuery config option (defaults to 'query')
12595      */
12596     triggerAction: 'query',
12597     /**
12598      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12599      * (defaults to 4, does not apply if editable = false)
12600      */
12601     minChars : 4,
12602     /**
12603      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12604      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12605      */
12606     typeAhead: false,
12607     /**
12608      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12609      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12610      */
12611     queryDelay: 500,
12612     /**
12613      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12614      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12615      */
12616     pageSize: 0,
12617     /**
12618      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12619      * when editable = true (defaults to false)
12620      */
12621     selectOnFocus:false,
12622     /**
12623      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12624      */
12625     queryParam: 'query',
12626     /**
12627      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12628      * when mode = 'remote' (defaults to 'Loading...')
12629      */
12630     loadingText: 'Loading...',
12631     /**
12632      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12633      */
12634     resizable: false,
12635     /**
12636      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12637      */
12638     handleHeight : 8,
12639     /**
12640      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12641      * traditional select (defaults to true)
12642      */
12643     editable: true,
12644     /**
12645      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12646      */
12647     allQuery: '',
12648     /**
12649      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12650      */
12651     mode: 'remote',
12652     /**
12653      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12654      * listWidth has a higher value)
12655      */
12656     minListWidth : 70,
12657     /**
12658      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12659      * allow the user to set arbitrary text into the field (defaults to false)
12660      */
12661     forceSelection:false,
12662     /**
12663      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12664      * if typeAhead = true (defaults to 250)
12665      */
12666     typeAheadDelay : 250,
12667     /**
12668      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12669      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12670      */
12671     valueNotFoundText : undefined,
12672     /**
12673      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12674      */
12675     blockFocus : false,
12676     
12677     /**
12678      * @cfg {Boolean} disableClear Disable showing of clear button.
12679      */
12680     disableClear : false,
12681     /**
12682      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12683      */
12684     alwaysQuery : false,
12685     
12686     /**
12687      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12688      */
12689     multiple : false,
12690     
12691     /**
12692      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12693      */
12694     invalidClass : "has-warning",
12695     
12696     /**
12697      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12698      */
12699     validClass : "has-success",
12700     
12701     /**
12702      * @cfg {Boolean} specialFilter (true|false) special filter default false
12703      */
12704     specialFilter : false,
12705     
12706     /**
12707      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12708      */
12709     mobileTouchView : true,
12710     
12711     /**
12712      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12713      */
12714     useNativeIOS : false,
12715     
12716     ios_options : false,
12717     
12718     //private
12719     addicon : false,
12720     editicon: false,
12721     
12722     page: 0,
12723     hasQuery: false,
12724     append: false,
12725     loadNext: false,
12726     autoFocus : true,
12727     tickable : false,
12728     btnPosition : 'right',
12729     triggerList : true,
12730     showToggleBtn : true,
12731     animate : true,
12732     emptyResultText: 'Empty',
12733     triggerText : 'Select',
12734     emptyTitle : '',
12735     
12736     // element that contains real text value.. (when hidden is used..)
12737     
12738     getAutoCreate : function()
12739     {   
12740         var cfg = false;
12741         //render
12742         /*
12743          * Render classic select for iso
12744          */
12745         
12746         if(Roo.isIOS && this.useNativeIOS){
12747             cfg = this.getAutoCreateNativeIOS();
12748             return cfg;
12749         }
12750         
12751         /*
12752          * Touch Devices
12753          */
12754         
12755         if(Roo.isTouch && this.mobileTouchView){
12756             cfg = this.getAutoCreateTouchView();
12757             return cfg;;
12758         }
12759         
12760         /*
12761          *  Normal ComboBox
12762          */
12763         if(!this.tickable){
12764             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12765             return cfg;
12766         }
12767         
12768         /*
12769          *  ComboBox with tickable selections
12770          */
12771              
12772         var align = this.labelAlign || this.parentLabelAlign();
12773         
12774         cfg = {
12775             cls : 'form-group roo-combobox-tickable' //input-group
12776         };
12777         
12778         var btn_text_select = '';
12779         var btn_text_done = '';
12780         var btn_text_cancel = '';
12781         
12782         if (this.btn_text_show) {
12783             btn_text_select = 'Select';
12784             btn_text_done = 'Done';
12785             btn_text_cancel = 'Cancel'; 
12786         }
12787         
12788         var buttons = {
12789             tag : 'div',
12790             cls : 'tickable-buttons',
12791             cn : [
12792                 {
12793                     tag : 'button',
12794                     type : 'button',
12795                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12796                     //html : this.triggerText
12797                     html: btn_text_select
12798                 },
12799                 {
12800                     tag : 'button',
12801                     type : 'button',
12802                     name : 'ok',
12803                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12804                     //html : 'Done'
12805                     html: btn_text_done
12806                 },
12807                 {
12808                     tag : 'button',
12809                     type : 'button',
12810                     name : 'cancel',
12811                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12812                     //html : 'Cancel'
12813                     html: btn_text_cancel
12814                 }
12815             ]
12816         };
12817         
12818         if(this.editable){
12819             buttons.cn.unshift({
12820                 tag: 'input',
12821                 cls: 'roo-select2-search-field-input'
12822             });
12823         }
12824         
12825         var _this = this;
12826         
12827         Roo.each(buttons.cn, function(c){
12828             if (_this.size) {
12829                 c.cls += ' btn-' + _this.size;
12830             }
12831
12832             if (_this.disabled) {
12833                 c.disabled = true;
12834             }
12835         });
12836         
12837         var box = {
12838             tag: 'div',
12839             cn: [
12840                 {
12841                     tag: 'input',
12842                     type : 'hidden',
12843                     cls: 'form-hidden-field'
12844                 },
12845                 {
12846                     tag: 'ul',
12847                     cls: 'roo-select2-choices',
12848                     cn:[
12849                         {
12850                             tag: 'li',
12851                             cls: 'roo-select2-search-field',
12852                             cn: [
12853                                 buttons
12854                             ]
12855                         }
12856                     ]
12857                 }
12858             ]
12859         };
12860         
12861         var combobox = {
12862             cls: 'roo-select2-container input-group roo-select2-container-multi',
12863             cn: [
12864                 box
12865 //                {
12866 //                    tag: 'ul',
12867 //                    cls: 'typeahead typeahead-long dropdown-menu',
12868 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12869 //                }
12870             ]
12871         };
12872         
12873         if(this.hasFeedback && !this.allowBlank){
12874             
12875             var feedback = {
12876                 tag: 'span',
12877                 cls: 'glyphicon form-control-feedback'
12878             };
12879
12880             combobox.cn.push(feedback);
12881         }
12882         
12883         
12884         if (align ==='left' && this.fieldLabel.length) {
12885             
12886             cfg.cls += ' roo-form-group-label-left';
12887             
12888             cfg.cn = [
12889                 {
12890                     tag : 'i',
12891                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12892                     tooltip : 'This field is required'
12893                 },
12894                 {
12895                     tag: 'label',
12896                     'for' :  id,
12897                     cls : 'control-label',
12898                     html : this.fieldLabel
12899
12900                 },
12901                 {
12902                     cls : "", 
12903                     cn: [
12904                         combobox
12905                     ]
12906                 }
12907
12908             ];
12909             
12910             var labelCfg = cfg.cn[1];
12911             var contentCfg = cfg.cn[2];
12912             
12913
12914             if(this.indicatorpos == 'right'){
12915                 
12916                 cfg.cn = [
12917                     {
12918                         tag: 'label',
12919                         'for' :  id,
12920                         cls : 'control-label',
12921                         cn : [
12922                             {
12923                                 tag : 'span',
12924                                 html : this.fieldLabel
12925                             },
12926                             {
12927                                 tag : 'i',
12928                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12929                                 tooltip : 'This field is required'
12930                             }
12931                         ]
12932                     },
12933                     {
12934                         cls : "",
12935                         cn: [
12936                             combobox
12937                         ]
12938                     }
12939
12940                 ];
12941                 
12942                 
12943                 
12944                 labelCfg = cfg.cn[0];
12945                 contentCfg = cfg.cn[1];
12946             
12947             }
12948             
12949             if(this.labelWidth > 12){
12950                 labelCfg.style = "width: " + this.labelWidth + 'px';
12951             }
12952             
12953             if(this.labelWidth < 13 && this.labelmd == 0){
12954                 this.labelmd = this.labelWidth;
12955             }
12956             
12957             if(this.labellg > 0){
12958                 labelCfg.cls += ' col-lg-' + this.labellg;
12959                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12960             }
12961             
12962             if(this.labelmd > 0){
12963                 labelCfg.cls += ' col-md-' + this.labelmd;
12964                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12965             }
12966             
12967             if(this.labelsm > 0){
12968                 labelCfg.cls += ' col-sm-' + this.labelsm;
12969                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12970             }
12971             
12972             if(this.labelxs > 0){
12973                 labelCfg.cls += ' col-xs-' + this.labelxs;
12974                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12975             }
12976                 
12977                 
12978         } else if ( this.fieldLabel.length) {
12979 //                Roo.log(" label");
12980                  cfg.cn = [
12981                     {
12982                         tag : 'i',
12983                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12984                         tooltip : 'This field is required'
12985                     },
12986                     {
12987                         tag: 'label',
12988                         //cls : 'input-group-addon',
12989                         html : this.fieldLabel
12990                     },
12991                     combobox
12992                 ];
12993                 
12994                 if(this.indicatorpos == 'right'){
12995                     cfg.cn = [
12996                         {
12997                             tag: 'label',
12998                             //cls : 'input-group-addon',
12999                             html : this.fieldLabel
13000                         },
13001                         {
13002                             tag : 'i',
13003                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13004                             tooltip : 'This field is required'
13005                         },
13006                         combobox
13007                     ];
13008                     
13009                 }
13010
13011         } else {
13012             
13013 //                Roo.log(" no label && no align");
13014                 cfg = combobox
13015                      
13016                 
13017         }
13018          
13019         var settings=this;
13020         ['xs','sm','md','lg'].map(function(size){
13021             if (settings[size]) {
13022                 cfg.cls += ' col-' + size + '-' + settings[size];
13023             }
13024         });
13025         
13026         return cfg;
13027         
13028     },
13029     
13030     _initEventsCalled : false,
13031     
13032     // private
13033     initEvents: function()
13034     {   
13035         if (this._initEventsCalled) { // as we call render... prevent looping...
13036             return;
13037         }
13038         this._initEventsCalled = true;
13039         
13040         if (!this.store) {
13041             throw "can not find store for combo";
13042         }
13043         
13044         this.indicator = this.indicatorEl();
13045         
13046         this.store = Roo.factory(this.store, Roo.data);
13047         this.store.parent = this;
13048         
13049         // if we are building from html. then this element is so complex, that we can not really
13050         // use the rendered HTML.
13051         // so we have to trash and replace the previous code.
13052         if (Roo.XComponent.build_from_html) {
13053             // remove this element....
13054             var e = this.el.dom, k=0;
13055             while (e ) { e = e.previousSibling;  ++k;}
13056
13057             this.el.remove();
13058             
13059             this.el=false;
13060             this.rendered = false;
13061             
13062             this.render(this.parent().getChildContainer(true), k);
13063         }
13064         
13065         if(Roo.isIOS && this.useNativeIOS){
13066             this.initIOSView();
13067             return;
13068         }
13069         
13070         /*
13071          * Touch Devices
13072          */
13073         
13074         if(Roo.isTouch && this.mobileTouchView){
13075             this.initTouchView();
13076             return;
13077         }
13078         
13079         if(this.tickable){
13080             this.initTickableEvents();
13081             return;
13082         }
13083         
13084         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13085         
13086         if(this.hiddenName){
13087             
13088             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13089             
13090             this.hiddenField.dom.value =
13091                 this.hiddenValue !== undefined ? this.hiddenValue :
13092                 this.value !== undefined ? this.value : '';
13093
13094             // prevent input submission
13095             this.el.dom.removeAttribute('name');
13096             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13097              
13098              
13099         }
13100         //if(Roo.isGecko){
13101         //    this.el.dom.setAttribute('autocomplete', 'off');
13102         //}
13103         
13104         var cls = 'x-combo-list';
13105         
13106         //this.list = new Roo.Layer({
13107         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13108         //});
13109         
13110         var _this = this;
13111         
13112         (function(){
13113             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13114             _this.list.setWidth(lw);
13115         }).defer(100);
13116         
13117         this.list.on('mouseover', this.onViewOver, this);
13118         this.list.on('mousemove', this.onViewMove, this);
13119         this.list.on('scroll', this.onViewScroll, this);
13120         
13121         /*
13122         this.list.swallowEvent('mousewheel');
13123         this.assetHeight = 0;
13124
13125         if(this.title){
13126             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13127             this.assetHeight += this.header.getHeight();
13128         }
13129
13130         this.innerList = this.list.createChild({cls:cls+'-inner'});
13131         this.innerList.on('mouseover', this.onViewOver, this);
13132         this.innerList.on('mousemove', this.onViewMove, this);
13133         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13134         
13135         if(this.allowBlank && !this.pageSize && !this.disableClear){
13136             this.footer = this.list.createChild({cls:cls+'-ft'});
13137             this.pageTb = new Roo.Toolbar(this.footer);
13138            
13139         }
13140         if(this.pageSize){
13141             this.footer = this.list.createChild({cls:cls+'-ft'});
13142             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13143                     {pageSize: this.pageSize});
13144             
13145         }
13146         
13147         if (this.pageTb && this.allowBlank && !this.disableClear) {
13148             var _this = this;
13149             this.pageTb.add(new Roo.Toolbar.Fill(), {
13150                 cls: 'x-btn-icon x-btn-clear',
13151                 text: '&#160;',
13152                 handler: function()
13153                 {
13154                     _this.collapse();
13155                     _this.clearValue();
13156                     _this.onSelect(false, -1);
13157                 }
13158             });
13159         }
13160         if (this.footer) {
13161             this.assetHeight += this.footer.getHeight();
13162         }
13163         */
13164             
13165         if(!this.tpl){
13166             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13167         }
13168
13169         this.view = new Roo.View(this.list, this.tpl, {
13170             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13171         });
13172         //this.view.wrapEl.setDisplayed(false);
13173         this.view.on('click', this.onViewClick, this);
13174         
13175         
13176         this.store.on('beforeload', this.onBeforeLoad, this);
13177         this.store.on('load', this.onLoad, this);
13178         this.store.on('loadexception', this.onLoadException, this);
13179         /*
13180         if(this.resizable){
13181             this.resizer = new Roo.Resizable(this.list,  {
13182                pinned:true, handles:'se'
13183             });
13184             this.resizer.on('resize', function(r, w, h){
13185                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13186                 this.listWidth = w;
13187                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13188                 this.restrictHeight();
13189             }, this);
13190             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13191         }
13192         */
13193         if(!this.editable){
13194             this.editable = true;
13195             this.setEditable(false);
13196         }
13197         
13198         /*
13199         
13200         if (typeof(this.events.add.listeners) != 'undefined') {
13201             
13202             this.addicon = this.wrap.createChild(
13203                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13204        
13205             this.addicon.on('click', function(e) {
13206                 this.fireEvent('add', this);
13207             }, this);
13208         }
13209         if (typeof(this.events.edit.listeners) != 'undefined') {
13210             
13211             this.editicon = this.wrap.createChild(
13212                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13213             if (this.addicon) {
13214                 this.editicon.setStyle('margin-left', '40px');
13215             }
13216             this.editicon.on('click', function(e) {
13217                 
13218                 // we fire even  if inothing is selected..
13219                 this.fireEvent('edit', this, this.lastData );
13220                 
13221             }, this);
13222         }
13223         */
13224         
13225         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13226             "up" : function(e){
13227                 this.inKeyMode = true;
13228                 this.selectPrev();
13229             },
13230
13231             "down" : function(e){
13232                 if(!this.isExpanded()){
13233                     this.onTriggerClick();
13234                 }else{
13235                     this.inKeyMode = true;
13236                     this.selectNext();
13237                 }
13238             },
13239
13240             "enter" : function(e){
13241 //                this.onViewClick();
13242                 //return true;
13243                 this.collapse();
13244                 
13245                 if(this.fireEvent("specialkey", this, e)){
13246                     this.onViewClick(false);
13247                 }
13248                 
13249                 return true;
13250             },
13251
13252             "esc" : function(e){
13253                 this.collapse();
13254             },
13255
13256             "tab" : function(e){
13257                 this.collapse();
13258                 
13259                 if(this.fireEvent("specialkey", this, e)){
13260                     this.onViewClick(false);
13261                 }
13262                 
13263                 return true;
13264             },
13265
13266             scope : this,
13267
13268             doRelay : function(foo, bar, hname){
13269                 if(hname == 'down' || this.scope.isExpanded()){
13270                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13271                 }
13272                 return true;
13273             },
13274
13275             forceKeyDown: true
13276         });
13277         
13278         
13279         this.queryDelay = Math.max(this.queryDelay || 10,
13280                 this.mode == 'local' ? 10 : 250);
13281         
13282         
13283         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13284         
13285         if(this.typeAhead){
13286             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13287         }
13288         if(this.editable !== false){
13289             this.inputEl().on("keyup", this.onKeyUp, this);
13290         }
13291         if(this.forceSelection){
13292             this.inputEl().on('blur', this.doForce, this);
13293         }
13294         
13295         if(this.multiple){
13296             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13297             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13298         }
13299     },
13300     
13301     initTickableEvents: function()
13302     {   
13303         this.createList();
13304         
13305         if(this.hiddenName){
13306             
13307             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13308             
13309             this.hiddenField.dom.value =
13310                 this.hiddenValue !== undefined ? this.hiddenValue :
13311                 this.value !== undefined ? this.value : '';
13312
13313             // prevent input submission
13314             this.el.dom.removeAttribute('name');
13315             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13316              
13317              
13318         }
13319         
13320 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13321         
13322         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13323         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13324         if(this.triggerList){
13325             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13326         }
13327          
13328         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13329         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13330         
13331         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13332         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13333         
13334         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13335         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13336         
13337         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13338         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13339         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13340         
13341         this.okBtn.hide();
13342         this.cancelBtn.hide();
13343         
13344         var _this = this;
13345         
13346         (function(){
13347             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13348             _this.list.setWidth(lw);
13349         }).defer(100);
13350         
13351         this.list.on('mouseover', this.onViewOver, this);
13352         this.list.on('mousemove', this.onViewMove, this);
13353         
13354         this.list.on('scroll', this.onViewScroll, this);
13355         
13356         if(!this.tpl){
13357             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>';
13358         }
13359
13360         this.view = new Roo.View(this.list, this.tpl, {
13361             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13362         });
13363         
13364         //this.view.wrapEl.setDisplayed(false);
13365         this.view.on('click', this.onViewClick, this);
13366         
13367         
13368         
13369         this.store.on('beforeload', this.onBeforeLoad, this);
13370         this.store.on('load', this.onLoad, this);
13371         this.store.on('loadexception', this.onLoadException, this);
13372         
13373         if(this.editable){
13374             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13375                 "up" : function(e){
13376                     this.inKeyMode = true;
13377                     this.selectPrev();
13378                 },
13379
13380                 "down" : function(e){
13381                     this.inKeyMode = true;
13382                     this.selectNext();
13383                 },
13384
13385                 "enter" : function(e){
13386                     if(this.fireEvent("specialkey", this, e)){
13387                         this.onViewClick(false);
13388                     }
13389                     
13390                     return true;
13391                 },
13392
13393                 "esc" : function(e){
13394                     this.onTickableFooterButtonClick(e, false, false);
13395                 },
13396
13397                 "tab" : function(e){
13398                     this.fireEvent("specialkey", this, e);
13399                     
13400                     this.onTickableFooterButtonClick(e, false, false);
13401                     
13402                     return true;
13403                 },
13404
13405                 scope : this,
13406
13407                 doRelay : function(e, fn, key){
13408                     if(this.scope.isExpanded()){
13409                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13410                     }
13411                     return true;
13412                 },
13413
13414                 forceKeyDown: true
13415             });
13416         }
13417         
13418         this.queryDelay = Math.max(this.queryDelay || 10,
13419                 this.mode == 'local' ? 10 : 250);
13420         
13421         
13422         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13423         
13424         if(this.typeAhead){
13425             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13426         }
13427         
13428         if(this.editable !== false){
13429             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13430         }
13431         
13432         this.indicator = this.indicatorEl();
13433         
13434         if(this.indicator){
13435             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13436             this.indicator.hide();
13437         }
13438         
13439     },
13440
13441     onDestroy : function(){
13442         if(this.view){
13443             this.view.setStore(null);
13444             this.view.el.removeAllListeners();
13445             this.view.el.remove();
13446             this.view.purgeListeners();
13447         }
13448         if(this.list){
13449             this.list.dom.innerHTML  = '';
13450         }
13451         
13452         if(this.store){
13453             this.store.un('beforeload', this.onBeforeLoad, this);
13454             this.store.un('load', this.onLoad, this);
13455             this.store.un('loadexception', this.onLoadException, this);
13456         }
13457         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13458     },
13459
13460     // private
13461     fireKey : function(e){
13462         if(e.isNavKeyPress() && !this.list.isVisible()){
13463             this.fireEvent("specialkey", this, e);
13464         }
13465     },
13466
13467     // private
13468     onResize: function(w, h){
13469 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13470 //        
13471 //        if(typeof w != 'number'){
13472 //            // we do not handle it!?!?
13473 //            return;
13474 //        }
13475 //        var tw = this.trigger.getWidth();
13476 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13477 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13478 //        var x = w - tw;
13479 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13480 //            
13481 //        //this.trigger.setStyle('left', x+'px');
13482 //        
13483 //        if(this.list && this.listWidth === undefined){
13484 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13485 //            this.list.setWidth(lw);
13486 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13487 //        }
13488         
13489     
13490         
13491     },
13492
13493     /**
13494      * Allow or prevent the user from directly editing the field text.  If false is passed,
13495      * the user will only be able to select from the items defined in the dropdown list.  This method
13496      * is the runtime equivalent of setting the 'editable' config option at config time.
13497      * @param {Boolean} value True to allow the user to directly edit the field text
13498      */
13499     setEditable : function(value){
13500         if(value == this.editable){
13501             return;
13502         }
13503         this.editable = value;
13504         if(!value){
13505             this.inputEl().dom.setAttribute('readOnly', true);
13506             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13507             this.inputEl().addClass('x-combo-noedit');
13508         }else{
13509             this.inputEl().dom.setAttribute('readOnly', false);
13510             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13511             this.inputEl().removeClass('x-combo-noedit');
13512         }
13513     },
13514
13515     // private
13516     
13517     onBeforeLoad : function(combo,opts){
13518         if(!this.hasFocus){
13519             return;
13520         }
13521          if (!opts.add) {
13522             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13523          }
13524         this.restrictHeight();
13525         this.selectedIndex = -1;
13526     },
13527
13528     // private
13529     onLoad : function(){
13530         
13531         this.hasQuery = false;
13532         
13533         if(!this.hasFocus){
13534             return;
13535         }
13536         
13537         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13538             this.loading.hide();
13539         }
13540         
13541         if(this.store.getCount() > 0){
13542             
13543             this.expand();
13544             this.restrictHeight();
13545             if(this.lastQuery == this.allQuery){
13546                 if(this.editable && !this.tickable){
13547                     this.inputEl().dom.select();
13548                 }
13549                 
13550                 if(
13551                     !this.selectByValue(this.value, true) &&
13552                     this.autoFocus && 
13553                     (
13554                         !this.store.lastOptions ||
13555                         typeof(this.store.lastOptions.add) == 'undefined' || 
13556                         this.store.lastOptions.add != true
13557                     )
13558                 ){
13559                     this.select(0, true);
13560                 }
13561             }else{
13562                 if(this.autoFocus){
13563                     this.selectNext();
13564                 }
13565                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13566                     this.taTask.delay(this.typeAheadDelay);
13567                 }
13568             }
13569         }else{
13570             this.onEmptyResults();
13571         }
13572         
13573         //this.el.focus();
13574     },
13575     // private
13576     onLoadException : function()
13577     {
13578         this.hasQuery = false;
13579         
13580         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13581             this.loading.hide();
13582         }
13583         
13584         if(this.tickable && this.editable){
13585             return;
13586         }
13587         
13588         this.collapse();
13589         // only causes errors at present
13590         //Roo.log(this.store.reader.jsonData);
13591         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13592             // fixme
13593             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13594         //}
13595         
13596         
13597     },
13598     // private
13599     onTypeAhead : function(){
13600         if(this.store.getCount() > 0){
13601             var r = this.store.getAt(0);
13602             var newValue = r.data[this.displayField];
13603             var len = newValue.length;
13604             var selStart = this.getRawValue().length;
13605             
13606             if(selStart != len){
13607                 this.setRawValue(newValue);
13608                 this.selectText(selStart, newValue.length);
13609             }
13610         }
13611     },
13612
13613     // private
13614     onSelect : function(record, index){
13615         
13616         if(this.fireEvent('beforeselect', this, record, index) !== false){
13617         
13618             this.setFromData(index > -1 ? record.data : false);
13619             
13620             this.collapse();
13621             this.fireEvent('select', this, record, index);
13622         }
13623     },
13624
13625     /**
13626      * Returns the currently selected field value or empty string if no value is set.
13627      * @return {String} value The selected value
13628      */
13629     getValue : function()
13630     {
13631         if(Roo.isIOS && this.useNativeIOS){
13632             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13633         }
13634         
13635         if(this.multiple){
13636             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13637         }
13638         
13639         if(this.valueField){
13640             return typeof this.value != 'undefined' ? this.value : '';
13641         }else{
13642             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13643         }
13644     },
13645     
13646     getRawValue : function()
13647     {
13648         if(Roo.isIOS && this.useNativeIOS){
13649             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13650         }
13651         
13652         var v = this.inputEl().getValue();
13653         
13654         return v;
13655     },
13656
13657     /**
13658      * Clears any text/value currently set in the field
13659      */
13660     clearValue : function(){
13661         
13662         if(this.hiddenField){
13663             this.hiddenField.dom.value = '';
13664         }
13665         this.value = '';
13666         this.setRawValue('');
13667         this.lastSelectionText = '';
13668         this.lastData = false;
13669         
13670         var close = this.closeTriggerEl();
13671         
13672         if(close){
13673             close.hide();
13674         }
13675         
13676         this.validate();
13677         
13678     },
13679
13680     /**
13681      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13682      * will be displayed in the field.  If the value does not match the data value of an existing item,
13683      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13684      * Otherwise the field will be blank (although the value will still be set).
13685      * @param {String} value The value to match
13686      */
13687     setValue : function(v)
13688     {
13689         if(Roo.isIOS && this.useNativeIOS){
13690             this.setIOSValue(v);
13691             return;
13692         }
13693         
13694         if(this.multiple){
13695             this.syncValue();
13696             return;
13697         }
13698         
13699         var text = v;
13700         if(this.valueField){
13701             var r = this.findRecord(this.valueField, v);
13702             if(r){
13703                 text = r.data[this.displayField];
13704             }else if(this.valueNotFoundText !== undefined){
13705                 text = this.valueNotFoundText;
13706             }
13707         }
13708         this.lastSelectionText = text;
13709         if(this.hiddenField){
13710             this.hiddenField.dom.value = v;
13711         }
13712         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13713         this.value = v;
13714         
13715         var close = this.closeTriggerEl();
13716         
13717         if(close){
13718             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13719         }
13720         
13721         this.validate();
13722     },
13723     /**
13724      * @property {Object} the last set data for the element
13725      */
13726     
13727     lastData : false,
13728     /**
13729      * Sets the value of the field based on a object which is related to the record format for the store.
13730      * @param {Object} value the value to set as. or false on reset?
13731      */
13732     setFromData : function(o){
13733         
13734         if(this.multiple){
13735             this.addItem(o);
13736             return;
13737         }
13738             
13739         var dv = ''; // display value
13740         var vv = ''; // value value..
13741         this.lastData = o;
13742         if (this.displayField) {
13743             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13744         } else {
13745             // this is an error condition!!!
13746             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13747         }
13748         
13749         if(this.valueField){
13750             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13751         }
13752         
13753         var close = this.closeTriggerEl();
13754         
13755         if(close){
13756             if(dv.length || vv * 1 > 0){
13757                 close.show() ;
13758                 this.blockFocus=true;
13759             } else {
13760                 close.hide();
13761             }             
13762         }
13763         
13764         if(this.hiddenField){
13765             this.hiddenField.dom.value = vv;
13766             
13767             this.lastSelectionText = dv;
13768             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13769             this.value = vv;
13770             return;
13771         }
13772         // no hidden field.. - we store the value in 'value', but still display
13773         // display field!!!!
13774         this.lastSelectionText = dv;
13775         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13776         this.value = vv;
13777         
13778         
13779         
13780     },
13781     // private
13782     reset : function(){
13783         // overridden so that last data is reset..
13784         
13785         if(this.multiple){
13786             this.clearItem();
13787             return;
13788         }
13789         
13790         this.setValue(this.originalValue);
13791         //this.clearInvalid();
13792         this.lastData = false;
13793         if (this.view) {
13794             this.view.clearSelections();
13795         }
13796         
13797         this.validate();
13798     },
13799     // private
13800     findRecord : function(prop, value){
13801         var record;
13802         if(this.store.getCount() > 0){
13803             this.store.each(function(r){
13804                 if(r.data[prop] == value){
13805                     record = r;
13806                     return false;
13807                 }
13808                 return true;
13809             });
13810         }
13811         return record;
13812     },
13813     
13814     getName: function()
13815     {
13816         // returns hidden if it's set..
13817         if (!this.rendered) {return ''};
13818         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13819         
13820     },
13821     // private
13822     onViewMove : function(e, t){
13823         this.inKeyMode = false;
13824     },
13825
13826     // private
13827     onViewOver : function(e, t){
13828         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13829             return;
13830         }
13831         var item = this.view.findItemFromChild(t);
13832         
13833         if(item){
13834             var index = this.view.indexOf(item);
13835             this.select(index, false);
13836         }
13837     },
13838
13839     // private
13840     onViewClick : function(view, doFocus, el, e)
13841     {
13842         var index = this.view.getSelectedIndexes()[0];
13843         
13844         var r = this.store.getAt(index);
13845         
13846         if(this.tickable){
13847             
13848             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13849                 return;
13850             }
13851             
13852             var rm = false;
13853             var _this = this;
13854             
13855             Roo.each(this.tickItems, function(v,k){
13856                 
13857                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13858                     Roo.log(v);
13859                     _this.tickItems.splice(k, 1);
13860                     
13861                     if(typeof(e) == 'undefined' && view == false){
13862                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13863                     }
13864                     
13865                     rm = true;
13866                     return;
13867                 }
13868             });
13869             
13870             if(rm){
13871                 return;
13872             }
13873             
13874             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13875                 this.tickItems.push(r.data);
13876             }
13877             
13878             if(typeof(e) == 'undefined' && view == false){
13879                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13880             }
13881                     
13882             return;
13883         }
13884         
13885         if(r){
13886             this.onSelect(r, index);
13887         }
13888         if(doFocus !== false && !this.blockFocus){
13889             this.inputEl().focus();
13890         }
13891     },
13892
13893     // private
13894     restrictHeight : function(){
13895         //this.innerList.dom.style.height = '';
13896         //var inner = this.innerList.dom;
13897         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13898         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13899         //this.list.beginUpdate();
13900         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13901         this.list.alignTo(this.inputEl(), this.listAlign);
13902         this.list.alignTo(this.inputEl(), this.listAlign);
13903         //this.list.endUpdate();
13904     },
13905
13906     // private
13907     onEmptyResults : function(){
13908         
13909         if(this.tickable && this.editable){
13910             this.restrictHeight();
13911             return;
13912         }
13913         
13914         this.collapse();
13915     },
13916
13917     /**
13918      * Returns true if the dropdown list is expanded, else false.
13919      */
13920     isExpanded : function(){
13921         return this.list.isVisible();
13922     },
13923
13924     /**
13925      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13926      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13927      * @param {String} value The data value of the item to select
13928      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13929      * selected item if it is not currently in view (defaults to true)
13930      * @return {Boolean} True if the value matched an item in the list, else false
13931      */
13932     selectByValue : function(v, scrollIntoView){
13933         if(v !== undefined && v !== null){
13934             var r = this.findRecord(this.valueField || this.displayField, v);
13935             if(r){
13936                 this.select(this.store.indexOf(r), scrollIntoView);
13937                 return true;
13938             }
13939         }
13940         return false;
13941     },
13942
13943     /**
13944      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13945      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13946      * @param {Number} index The zero-based index of the list item to select
13947      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13948      * selected item if it is not currently in view (defaults to true)
13949      */
13950     select : function(index, scrollIntoView){
13951         this.selectedIndex = index;
13952         this.view.select(index);
13953         if(scrollIntoView !== false){
13954             var el = this.view.getNode(index);
13955             /*
13956              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13957              */
13958             if(el){
13959                 this.list.scrollChildIntoView(el, false);
13960             }
13961         }
13962     },
13963
13964     // private
13965     selectNext : function(){
13966         var ct = this.store.getCount();
13967         if(ct > 0){
13968             if(this.selectedIndex == -1){
13969                 this.select(0);
13970             }else if(this.selectedIndex < ct-1){
13971                 this.select(this.selectedIndex+1);
13972             }
13973         }
13974     },
13975
13976     // private
13977     selectPrev : function(){
13978         var ct = this.store.getCount();
13979         if(ct > 0){
13980             if(this.selectedIndex == -1){
13981                 this.select(0);
13982             }else if(this.selectedIndex != 0){
13983                 this.select(this.selectedIndex-1);
13984             }
13985         }
13986     },
13987
13988     // private
13989     onKeyUp : function(e){
13990         if(this.editable !== false && !e.isSpecialKey()){
13991             this.lastKey = e.getKey();
13992             this.dqTask.delay(this.queryDelay);
13993         }
13994     },
13995
13996     // private
13997     validateBlur : function(){
13998         return !this.list || !this.list.isVisible();   
13999     },
14000
14001     // private
14002     initQuery : function(){
14003         
14004         var v = this.getRawValue();
14005         
14006         if(this.tickable && this.editable){
14007             v = this.tickableInputEl().getValue();
14008         }
14009         
14010         this.doQuery(v);
14011     },
14012
14013     // private
14014     doForce : function(){
14015         if(this.inputEl().dom.value.length > 0){
14016             this.inputEl().dom.value =
14017                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14018              
14019         }
14020     },
14021
14022     /**
14023      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14024      * query allowing the query action to be canceled if needed.
14025      * @param {String} query The SQL query to execute
14026      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14027      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14028      * saved in the current store (defaults to false)
14029      */
14030     doQuery : function(q, forceAll){
14031         
14032         if(q === undefined || q === null){
14033             q = '';
14034         }
14035         var qe = {
14036             query: q,
14037             forceAll: forceAll,
14038             combo: this,
14039             cancel:false
14040         };
14041         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14042             return false;
14043         }
14044         q = qe.query;
14045         
14046         forceAll = qe.forceAll;
14047         if(forceAll === true || (q.length >= this.minChars)){
14048             
14049             this.hasQuery = true;
14050             
14051             if(this.lastQuery != q || this.alwaysQuery){
14052                 this.lastQuery = q;
14053                 if(this.mode == 'local'){
14054                     this.selectedIndex = -1;
14055                     if(forceAll){
14056                         this.store.clearFilter();
14057                     }else{
14058                         
14059                         if(this.specialFilter){
14060                             this.fireEvent('specialfilter', this);
14061                             this.onLoad();
14062                             return;
14063                         }
14064                         
14065                         this.store.filter(this.displayField, q);
14066                     }
14067                     
14068                     this.store.fireEvent("datachanged", this.store);
14069                     
14070                     this.onLoad();
14071                     
14072                     
14073                 }else{
14074                     
14075                     this.store.baseParams[this.queryParam] = q;
14076                     
14077                     var options = {params : this.getParams(q)};
14078                     
14079                     if(this.loadNext){
14080                         options.add = true;
14081                         options.params.start = this.page * this.pageSize;
14082                     }
14083                     
14084                     this.store.load(options);
14085                     
14086                     /*
14087                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14088                      *  we should expand the list on onLoad
14089                      *  so command out it
14090                      */
14091 //                    this.expand();
14092                 }
14093             }else{
14094                 this.selectedIndex = -1;
14095                 this.onLoad();   
14096             }
14097         }
14098         
14099         this.loadNext = false;
14100     },
14101     
14102     // private
14103     getParams : function(q){
14104         var p = {};
14105         //p[this.queryParam] = q;
14106         
14107         if(this.pageSize){
14108             p.start = 0;
14109             p.limit = this.pageSize;
14110         }
14111         return p;
14112     },
14113
14114     /**
14115      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14116      */
14117     collapse : function(){
14118         if(!this.isExpanded()){
14119             return;
14120         }
14121         
14122         this.list.hide();
14123         
14124         this.hasFocus = false;
14125         
14126         if(this.tickable){
14127             this.okBtn.hide();
14128             this.cancelBtn.hide();
14129             this.trigger.show();
14130             
14131             if(this.editable){
14132                 this.tickableInputEl().dom.value = '';
14133                 this.tickableInputEl().blur();
14134             }
14135             
14136         }
14137         
14138         Roo.get(document).un('mousedown', this.collapseIf, this);
14139         Roo.get(document).un('mousewheel', this.collapseIf, this);
14140         if (!this.editable) {
14141             Roo.get(document).un('keydown', this.listKeyPress, this);
14142         }
14143         this.fireEvent('collapse', this);
14144         
14145         this.validate();
14146     },
14147
14148     // private
14149     collapseIf : function(e){
14150         var in_combo  = e.within(this.el);
14151         var in_list =  e.within(this.list);
14152         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14153         
14154         if (in_combo || in_list || is_list) {
14155             //e.stopPropagation();
14156             return;
14157         }
14158         
14159         if(this.tickable){
14160             this.onTickableFooterButtonClick(e, false, false);
14161         }
14162
14163         this.collapse();
14164         
14165     },
14166
14167     /**
14168      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14169      */
14170     expand : function(){
14171        
14172         if(this.isExpanded() || !this.hasFocus){
14173             return;
14174         }
14175         
14176         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14177         this.list.setWidth(lw);
14178         
14179         Roo.log('expand');
14180         
14181         this.list.show();
14182         
14183         this.restrictHeight();
14184         
14185         if(this.tickable){
14186             
14187             this.tickItems = Roo.apply([], this.item);
14188             
14189             this.okBtn.show();
14190             this.cancelBtn.show();
14191             this.trigger.hide();
14192             
14193             if(this.editable){
14194                 this.tickableInputEl().focus();
14195             }
14196             
14197         }
14198         
14199         Roo.get(document).on('mousedown', this.collapseIf, this);
14200         Roo.get(document).on('mousewheel', this.collapseIf, this);
14201         if (!this.editable) {
14202             Roo.get(document).on('keydown', this.listKeyPress, this);
14203         }
14204         
14205         this.fireEvent('expand', this);
14206     },
14207
14208     // private
14209     // Implements the default empty TriggerField.onTriggerClick function
14210     onTriggerClick : function(e)
14211     {
14212         Roo.log('trigger click');
14213         
14214         if(this.disabled || !this.triggerList){
14215             return;
14216         }
14217         
14218         this.page = 0;
14219         this.loadNext = false;
14220         
14221         if(this.isExpanded()){
14222             this.collapse();
14223             if (!this.blockFocus) {
14224                 this.inputEl().focus();
14225             }
14226             
14227         }else {
14228             this.hasFocus = true;
14229             if(this.triggerAction == 'all') {
14230                 this.doQuery(this.allQuery, true);
14231             } else {
14232                 this.doQuery(this.getRawValue());
14233             }
14234             if (!this.blockFocus) {
14235                 this.inputEl().focus();
14236             }
14237         }
14238     },
14239     
14240     onTickableTriggerClick : function(e)
14241     {
14242         if(this.disabled){
14243             return;
14244         }
14245         
14246         this.page = 0;
14247         this.loadNext = false;
14248         this.hasFocus = true;
14249         
14250         if(this.triggerAction == 'all') {
14251             this.doQuery(this.allQuery, true);
14252         } else {
14253             this.doQuery(this.getRawValue());
14254         }
14255     },
14256     
14257     onSearchFieldClick : function(e)
14258     {
14259         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14260             this.onTickableFooterButtonClick(e, false, false);
14261             return;
14262         }
14263         
14264         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14265             return;
14266         }
14267         
14268         this.page = 0;
14269         this.loadNext = false;
14270         this.hasFocus = true;
14271         
14272         if(this.triggerAction == 'all') {
14273             this.doQuery(this.allQuery, true);
14274         } else {
14275             this.doQuery(this.getRawValue());
14276         }
14277     },
14278     
14279     listKeyPress : function(e)
14280     {
14281         //Roo.log('listkeypress');
14282         // scroll to first matching element based on key pres..
14283         if (e.isSpecialKey()) {
14284             return false;
14285         }
14286         var k = String.fromCharCode(e.getKey()).toUpperCase();
14287         //Roo.log(k);
14288         var match  = false;
14289         var csel = this.view.getSelectedNodes();
14290         var cselitem = false;
14291         if (csel.length) {
14292             var ix = this.view.indexOf(csel[0]);
14293             cselitem  = this.store.getAt(ix);
14294             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14295                 cselitem = false;
14296             }
14297             
14298         }
14299         
14300         this.store.each(function(v) { 
14301             if (cselitem) {
14302                 // start at existing selection.
14303                 if (cselitem.id == v.id) {
14304                     cselitem = false;
14305                 }
14306                 return true;
14307             }
14308                 
14309             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14310                 match = this.store.indexOf(v);
14311                 return false;
14312             }
14313             return true;
14314         }, this);
14315         
14316         if (match === false) {
14317             return true; // no more action?
14318         }
14319         // scroll to?
14320         this.view.select(match);
14321         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14322         sn.scrollIntoView(sn.dom.parentNode, false);
14323     },
14324     
14325     onViewScroll : function(e, t){
14326         
14327         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){
14328             return;
14329         }
14330         
14331         this.hasQuery = true;
14332         
14333         this.loading = this.list.select('.loading', true).first();
14334         
14335         if(this.loading === null){
14336             this.list.createChild({
14337                 tag: 'div',
14338                 cls: 'loading roo-select2-more-results roo-select2-active',
14339                 html: 'Loading more results...'
14340             });
14341             
14342             this.loading = this.list.select('.loading', true).first();
14343             
14344             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14345             
14346             this.loading.hide();
14347         }
14348         
14349         this.loading.show();
14350         
14351         var _combo = this;
14352         
14353         this.page++;
14354         this.loadNext = true;
14355         
14356         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14357         
14358         return;
14359     },
14360     
14361     addItem : function(o)
14362     {   
14363         var dv = ''; // display value
14364         
14365         if (this.displayField) {
14366             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14367         } else {
14368             // this is an error condition!!!
14369             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14370         }
14371         
14372         if(!dv.length){
14373             return;
14374         }
14375         
14376         var choice = this.choices.createChild({
14377             tag: 'li',
14378             cls: 'roo-select2-search-choice',
14379             cn: [
14380                 {
14381                     tag: 'div',
14382                     html: dv
14383                 },
14384                 {
14385                     tag: 'a',
14386                     href: '#',
14387                     cls: 'roo-select2-search-choice-close fa fa-times',
14388                     tabindex: '-1'
14389                 }
14390             ]
14391             
14392         }, this.searchField);
14393         
14394         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14395         
14396         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14397         
14398         this.item.push(o);
14399         
14400         this.lastData = o;
14401         
14402         this.syncValue();
14403         
14404         this.inputEl().dom.value = '';
14405         
14406         this.validate();
14407     },
14408     
14409     onRemoveItem : function(e, _self, o)
14410     {
14411         e.preventDefault();
14412         
14413         this.lastItem = Roo.apply([], this.item);
14414         
14415         var index = this.item.indexOf(o.data) * 1;
14416         
14417         if( index < 0){
14418             Roo.log('not this item?!');
14419             return;
14420         }
14421         
14422         this.item.splice(index, 1);
14423         o.item.remove();
14424         
14425         this.syncValue();
14426         
14427         this.fireEvent('remove', this, e);
14428         
14429         this.validate();
14430         
14431     },
14432     
14433     syncValue : function()
14434     {
14435         if(!this.item.length){
14436             this.clearValue();
14437             return;
14438         }
14439             
14440         var value = [];
14441         var _this = this;
14442         Roo.each(this.item, function(i){
14443             if(_this.valueField){
14444                 value.push(i[_this.valueField]);
14445                 return;
14446             }
14447
14448             value.push(i);
14449         });
14450
14451         this.value = value.join(',');
14452
14453         if(this.hiddenField){
14454             this.hiddenField.dom.value = this.value;
14455         }
14456         
14457         this.store.fireEvent("datachanged", this.store);
14458         
14459         this.validate();
14460     },
14461     
14462     clearItem : function()
14463     {
14464         if(!this.multiple){
14465             return;
14466         }
14467         
14468         this.item = [];
14469         
14470         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14471            c.remove();
14472         });
14473         
14474         this.syncValue();
14475         
14476         this.validate();
14477         
14478         if(this.tickable && !Roo.isTouch){
14479             this.view.refresh();
14480         }
14481     },
14482     
14483     inputEl: function ()
14484     {
14485         if(Roo.isIOS && this.useNativeIOS){
14486             return this.el.select('select.roo-ios-select', true).first();
14487         }
14488         
14489         if(Roo.isTouch && this.mobileTouchView){
14490             return this.el.select('input.form-control',true).first();
14491         }
14492         
14493         if(this.tickable){
14494             return this.searchField;
14495         }
14496         
14497         return this.el.select('input.form-control',true).first();
14498     },
14499     
14500     onTickableFooterButtonClick : function(e, btn, el)
14501     {
14502         e.preventDefault();
14503         
14504         this.lastItem = Roo.apply([], this.item);
14505         
14506         if(btn && btn.name == 'cancel'){
14507             this.tickItems = Roo.apply([], this.item);
14508             this.collapse();
14509             return;
14510         }
14511         
14512         this.clearItem();
14513         
14514         var _this = this;
14515         
14516         Roo.each(this.tickItems, function(o){
14517             _this.addItem(o);
14518         });
14519         
14520         this.collapse();
14521         
14522     },
14523     
14524     validate : function()
14525     {
14526         var v = this.getRawValue();
14527         
14528         if(this.multiple){
14529             v = this.getValue();
14530         }
14531         
14532         if(this.disabled || this.allowBlank || v.length){
14533             this.markValid();
14534             return true;
14535         }
14536         
14537         this.markInvalid();
14538         return false;
14539     },
14540     
14541     tickableInputEl : function()
14542     {
14543         if(!this.tickable || !this.editable){
14544             return this.inputEl();
14545         }
14546         
14547         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14548     },
14549     
14550     
14551     getAutoCreateTouchView : function()
14552     {
14553         var id = Roo.id();
14554         
14555         var cfg = {
14556             cls: 'form-group' //input-group
14557         };
14558         
14559         var input =  {
14560             tag: 'input',
14561             id : id,
14562             type : this.inputType,
14563             cls : 'form-control x-combo-noedit',
14564             autocomplete: 'new-password',
14565             placeholder : this.placeholder || '',
14566             readonly : true
14567         };
14568         
14569         if (this.name) {
14570             input.name = this.name;
14571         }
14572         
14573         if (this.size) {
14574             input.cls += ' input-' + this.size;
14575         }
14576         
14577         if (this.disabled) {
14578             input.disabled = true;
14579         }
14580         
14581         var inputblock = {
14582             cls : '',
14583             cn : [
14584                 input
14585             ]
14586         };
14587         
14588         if(this.before){
14589             inputblock.cls += ' input-group';
14590             
14591             inputblock.cn.unshift({
14592                 tag :'span',
14593                 cls : 'input-group-addon',
14594                 html : this.before
14595             });
14596         }
14597         
14598         if(this.removable && !this.multiple){
14599             inputblock.cls += ' roo-removable';
14600             
14601             inputblock.cn.push({
14602                 tag: 'button',
14603                 html : 'x',
14604                 cls : 'roo-combo-removable-btn close'
14605             });
14606         }
14607
14608         if(this.hasFeedback && !this.allowBlank){
14609             
14610             inputblock.cls += ' has-feedback';
14611             
14612             inputblock.cn.push({
14613                 tag: 'span',
14614                 cls: 'glyphicon form-control-feedback'
14615             });
14616             
14617         }
14618         
14619         if (this.after) {
14620             
14621             inputblock.cls += (this.before) ? '' : ' input-group';
14622             
14623             inputblock.cn.push({
14624                 tag :'span',
14625                 cls : 'input-group-addon',
14626                 html : this.after
14627             });
14628         }
14629
14630         var box = {
14631             tag: 'div',
14632             cn: [
14633                 {
14634                     tag: 'input',
14635                     type : 'hidden',
14636                     cls: 'form-hidden-field'
14637                 },
14638                 inputblock
14639             ]
14640             
14641         };
14642         
14643         if(this.multiple){
14644             box = {
14645                 tag: 'div',
14646                 cn: [
14647                     {
14648                         tag: 'input',
14649                         type : 'hidden',
14650                         cls: 'form-hidden-field'
14651                     },
14652                     {
14653                         tag: 'ul',
14654                         cls: 'roo-select2-choices',
14655                         cn:[
14656                             {
14657                                 tag: 'li',
14658                                 cls: 'roo-select2-search-field',
14659                                 cn: [
14660
14661                                     inputblock
14662                                 ]
14663                             }
14664                         ]
14665                     }
14666                 ]
14667             }
14668         };
14669         
14670         var combobox = {
14671             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14672             cn: [
14673                 box
14674             ]
14675         };
14676         
14677         if(!this.multiple && this.showToggleBtn){
14678             
14679             var caret = {
14680                         tag: 'span',
14681                         cls: 'caret'
14682             };
14683             
14684             if (this.caret != false) {
14685                 caret = {
14686                      tag: 'i',
14687                      cls: 'fa fa-' + this.caret
14688                 };
14689                 
14690             }
14691             
14692             combobox.cn.push({
14693                 tag :'span',
14694                 cls : 'input-group-addon btn dropdown-toggle',
14695                 cn : [
14696                     caret,
14697                     {
14698                         tag: 'span',
14699                         cls: 'combobox-clear',
14700                         cn  : [
14701                             {
14702                                 tag : 'i',
14703                                 cls: 'icon-remove'
14704                             }
14705                         ]
14706                     }
14707                 ]
14708
14709             })
14710         }
14711         
14712         if(this.multiple){
14713             combobox.cls += ' roo-select2-container-multi';
14714         }
14715         
14716         var align = this.labelAlign || this.parentLabelAlign();
14717         
14718         if (align ==='left' && this.fieldLabel.length) {
14719
14720             cfg.cn = [
14721                 {
14722                    tag : 'i',
14723                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14724                    tooltip : 'This field is required'
14725                 },
14726                 {
14727                     tag: 'label',
14728                     cls : 'control-label',
14729                     html : this.fieldLabel
14730
14731                 },
14732                 {
14733                     cls : '', 
14734                     cn: [
14735                         combobox
14736                     ]
14737                 }
14738             ];
14739             
14740             var labelCfg = cfg.cn[1];
14741             var contentCfg = cfg.cn[2];
14742             
14743
14744             if(this.indicatorpos == 'right'){
14745                 cfg.cn = [
14746                     {
14747                         tag: 'label',
14748                         'for' :  id,
14749                         cls : 'control-label',
14750                         cn : [
14751                             {
14752                                 tag : 'span',
14753                                 html : this.fieldLabel
14754                             },
14755                             {
14756                                 tag : 'i',
14757                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14758                                 tooltip : 'This field is required'
14759                             }
14760                         ]
14761                     },
14762                     {
14763                         cls : "",
14764                         cn: [
14765                             combobox
14766                         ]
14767                     }
14768
14769                 ];
14770                 
14771                 labelCfg = cfg.cn[0];
14772                 contentCfg = cfg.cn[1];
14773             }
14774             
14775            
14776             
14777             if(this.labelWidth > 12){
14778                 labelCfg.style = "width: " + this.labelWidth + 'px';
14779             }
14780             
14781             if(this.labelWidth < 13 && this.labelmd == 0){
14782                 this.labelmd = this.labelWidth;
14783             }
14784             
14785             if(this.labellg > 0){
14786                 labelCfg.cls += ' col-lg-' + this.labellg;
14787                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14788             }
14789             
14790             if(this.labelmd > 0){
14791                 labelCfg.cls += ' col-md-' + this.labelmd;
14792                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14793             }
14794             
14795             if(this.labelsm > 0){
14796                 labelCfg.cls += ' col-sm-' + this.labelsm;
14797                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14798             }
14799             
14800             if(this.labelxs > 0){
14801                 labelCfg.cls += ' col-xs-' + this.labelxs;
14802                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14803             }
14804                 
14805                 
14806         } else if ( this.fieldLabel.length) {
14807             cfg.cn = [
14808                 {
14809                    tag : 'i',
14810                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14811                    tooltip : 'This field is required'
14812                 },
14813                 {
14814                     tag: 'label',
14815                     cls : 'control-label',
14816                     html : this.fieldLabel
14817
14818                 },
14819                 {
14820                     cls : '', 
14821                     cn: [
14822                         combobox
14823                     ]
14824                 }
14825             ];
14826             
14827             if(this.indicatorpos == 'right'){
14828                 cfg.cn = [
14829                     {
14830                         tag: 'label',
14831                         cls : 'control-label',
14832                         html : this.fieldLabel,
14833                         cn : [
14834                             {
14835                                tag : 'i',
14836                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14837                                tooltip : 'This field is required'
14838                             }
14839                         ]
14840                     },
14841                     {
14842                         cls : '', 
14843                         cn: [
14844                             combobox
14845                         ]
14846                     }
14847                 ];
14848             }
14849         } else {
14850             cfg.cn = combobox;    
14851         }
14852         
14853         
14854         var settings = this;
14855         
14856         ['xs','sm','md','lg'].map(function(size){
14857             if (settings[size]) {
14858                 cfg.cls += ' col-' + size + '-' + settings[size];
14859             }
14860         });
14861         
14862         return cfg;
14863     },
14864     
14865     initTouchView : function()
14866     {
14867         this.renderTouchView();
14868         
14869         this.touchViewEl.on('scroll', function(){
14870             this.el.dom.scrollTop = 0;
14871         }, this);
14872         
14873         this.originalValue = this.getValue();
14874         
14875         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14876         
14877         this.inputEl().on("click", this.showTouchView, this);
14878         if (this.triggerEl) {
14879             this.triggerEl.on("click", this.showTouchView, this);
14880         }
14881         
14882         
14883         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14884         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14885         
14886         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14887         
14888         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14889         this.store.on('load', this.onTouchViewLoad, this);
14890         this.store.on('loadexception', this.onTouchViewLoadException, this);
14891         
14892         if(this.hiddenName){
14893             
14894             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14895             
14896             this.hiddenField.dom.value =
14897                 this.hiddenValue !== undefined ? this.hiddenValue :
14898                 this.value !== undefined ? this.value : '';
14899         
14900             this.el.dom.removeAttribute('name');
14901             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14902         }
14903         
14904         if(this.multiple){
14905             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14906             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14907         }
14908         
14909         if(this.removable && !this.multiple){
14910             var close = this.closeTriggerEl();
14911             if(close){
14912                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14913                 close.on('click', this.removeBtnClick, this, close);
14914             }
14915         }
14916         /*
14917          * fix the bug in Safari iOS8
14918          */
14919         this.inputEl().on("focus", function(e){
14920             document.activeElement.blur();
14921         }, this);
14922         
14923         return;
14924         
14925         
14926     },
14927     
14928     renderTouchView : function()
14929     {
14930         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14931         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14932         
14933         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14934         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14935         
14936         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14937         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14938         this.touchViewBodyEl.setStyle('overflow', 'auto');
14939         
14940         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14941         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14942         
14943         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14944         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14945         
14946     },
14947     
14948     showTouchView : function()
14949     {
14950         if(this.disabled){
14951             return;
14952         }
14953         
14954         this.touchViewHeaderEl.hide();
14955
14956         if(this.modalTitle.length){
14957             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14958             this.touchViewHeaderEl.show();
14959         }
14960
14961         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14962         this.touchViewEl.show();
14963
14964         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14965         
14966         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14967         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14968
14969         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14970
14971         if(this.modalTitle.length){
14972             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14973         }
14974         
14975         this.touchViewBodyEl.setHeight(bodyHeight);
14976
14977         if(this.animate){
14978             var _this = this;
14979             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14980         }else{
14981             this.touchViewEl.addClass('in');
14982         }
14983
14984         this.doTouchViewQuery();
14985         
14986     },
14987     
14988     hideTouchView : function()
14989     {
14990         this.touchViewEl.removeClass('in');
14991
14992         if(this.animate){
14993             var _this = this;
14994             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14995         }else{
14996             this.touchViewEl.setStyle('display', 'none');
14997         }
14998         
14999     },
15000     
15001     setTouchViewValue : function()
15002     {
15003         if(this.multiple){
15004             this.clearItem();
15005         
15006             var _this = this;
15007
15008             Roo.each(this.tickItems, function(o){
15009                 this.addItem(o);
15010             }, this);
15011         }
15012         
15013         this.hideTouchView();
15014     },
15015     
15016     doTouchViewQuery : function()
15017     {
15018         var qe = {
15019             query: '',
15020             forceAll: true,
15021             combo: this,
15022             cancel:false
15023         };
15024         
15025         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15026             return false;
15027         }
15028         
15029         if(!this.alwaysQuery || this.mode == 'local'){
15030             this.onTouchViewLoad();
15031             return;
15032         }
15033         
15034         this.store.load();
15035     },
15036     
15037     onTouchViewBeforeLoad : function(combo,opts)
15038     {
15039         return;
15040     },
15041
15042     // private
15043     onTouchViewLoad : function()
15044     {
15045         if(this.store.getCount() < 1){
15046             this.onTouchViewEmptyResults();
15047             return;
15048         }
15049         
15050         this.clearTouchView();
15051         
15052         var rawValue = this.getRawValue();
15053         
15054         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15055         
15056         this.tickItems = [];
15057         
15058         this.store.data.each(function(d, rowIndex){
15059             var row = this.touchViewListGroup.createChild(template);
15060             
15061             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15062                 row.addClass(d.data.cls);
15063             }
15064             
15065             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15066                 var cfg = {
15067                     data : d.data,
15068                     html : d.data[this.displayField]
15069                 };
15070                 
15071                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15072                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15073                 }
15074             }
15075             row.removeClass('selected');
15076             if(!this.multiple && this.valueField &&
15077                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15078             {
15079                 // radio buttons..
15080                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15081                 row.addClass('selected');
15082             }
15083             
15084             if(this.multiple && this.valueField &&
15085                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15086             {
15087                 
15088                 // checkboxes...
15089                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15090                 this.tickItems.push(d.data);
15091             }
15092             
15093             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15094             
15095         }, this);
15096         
15097         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15098         
15099         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15100
15101         if(this.modalTitle.length){
15102             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15103         }
15104
15105         var listHeight = this.touchViewListGroup.getHeight();
15106         
15107         var _this = this;
15108         
15109         if(firstChecked && listHeight > bodyHeight){
15110             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15111         }
15112         
15113     },
15114     
15115     onTouchViewLoadException : function()
15116     {
15117         this.hideTouchView();
15118     },
15119     
15120     onTouchViewEmptyResults : function()
15121     {
15122         this.clearTouchView();
15123         
15124         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15125         
15126         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15127         
15128     },
15129     
15130     clearTouchView : function()
15131     {
15132         this.touchViewListGroup.dom.innerHTML = '';
15133     },
15134     
15135     onTouchViewClick : function(e, el, o)
15136     {
15137         e.preventDefault();
15138         
15139         var row = o.row;
15140         var rowIndex = o.rowIndex;
15141         
15142         var r = this.store.getAt(rowIndex);
15143         
15144         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15145             
15146             if(!this.multiple){
15147                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15148                     c.dom.removeAttribute('checked');
15149                 }, this);
15150
15151                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15152
15153                 this.setFromData(r.data);
15154
15155                 var close = this.closeTriggerEl();
15156
15157                 if(close){
15158                     close.show();
15159                 }
15160
15161                 this.hideTouchView();
15162
15163                 this.fireEvent('select', this, r, rowIndex);
15164
15165                 return;
15166             }
15167
15168             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15169                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15170                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15171                 return;
15172             }
15173
15174             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15175             this.addItem(r.data);
15176             this.tickItems.push(r.data);
15177         }
15178     },
15179     
15180     getAutoCreateNativeIOS : function()
15181     {
15182         var cfg = {
15183             cls: 'form-group' //input-group,
15184         };
15185         
15186         var combobox =  {
15187             tag: 'select',
15188             cls : 'roo-ios-select'
15189         };
15190         
15191         if (this.name) {
15192             combobox.name = this.name;
15193         }
15194         
15195         if (this.disabled) {
15196             combobox.disabled = true;
15197         }
15198         
15199         var settings = this;
15200         
15201         ['xs','sm','md','lg'].map(function(size){
15202             if (settings[size]) {
15203                 cfg.cls += ' col-' + size + '-' + settings[size];
15204             }
15205         });
15206         
15207         cfg.cn = combobox;
15208         
15209         return cfg;
15210         
15211     },
15212     
15213     initIOSView : function()
15214     {
15215         this.store.on('load', this.onIOSViewLoad, this);
15216         
15217         return;
15218     },
15219     
15220     onIOSViewLoad : function()
15221     {
15222         if(this.store.getCount() < 1){
15223             return;
15224         }
15225         
15226         this.clearIOSView();
15227         
15228         if(this.allowBlank) {
15229             
15230             var default_text = '-- SELECT --';
15231             
15232             if(this.placeholder.length){
15233                 default_text = this.placeholder;
15234             }
15235             
15236             if(this.emptyTitle.length){
15237                 default_text = ' - ' + this.emptyTitle + ' -';
15238             }
15239             
15240             var opt = this.inputEl().createChild({
15241                 tag: 'option',
15242                 value : 0,
15243                 html : default_text
15244             });
15245             
15246             var o = {};
15247             o[this.valueField] = 0;
15248             o[this.displayField] = default_text;
15249             
15250             this.ios_options.push({
15251                 data : o,
15252                 el : opt
15253             });
15254             
15255         }
15256         
15257         this.store.data.each(function(d, rowIndex){
15258             
15259             var html = '';
15260             
15261             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15262                 html = d.data[this.displayField];
15263             }
15264             
15265             var value = '';
15266             
15267             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15268                 value = d.data[this.valueField];
15269             }
15270             
15271             var option = {
15272                 tag: 'option',
15273                 value : value,
15274                 html : html
15275             };
15276             
15277             if(this.value == d.data[this.valueField]){
15278                 option['selected'] = true;
15279             }
15280             
15281             var opt = this.inputEl().createChild(option);
15282             
15283             this.ios_options.push({
15284                 data : d.data,
15285                 el : opt
15286             });
15287             
15288         }, this);
15289         
15290         this.inputEl().on('change', function(){
15291            this.fireEvent('select', this);
15292         }, this);
15293         
15294     },
15295     
15296     clearIOSView: function()
15297     {
15298         this.inputEl().dom.innerHTML = '';
15299         
15300         this.ios_options = [];
15301     },
15302     
15303     setIOSValue: function(v)
15304     {
15305         this.value = v;
15306         
15307         if(!this.ios_options){
15308             return;
15309         }
15310         
15311         Roo.each(this.ios_options, function(opts){
15312            
15313            opts.el.dom.removeAttribute('selected');
15314            
15315            if(opts.data[this.valueField] != v){
15316                return;
15317            }
15318            
15319            opts.el.dom.setAttribute('selected', true);
15320            
15321         }, this);
15322     }
15323
15324     /** 
15325     * @cfg {Boolean} grow 
15326     * @hide 
15327     */
15328     /** 
15329     * @cfg {Number} growMin 
15330     * @hide 
15331     */
15332     /** 
15333     * @cfg {Number} growMax 
15334     * @hide 
15335     */
15336     /**
15337      * @hide
15338      * @method autoSize
15339      */
15340 });
15341
15342 Roo.apply(Roo.bootstrap.ComboBox,  {
15343     
15344     header : {
15345         tag: 'div',
15346         cls: 'modal-header',
15347         cn: [
15348             {
15349                 tag: 'h4',
15350                 cls: 'modal-title'
15351             }
15352         ]
15353     },
15354     
15355     body : {
15356         tag: 'div',
15357         cls: 'modal-body',
15358         cn: [
15359             {
15360                 tag: 'ul',
15361                 cls: 'list-group'
15362             }
15363         ]
15364     },
15365     
15366     listItemRadio : {
15367         tag: 'li',
15368         cls: 'list-group-item',
15369         cn: [
15370             {
15371                 tag: 'span',
15372                 cls: 'roo-combobox-list-group-item-value'
15373             },
15374             {
15375                 tag: 'div',
15376                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15377                 cn: [
15378                     {
15379                         tag: 'input',
15380                         type: 'radio'
15381                     },
15382                     {
15383                         tag: 'label'
15384                     }
15385                 ]
15386             }
15387         ]
15388     },
15389     
15390     listItemCheckbox : {
15391         tag: 'li',
15392         cls: 'list-group-item',
15393         cn: [
15394             {
15395                 tag: 'span',
15396                 cls: 'roo-combobox-list-group-item-value'
15397             },
15398             {
15399                 tag: 'div',
15400                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15401                 cn: [
15402                     {
15403                         tag: 'input',
15404                         type: 'checkbox'
15405                     },
15406                     {
15407                         tag: 'label'
15408                     }
15409                 ]
15410             }
15411         ]
15412     },
15413     
15414     emptyResult : {
15415         tag: 'div',
15416         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15417     },
15418     
15419     footer : {
15420         tag: 'div',
15421         cls: 'modal-footer',
15422         cn: [
15423             {
15424                 tag: 'div',
15425                 cls: 'row',
15426                 cn: [
15427                     {
15428                         tag: 'div',
15429                         cls: 'col-xs-6 text-left',
15430                         cn: {
15431                             tag: 'button',
15432                             cls: 'btn btn-danger roo-touch-view-cancel',
15433                             html: 'Cancel'
15434                         }
15435                     },
15436                     {
15437                         tag: 'div',
15438                         cls: 'col-xs-6 text-right',
15439                         cn: {
15440                             tag: 'button',
15441                             cls: 'btn btn-success roo-touch-view-ok',
15442                             html: 'OK'
15443                         }
15444                     }
15445                 ]
15446             }
15447         ]
15448         
15449     }
15450 });
15451
15452 Roo.apply(Roo.bootstrap.ComboBox,  {
15453     
15454     touchViewTemplate : {
15455         tag: 'div',
15456         cls: 'modal fade roo-combobox-touch-view',
15457         cn: [
15458             {
15459                 tag: 'div',
15460                 cls: 'modal-dialog',
15461                 style : 'position:fixed', // we have to fix position....
15462                 cn: [
15463                     {
15464                         tag: 'div',
15465                         cls: 'modal-content',
15466                         cn: [
15467                             Roo.bootstrap.ComboBox.header,
15468                             Roo.bootstrap.ComboBox.body,
15469                             Roo.bootstrap.ComboBox.footer
15470                         ]
15471                     }
15472                 ]
15473             }
15474         ]
15475     }
15476 });/*
15477  * Based on:
15478  * Ext JS Library 1.1.1
15479  * Copyright(c) 2006-2007, Ext JS, LLC.
15480  *
15481  * Originally Released Under LGPL - original licence link has changed is not relivant.
15482  *
15483  * Fork - LGPL
15484  * <script type="text/javascript">
15485  */
15486
15487 /**
15488  * @class Roo.View
15489  * @extends Roo.util.Observable
15490  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15491  * This class also supports single and multi selection modes. <br>
15492  * Create a data model bound view:
15493  <pre><code>
15494  var store = new Roo.data.Store(...);
15495
15496  var view = new Roo.View({
15497     el : "my-element",
15498     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15499  
15500     singleSelect: true,
15501     selectedClass: "ydataview-selected",
15502     store: store
15503  });
15504
15505  // listen for node click?
15506  view.on("click", function(vw, index, node, e){
15507  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15508  });
15509
15510  // load XML data
15511  dataModel.load("foobar.xml");
15512  </code></pre>
15513  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15514  * <br><br>
15515  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15516  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15517  * 
15518  * Note: old style constructor is still suported (container, template, config)
15519  * 
15520  * @constructor
15521  * Create a new View
15522  * @param {Object} config The config object
15523  * 
15524  */
15525 Roo.View = function(config, depreciated_tpl, depreciated_config){
15526     
15527     this.parent = false;
15528     
15529     if (typeof(depreciated_tpl) == 'undefined') {
15530         // new way.. - universal constructor.
15531         Roo.apply(this, config);
15532         this.el  = Roo.get(this.el);
15533     } else {
15534         // old format..
15535         this.el  = Roo.get(config);
15536         this.tpl = depreciated_tpl;
15537         Roo.apply(this, depreciated_config);
15538     }
15539     this.wrapEl  = this.el.wrap().wrap();
15540     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15541     
15542     
15543     if(typeof(this.tpl) == "string"){
15544         this.tpl = new Roo.Template(this.tpl);
15545     } else {
15546         // support xtype ctors..
15547         this.tpl = new Roo.factory(this.tpl, Roo);
15548     }
15549     
15550     
15551     this.tpl.compile();
15552     
15553     /** @private */
15554     this.addEvents({
15555         /**
15556          * @event beforeclick
15557          * Fires before a click is processed. Returns false to cancel the default action.
15558          * @param {Roo.View} this
15559          * @param {Number} index The index of the target node
15560          * @param {HTMLElement} node The target node
15561          * @param {Roo.EventObject} e The raw event object
15562          */
15563             "beforeclick" : true,
15564         /**
15565          * @event click
15566          * Fires when a template node is clicked.
15567          * @param {Roo.View} this
15568          * @param {Number} index The index of the target node
15569          * @param {HTMLElement} node The target node
15570          * @param {Roo.EventObject} e The raw event object
15571          */
15572             "click" : true,
15573         /**
15574          * @event dblclick
15575          * Fires when a template node is double clicked.
15576          * @param {Roo.View} this
15577          * @param {Number} index The index of the target node
15578          * @param {HTMLElement} node The target node
15579          * @param {Roo.EventObject} e The raw event object
15580          */
15581             "dblclick" : true,
15582         /**
15583          * @event contextmenu
15584          * Fires when a template node is right clicked.
15585          * @param {Roo.View} this
15586          * @param {Number} index The index of the target node
15587          * @param {HTMLElement} node The target node
15588          * @param {Roo.EventObject} e The raw event object
15589          */
15590             "contextmenu" : true,
15591         /**
15592          * @event selectionchange
15593          * Fires when the selected nodes change.
15594          * @param {Roo.View} this
15595          * @param {Array} selections Array of the selected nodes
15596          */
15597             "selectionchange" : true,
15598     
15599         /**
15600          * @event beforeselect
15601          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15602          * @param {Roo.View} this
15603          * @param {HTMLElement} node The node to be selected
15604          * @param {Array} selections Array of currently selected nodes
15605          */
15606             "beforeselect" : true,
15607         /**
15608          * @event preparedata
15609          * Fires on every row to render, to allow you to change the data.
15610          * @param {Roo.View} this
15611          * @param {Object} data to be rendered (change this)
15612          */
15613           "preparedata" : true
15614           
15615           
15616         });
15617
15618
15619
15620     this.el.on({
15621         "click": this.onClick,
15622         "dblclick": this.onDblClick,
15623         "contextmenu": this.onContextMenu,
15624         scope:this
15625     });
15626
15627     this.selections = [];
15628     this.nodes = [];
15629     this.cmp = new Roo.CompositeElementLite([]);
15630     if(this.store){
15631         this.store = Roo.factory(this.store, Roo.data);
15632         this.setStore(this.store, true);
15633     }
15634     
15635     if ( this.footer && this.footer.xtype) {
15636            
15637          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15638         
15639         this.footer.dataSource = this.store;
15640         this.footer.container = fctr;
15641         this.footer = Roo.factory(this.footer, Roo);
15642         fctr.insertFirst(this.el);
15643         
15644         // this is a bit insane - as the paging toolbar seems to detach the el..
15645 //        dom.parentNode.parentNode.parentNode
15646          // they get detached?
15647     }
15648     
15649     
15650     Roo.View.superclass.constructor.call(this);
15651     
15652     
15653 };
15654
15655 Roo.extend(Roo.View, Roo.util.Observable, {
15656     
15657      /**
15658      * @cfg {Roo.data.Store} store Data store to load data from.
15659      */
15660     store : false,
15661     
15662     /**
15663      * @cfg {String|Roo.Element} el The container element.
15664      */
15665     el : '',
15666     
15667     /**
15668      * @cfg {String|Roo.Template} tpl The template used by this View 
15669      */
15670     tpl : false,
15671     /**
15672      * @cfg {String} dataName the named area of the template to use as the data area
15673      *                          Works with domtemplates roo-name="name"
15674      */
15675     dataName: false,
15676     /**
15677      * @cfg {String} selectedClass The css class to add to selected nodes
15678      */
15679     selectedClass : "x-view-selected",
15680      /**
15681      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15682      */
15683     emptyText : "",
15684     
15685     /**
15686      * @cfg {String} text to display on mask (default Loading)
15687      */
15688     mask : false,
15689     /**
15690      * @cfg {Boolean} multiSelect Allow multiple selection
15691      */
15692     multiSelect : false,
15693     /**
15694      * @cfg {Boolean} singleSelect Allow single selection
15695      */
15696     singleSelect:  false,
15697     
15698     /**
15699      * @cfg {Boolean} toggleSelect - selecting 
15700      */
15701     toggleSelect : false,
15702     
15703     /**
15704      * @cfg {Boolean} tickable - selecting 
15705      */
15706     tickable : false,
15707     
15708     /**
15709      * Returns the element this view is bound to.
15710      * @return {Roo.Element}
15711      */
15712     getEl : function(){
15713         return this.wrapEl;
15714     },
15715     
15716     
15717
15718     /**
15719      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15720      */
15721     refresh : function(){
15722         //Roo.log('refresh');
15723         var t = this.tpl;
15724         
15725         // if we are using something like 'domtemplate', then
15726         // the what gets used is:
15727         // t.applySubtemplate(NAME, data, wrapping data..)
15728         // the outer template then get' applied with
15729         //     the store 'extra data'
15730         // and the body get's added to the
15731         //      roo-name="data" node?
15732         //      <span class='roo-tpl-{name}'></span> ?????
15733         
15734         
15735         
15736         this.clearSelections();
15737         this.el.update("");
15738         var html = [];
15739         var records = this.store.getRange();
15740         if(records.length < 1) {
15741             
15742             // is this valid??  = should it render a template??
15743             
15744             this.el.update(this.emptyText);
15745             return;
15746         }
15747         var el = this.el;
15748         if (this.dataName) {
15749             this.el.update(t.apply(this.store.meta)); //????
15750             el = this.el.child('.roo-tpl-' + this.dataName);
15751         }
15752         
15753         for(var i = 0, len = records.length; i < len; i++){
15754             var data = this.prepareData(records[i].data, i, records[i]);
15755             this.fireEvent("preparedata", this, data, i, records[i]);
15756             
15757             var d = Roo.apply({}, data);
15758             
15759             if(this.tickable){
15760                 Roo.apply(d, {'roo-id' : Roo.id()});
15761                 
15762                 var _this = this;
15763             
15764                 Roo.each(this.parent.item, function(item){
15765                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15766                         return;
15767                     }
15768                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15769                 });
15770             }
15771             
15772             html[html.length] = Roo.util.Format.trim(
15773                 this.dataName ?
15774                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15775                     t.apply(d)
15776             );
15777         }
15778         
15779         
15780         
15781         el.update(html.join(""));
15782         this.nodes = el.dom.childNodes;
15783         this.updateIndexes(0);
15784     },
15785     
15786
15787     /**
15788      * Function to override to reformat the data that is sent to
15789      * the template for each node.
15790      * DEPRICATED - use the preparedata event handler.
15791      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15792      * a JSON object for an UpdateManager bound view).
15793      */
15794     prepareData : function(data, index, record)
15795     {
15796         this.fireEvent("preparedata", this, data, index, record);
15797         return data;
15798     },
15799
15800     onUpdate : function(ds, record){
15801         // Roo.log('on update');   
15802         this.clearSelections();
15803         var index = this.store.indexOf(record);
15804         var n = this.nodes[index];
15805         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15806         n.parentNode.removeChild(n);
15807         this.updateIndexes(index, index);
15808     },
15809
15810     
15811     
15812 // --------- FIXME     
15813     onAdd : function(ds, records, index)
15814     {
15815         //Roo.log(['on Add', ds, records, index] );        
15816         this.clearSelections();
15817         if(this.nodes.length == 0){
15818             this.refresh();
15819             return;
15820         }
15821         var n = this.nodes[index];
15822         for(var i = 0, len = records.length; i < len; i++){
15823             var d = this.prepareData(records[i].data, i, records[i]);
15824             if(n){
15825                 this.tpl.insertBefore(n, d);
15826             }else{
15827                 
15828                 this.tpl.append(this.el, d);
15829             }
15830         }
15831         this.updateIndexes(index);
15832     },
15833
15834     onRemove : function(ds, record, index){
15835        // Roo.log('onRemove');
15836         this.clearSelections();
15837         var el = this.dataName  ?
15838             this.el.child('.roo-tpl-' + this.dataName) :
15839             this.el; 
15840         
15841         el.dom.removeChild(this.nodes[index]);
15842         this.updateIndexes(index);
15843     },
15844
15845     /**
15846      * Refresh an individual node.
15847      * @param {Number} index
15848      */
15849     refreshNode : function(index){
15850         this.onUpdate(this.store, this.store.getAt(index));
15851     },
15852
15853     updateIndexes : function(startIndex, endIndex){
15854         var ns = this.nodes;
15855         startIndex = startIndex || 0;
15856         endIndex = endIndex || ns.length - 1;
15857         for(var i = startIndex; i <= endIndex; i++){
15858             ns[i].nodeIndex = i;
15859         }
15860     },
15861
15862     /**
15863      * Changes the data store this view uses and refresh the view.
15864      * @param {Store} store
15865      */
15866     setStore : function(store, initial){
15867         if(!initial && this.store){
15868             this.store.un("datachanged", this.refresh);
15869             this.store.un("add", this.onAdd);
15870             this.store.un("remove", this.onRemove);
15871             this.store.un("update", this.onUpdate);
15872             this.store.un("clear", this.refresh);
15873             this.store.un("beforeload", this.onBeforeLoad);
15874             this.store.un("load", this.onLoad);
15875             this.store.un("loadexception", this.onLoad);
15876         }
15877         if(store){
15878           
15879             store.on("datachanged", this.refresh, this);
15880             store.on("add", this.onAdd, this);
15881             store.on("remove", this.onRemove, this);
15882             store.on("update", this.onUpdate, this);
15883             store.on("clear", this.refresh, this);
15884             store.on("beforeload", this.onBeforeLoad, this);
15885             store.on("load", this.onLoad, this);
15886             store.on("loadexception", this.onLoad, this);
15887         }
15888         
15889         if(store){
15890             this.refresh();
15891         }
15892     },
15893     /**
15894      * onbeforeLoad - masks the loading area.
15895      *
15896      */
15897     onBeforeLoad : function(store,opts)
15898     {
15899          //Roo.log('onBeforeLoad');   
15900         if (!opts.add) {
15901             this.el.update("");
15902         }
15903         this.el.mask(this.mask ? this.mask : "Loading" ); 
15904     },
15905     onLoad : function ()
15906     {
15907         this.el.unmask();
15908     },
15909     
15910
15911     /**
15912      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15913      * @param {HTMLElement} node
15914      * @return {HTMLElement} The template node
15915      */
15916     findItemFromChild : function(node){
15917         var el = this.dataName  ?
15918             this.el.child('.roo-tpl-' + this.dataName,true) :
15919             this.el.dom; 
15920         
15921         if(!node || node.parentNode == el){
15922                     return node;
15923             }
15924             var p = node.parentNode;
15925             while(p && p != el){
15926             if(p.parentNode == el){
15927                 return p;
15928             }
15929             p = p.parentNode;
15930         }
15931             return null;
15932     },
15933
15934     /** @ignore */
15935     onClick : function(e){
15936         var item = this.findItemFromChild(e.getTarget());
15937         if(item){
15938             var index = this.indexOf(item);
15939             if(this.onItemClick(item, index, e) !== false){
15940                 this.fireEvent("click", this, index, item, e);
15941             }
15942         }else{
15943             this.clearSelections();
15944         }
15945     },
15946
15947     /** @ignore */
15948     onContextMenu : function(e){
15949         var item = this.findItemFromChild(e.getTarget());
15950         if(item){
15951             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15952         }
15953     },
15954
15955     /** @ignore */
15956     onDblClick : function(e){
15957         var item = this.findItemFromChild(e.getTarget());
15958         if(item){
15959             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15960         }
15961     },
15962
15963     onItemClick : function(item, index, e)
15964     {
15965         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15966             return false;
15967         }
15968         if (this.toggleSelect) {
15969             var m = this.isSelected(item) ? 'unselect' : 'select';
15970             //Roo.log(m);
15971             var _t = this;
15972             _t[m](item, true, false);
15973             return true;
15974         }
15975         if(this.multiSelect || this.singleSelect){
15976             if(this.multiSelect && e.shiftKey && this.lastSelection){
15977                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15978             }else{
15979                 this.select(item, this.multiSelect && e.ctrlKey);
15980                 this.lastSelection = item;
15981             }
15982             
15983             if(!this.tickable){
15984                 e.preventDefault();
15985             }
15986             
15987         }
15988         return true;
15989     },
15990
15991     /**
15992      * Get the number of selected nodes.
15993      * @return {Number}
15994      */
15995     getSelectionCount : function(){
15996         return this.selections.length;
15997     },
15998
15999     /**
16000      * Get the currently selected nodes.
16001      * @return {Array} An array of HTMLElements
16002      */
16003     getSelectedNodes : function(){
16004         return this.selections;
16005     },
16006
16007     /**
16008      * Get the indexes of the selected nodes.
16009      * @return {Array}
16010      */
16011     getSelectedIndexes : function(){
16012         var indexes = [], s = this.selections;
16013         for(var i = 0, len = s.length; i < len; i++){
16014             indexes.push(s[i].nodeIndex);
16015         }
16016         return indexes;
16017     },
16018
16019     /**
16020      * Clear all selections
16021      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16022      */
16023     clearSelections : function(suppressEvent){
16024         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16025             this.cmp.elements = this.selections;
16026             this.cmp.removeClass(this.selectedClass);
16027             this.selections = [];
16028             if(!suppressEvent){
16029                 this.fireEvent("selectionchange", this, this.selections);
16030             }
16031         }
16032     },
16033
16034     /**
16035      * Returns true if the passed node is selected
16036      * @param {HTMLElement/Number} node The node or node index
16037      * @return {Boolean}
16038      */
16039     isSelected : function(node){
16040         var s = this.selections;
16041         if(s.length < 1){
16042             return false;
16043         }
16044         node = this.getNode(node);
16045         return s.indexOf(node) !== -1;
16046     },
16047
16048     /**
16049      * Selects nodes.
16050      * @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
16051      * @param {Boolean} keepExisting (optional) true to keep existing selections
16052      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16053      */
16054     select : function(nodeInfo, keepExisting, suppressEvent){
16055         if(nodeInfo instanceof Array){
16056             if(!keepExisting){
16057                 this.clearSelections(true);
16058             }
16059             for(var i = 0, len = nodeInfo.length; i < len; i++){
16060                 this.select(nodeInfo[i], true, true);
16061             }
16062             return;
16063         } 
16064         var node = this.getNode(nodeInfo);
16065         if(!node || this.isSelected(node)){
16066             return; // already selected.
16067         }
16068         if(!keepExisting){
16069             this.clearSelections(true);
16070         }
16071         
16072         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16073             Roo.fly(node).addClass(this.selectedClass);
16074             this.selections.push(node);
16075             if(!suppressEvent){
16076                 this.fireEvent("selectionchange", this, this.selections);
16077             }
16078         }
16079         
16080         
16081     },
16082       /**
16083      * Unselects nodes.
16084      * @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
16085      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16086      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16087      */
16088     unselect : function(nodeInfo, keepExisting, suppressEvent)
16089     {
16090         if(nodeInfo instanceof Array){
16091             Roo.each(this.selections, function(s) {
16092                 this.unselect(s, nodeInfo);
16093             }, this);
16094             return;
16095         }
16096         var node = this.getNode(nodeInfo);
16097         if(!node || !this.isSelected(node)){
16098             //Roo.log("not selected");
16099             return; // not selected.
16100         }
16101         // fireevent???
16102         var ns = [];
16103         Roo.each(this.selections, function(s) {
16104             if (s == node ) {
16105                 Roo.fly(node).removeClass(this.selectedClass);
16106
16107                 return;
16108             }
16109             ns.push(s);
16110         },this);
16111         
16112         this.selections= ns;
16113         this.fireEvent("selectionchange", this, this.selections);
16114     },
16115
16116     /**
16117      * Gets a template node.
16118      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16119      * @return {HTMLElement} The node or null if it wasn't found
16120      */
16121     getNode : function(nodeInfo){
16122         if(typeof nodeInfo == "string"){
16123             return document.getElementById(nodeInfo);
16124         }else if(typeof nodeInfo == "number"){
16125             return this.nodes[nodeInfo];
16126         }
16127         return nodeInfo;
16128     },
16129
16130     /**
16131      * Gets a range template nodes.
16132      * @param {Number} startIndex
16133      * @param {Number} endIndex
16134      * @return {Array} An array of nodes
16135      */
16136     getNodes : function(start, end){
16137         var ns = this.nodes;
16138         start = start || 0;
16139         end = typeof end == "undefined" ? ns.length - 1 : end;
16140         var nodes = [];
16141         if(start <= end){
16142             for(var i = start; i <= end; i++){
16143                 nodes.push(ns[i]);
16144             }
16145         } else{
16146             for(var i = start; i >= end; i--){
16147                 nodes.push(ns[i]);
16148             }
16149         }
16150         return nodes;
16151     },
16152
16153     /**
16154      * Finds the index of the passed node
16155      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16156      * @return {Number} The index of the node or -1
16157      */
16158     indexOf : function(node){
16159         node = this.getNode(node);
16160         if(typeof node.nodeIndex == "number"){
16161             return node.nodeIndex;
16162         }
16163         var ns = this.nodes;
16164         for(var i = 0, len = ns.length; i < len; i++){
16165             if(ns[i] == node){
16166                 return i;
16167             }
16168         }
16169         return -1;
16170     }
16171 });
16172 /*
16173  * - LGPL
16174  *
16175  * based on jquery fullcalendar
16176  * 
16177  */
16178
16179 Roo.bootstrap = Roo.bootstrap || {};
16180 /**
16181  * @class Roo.bootstrap.Calendar
16182  * @extends Roo.bootstrap.Component
16183  * Bootstrap Calendar class
16184  * @cfg {Boolean} loadMask (true|false) default false
16185  * @cfg {Object} header generate the user specific header of the calendar, default false
16186
16187  * @constructor
16188  * Create a new Container
16189  * @param {Object} config The config object
16190  */
16191
16192
16193
16194 Roo.bootstrap.Calendar = function(config){
16195     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16196      this.addEvents({
16197         /**
16198              * @event select
16199              * Fires when a date is selected
16200              * @param {DatePicker} this
16201              * @param {Date} date The selected date
16202              */
16203         'select': true,
16204         /**
16205              * @event monthchange
16206              * Fires when the displayed month changes 
16207              * @param {DatePicker} this
16208              * @param {Date} date The selected month
16209              */
16210         'monthchange': true,
16211         /**
16212              * @event evententer
16213              * Fires when mouse over an event
16214              * @param {Calendar} this
16215              * @param {event} Event
16216              */
16217         'evententer': true,
16218         /**
16219              * @event eventleave
16220              * Fires when the mouse leaves an
16221              * @param {Calendar} this
16222              * @param {event}
16223              */
16224         'eventleave': true,
16225         /**
16226              * @event eventclick
16227              * Fires when the mouse click an
16228              * @param {Calendar} this
16229              * @param {event}
16230              */
16231         'eventclick': true
16232         
16233     });
16234
16235 };
16236
16237 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16238     
16239      /**
16240      * @cfg {Number} startDay
16241      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16242      */
16243     startDay : 0,
16244     
16245     loadMask : false,
16246     
16247     header : false,
16248       
16249     getAutoCreate : function(){
16250         
16251         
16252         var fc_button = function(name, corner, style, content ) {
16253             return Roo.apply({},{
16254                 tag : 'span',
16255                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16256                          (corner.length ?
16257                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16258                             ''
16259                         ),
16260                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16261                 unselectable: 'on'
16262             });
16263         };
16264         
16265         var header = {};
16266         
16267         if(!this.header){
16268             header = {
16269                 tag : 'table',
16270                 cls : 'fc-header',
16271                 style : 'width:100%',
16272                 cn : [
16273                     {
16274                         tag: 'tr',
16275                         cn : [
16276                             {
16277                                 tag : 'td',
16278                                 cls : 'fc-header-left',
16279                                 cn : [
16280                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16281                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16282                                     { tag: 'span', cls: 'fc-header-space' },
16283                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16284
16285
16286                                 ]
16287                             },
16288
16289                             {
16290                                 tag : 'td',
16291                                 cls : 'fc-header-center',
16292                                 cn : [
16293                                     {
16294                                         tag: 'span',
16295                                         cls: 'fc-header-title',
16296                                         cn : {
16297                                             tag: 'H2',
16298                                             html : 'month / year'
16299                                         }
16300                                     }
16301
16302                                 ]
16303                             },
16304                             {
16305                                 tag : 'td',
16306                                 cls : 'fc-header-right',
16307                                 cn : [
16308                               /*      fc_button('month', 'left', '', 'month' ),
16309                                     fc_button('week', '', '', 'week' ),
16310                                     fc_button('day', 'right', '', 'day' )
16311                                 */    
16312
16313                                 ]
16314                             }
16315
16316                         ]
16317                     }
16318                 ]
16319             };
16320         }
16321         
16322         header = this.header;
16323         
16324        
16325         var cal_heads = function() {
16326             var ret = [];
16327             // fixme - handle this.
16328             
16329             for (var i =0; i < Date.dayNames.length; i++) {
16330                 var d = Date.dayNames[i];
16331                 ret.push({
16332                     tag: 'th',
16333                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16334                     html : d.substring(0,3)
16335                 });
16336                 
16337             }
16338             ret[0].cls += ' fc-first';
16339             ret[6].cls += ' fc-last';
16340             return ret;
16341         };
16342         var cal_cell = function(n) {
16343             return  {
16344                 tag: 'td',
16345                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16346                 cn : [
16347                     {
16348                         cn : [
16349                             {
16350                                 cls: 'fc-day-number',
16351                                 html: 'D'
16352                             },
16353                             {
16354                                 cls: 'fc-day-content',
16355                              
16356                                 cn : [
16357                                      {
16358                                         style: 'position: relative;' // height: 17px;
16359                                     }
16360                                 ]
16361                             }
16362                             
16363                             
16364                         ]
16365                     }
16366                 ]
16367                 
16368             }
16369         };
16370         var cal_rows = function() {
16371             
16372             var ret = [];
16373             for (var r = 0; r < 6; r++) {
16374                 var row= {
16375                     tag : 'tr',
16376                     cls : 'fc-week',
16377                     cn : []
16378                 };
16379                 
16380                 for (var i =0; i < Date.dayNames.length; i++) {
16381                     var d = Date.dayNames[i];
16382                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16383
16384                 }
16385                 row.cn[0].cls+=' fc-first';
16386                 row.cn[0].cn[0].style = 'min-height:90px';
16387                 row.cn[6].cls+=' fc-last';
16388                 ret.push(row);
16389                 
16390             }
16391             ret[0].cls += ' fc-first';
16392             ret[4].cls += ' fc-prev-last';
16393             ret[5].cls += ' fc-last';
16394             return ret;
16395             
16396         };
16397         
16398         var cal_table = {
16399             tag: 'table',
16400             cls: 'fc-border-separate',
16401             style : 'width:100%',
16402             cellspacing  : 0,
16403             cn : [
16404                 { 
16405                     tag: 'thead',
16406                     cn : [
16407                         { 
16408                             tag: 'tr',
16409                             cls : 'fc-first fc-last',
16410                             cn : cal_heads()
16411                         }
16412                     ]
16413                 },
16414                 { 
16415                     tag: 'tbody',
16416                     cn : cal_rows()
16417                 }
16418                   
16419             ]
16420         };
16421          
16422          var cfg = {
16423             cls : 'fc fc-ltr',
16424             cn : [
16425                 header,
16426                 {
16427                     cls : 'fc-content',
16428                     style : "position: relative;",
16429                     cn : [
16430                         {
16431                             cls : 'fc-view fc-view-month fc-grid',
16432                             style : 'position: relative',
16433                             unselectable : 'on',
16434                             cn : [
16435                                 {
16436                                     cls : 'fc-event-container',
16437                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16438                                 },
16439                                 cal_table
16440                             ]
16441                         }
16442                     ]
16443     
16444                 }
16445            ] 
16446             
16447         };
16448         
16449          
16450         
16451         return cfg;
16452     },
16453     
16454     
16455     initEvents : function()
16456     {
16457         if(!this.store){
16458             throw "can not find store for calendar";
16459         }
16460         
16461         var mark = {
16462             tag: "div",
16463             cls:"x-dlg-mask",
16464             style: "text-align:center",
16465             cn: [
16466                 {
16467                     tag: "div",
16468                     style: "background-color:white;width:50%;margin:250 auto",
16469                     cn: [
16470                         {
16471                             tag: "img",
16472                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16473                         },
16474                         {
16475                             tag: "span",
16476                             html: "Loading"
16477                         }
16478                         
16479                     ]
16480                 }
16481             ]
16482         };
16483         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16484         
16485         var size = this.el.select('.fc-content', true).first().getSize();
16486         this.maskEl.setSize(size.width, size.height);
16487         this.maskEl.enableDisplayMode("block");
16488         if(!this.loadMask){
16489             this.maskEl.hide();
16490         }
16491         
16492         this.store = Roo.factory(this.store, Roo.data);
16493         this.store.on('load', this.onLoad, this);
16494         this.store.on('beforeload', this.onBeforeLoad, this);
16495         
16496         this.resize();
16497         
16498         this.cells = this.el.select('.fc-day',true);
16499         //Roo.log(this.cells);
16500         this.textNodes = this.el.query('.fc-day-number');
16501         this.cells.addClassOnOver('fc-state-hover');
16502         
16503         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16504         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16505         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16506         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16507         
16508         this.on('monthchange', this.onMonthChange, this);
16509         
16510         this.update(new Date().clearTime());
16511     },
16512     
16513     resize : function() {
16514         var sz  = this.el.getSize();
16515         
16516         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16517         this.el.select('.fc-day-content div',true).setHeight(34);
16518     },
16519     
16520     
16521     // private
16522     showPrevMonth : function(e){
16523         this.update(this.activeDate.add("mo", -1));
16524     },
16525     showToday : function(e){
16526         this.update(new Date().clearTime());
16527     },
16528     // private
16529     showNextMonth : function(e){
16530         this.update(this.activeDate.add("mo", 1));
16531     },
16532
16533     // private
16534     showPrevYear : function(){
16535         this.update(this.activeDate.add("y", -1));
16536     },
16537
16538     // private
16539     showNextYear : function(){
16540         this.update(this.activeDate.add("y", 1));
16541     },
16542
16543     
16544    // private
16545     update : function(date)
16546     {
16547         var vd = this.activeDate;
16548         this.activeDate = date;
16549 //        if(vd && this.el){
16550 //            var t = date.getTime();
16551 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16552 //                Roo.log('using add remove');
16553 //                
16554 //                this.fireEvent('monthchange', this, date);
16555 //                
16556 //                this.cells.removeClass("fc-state-highlight");
16557 //                this.cells.each(function(c){
16558 //                   if(c.dateValue == t){
16559 //                       c.addClass("fc-state-highlight");
16560 //                       setTimeout(function(){
16561 //                            try{c.dom.firstChild.focus();}catch(e){}
16562 //                       }, 50);
16563 //                       return false;
16564 //                   }
16565 //                   return true;
16566 //                });
16567 //                return;
16568 //            }
16569 //        }
16570         
16571         var days = date.getDaysInMonth();
16572         
16573         var firstOfMonth = date.getFirstDateOfMonth();
16574         var startingPos = firstOfMonth.getDay()-this.startDay;
16575         
16576         if(startingPos < this.startDay){
16577             startingPos += 7;
16578         }
16579         
16580         var pm = date.add(Date.MONTH, -1);
16581         var prevStart = pm.getDaysInMonth()-startingPos;
16582 //        
16583         this.cells = this.el.select('.fc-day',true);
16584         this.textNodes = this.el.query('.fc-day-number');
16585         this.cells.addClassOnOver('fc-state-hover');
16586         
16587         var cells = this.cells.elements;
16588         var textEls = this.textNodes;
16589         
16590         Roo.each(cells, function(cell){
16591             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16592         });
16593         
16594         days += startingPos;
16595
16596         // convert everything to numbers so it's fast
16597         var day = 86400000;
16598         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16599         //Roo.log(d);
16600         //Roo.log(pm);
16601         //Roo.log(prevStart);
16602         
16603         var today = new Date().clearTime().getTime();
16604         var sel = date.clearTime().getTime();
16605         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16606         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16607         var ddMatch = this.disabledDatesRE;
16608         var ddText = this.disabledDatesText;
16609         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16610         var ddaysText = this.disabledDaysText;
16611         var format = this.format;
16612         
16613         var setCellClass = function(cal, cell){
16614             cell.row = 0;
16615             cell.events = [];
16616             cell.more = [];
16617             //Roo.log('set Cell Class');
16618             cell.title = "";
16619             var t = d.getTime();
16620             
16621             //Roo.log(d);
16622             
16623             cell.dateValue = t;
16624             if(t == today){
16625                 cell.className += " fc-today";
16626                 cell.className += " fc-state-highlight";
16627                 cell.title = cal.todayText;
16628             }
16629             if(t == sel){
16630                 // disable highlight in other month..
16631                 //cell.className += " fc-state-highlight";
16632                 
16633             }
16634             // disabling
16635             if(t < min) {
16636                 cell.className = " fc-state-disabled";
16637                 cell.title = cal.minText;
16638                 return;
16639             }
16640             if(t > max) {
16641                 cell.className = " fc-state-disabled";
16642                 cell.title = cal.maxText;
16643                 return;
16644             }
16645             if(ddays){
16646                 if(ddays.indexOf(d.getDay()) != -1){
16647                     cell.title = ddaysText;
16648                     cell.className = " fc-state-disabled";
16649                 }
16650             }
16651             if(ddMatch && format){
16652                 var fvalue = d.dateFormat(format);
16653                 if(ddMatch.test(fvalue)){
16654                     cell.title = ddText.replace("%0", fvalue);
16655                     cell.className = " fc-state-disabled";
16656                 }
16657             }
16658             
16659             if (!cell.initialClassName) {
16660                 cell.initialClassName = cell.dom.className;
16661             }
16662             
16663             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16664         };
16665
16666         var i = 0;
16667         
16668         for(; i < startingPos; i++) {
16669             textEls[i].innerHTML = (++prevStart);
16670             d.setDate(d.getDate()+1);
16671             
16672             cells[i].className = "fc-past fc-other-month";
16673             setCellClass(this, cells[i]);
16674         }
16675         
16676         var intDay = 0;
16677         
16678         for(; i < days; i++){
16679             intDay = i - startingPos + 1;
16680             textEls[i].innerHTML = (intDay);
16681             d.setDate(d.getDate()+1);
16682             
16683             cells[i].className = ''; // "x-date-active";
16684             setCellClass(this, cells[i]);
16685         }
16686         var extraDays = 0;
16687         
16688         for(; i < 42; i++) {
16689             textEls[i].innerHTML = (++extraDays);
16690             d.setDate(d.getDate()+1);
16691             
16692             cells[i].className = "fc-future fc-other-month";
16693             setCellClass(this, cells[i]);
16694         }
16695         
16696         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16697         
16698         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16699         
16700         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16701         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16702         
16703         if(totalRows != 6){
16704             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16705             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16706         }
16707         
16708         this.fireEvent('monthchange', this, date);
16709         
16710         
16711         /*
16712         if(!this.internalRender){
16713             var main = this.el.dom.firstChild;
16714             var w = main.offsetWidth;
16715             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16716             Roo.fly(main).setWidth(w);
16717             this.internalRender = true;
16718             // opera does not respect the auto grow header center column
16719             // then, after it gets a width opera refuses to recalculate
16720             // without a second pass
16721             if(Roo.isOpera && !this.secondPass){
16722                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16723                 this.secondPass = true;
16724                 this.update.defer(10, this, [date]);
16725             }
16726         }
16727         */
16728         
16729     },
16730     
16731     findCell : function(dt) {
16732         dt = dt.clearTime().getTime();
16733         var ret = false;
16734         this.cells.each(function(c){
16735             //Roo.log("check " +c.dateValue + '?=' + dt);
16736             if(c.dateValue == dt){
16737                 ret = c;
16738                 return false;
16739             }
16740             return true;
16741         });
16742         
16743         return ret;
16744     },
16745     
16746     findCells : function(ev) {
16747         var s = ev.start.clone().clearTime().getTime();
16748        // Roo.log(s);
16749         var e= ev.end.clone().clearTime().getTime();
16750        // Roo.log(e);
16751         var ret = [];
16752         this.cells.each(function(c){
16753              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16754             
16755             if(c.dateValue > e){
16756                 return ;
16757             }
16758             if(c.dateValue < s){
16759                 return ;
16760             }
16761             ret.push(c);
16762         });
16763         
16764         return ret;    
16765     },
16766     
16767 //    findBestRow: function(cells)
16768 //    {
16769 //        var ret = 0;
16770 //        
16771 //        for (var i =0 ; i < cells.length;i++) {
16772 //            ret  = Math.max(cells[i].rows || 0,ret);
16773 //        }
16774 //        return ret;
16775 //        
16776 //    },
16777     
16778     
16779     addItem : function(ev)
16780     {
16781         // look for vertical location slot in
16782         var cells = this.findCells(ev);
16783         
16784 //        ev.row = this.findBestRow(cells);
16785         
16786         // work out the location.
16787         
16788         var crow = false;
16789         var rows = [];
16790         for(var i =0; i < cells.length; i++) {
16791             
16792             cells[i].row = cells[0].row;
16793             
16794             if(i == 0){
16795                 cells[i].row = cells[i].row + 1;
16796             }
16797             
16798             if (!crow) {
16799                 crow = {
16800                     start : cells[i],
16801                     end :  cells[i]
16802                 };
16803                 continue;
16804             }
16805             if (crow.start.getY() == cells[i].getY()) {
16806                 // on same row.
16807                 crow.end = cells[i];
16808                 continue;
16809             }
16810             // different row.
16811             rows.push(crow);
16812             crow = {
16813                 start: cells[i],
16814                 end : cells[i]
16815             };
16816             
16817         }
16818         
16819         rows.push(crow);
16820         ev.els = [];
16821         ev.rows = rows;
16822         ev.cells = cells;
16823         
16824         cells[0].events.push(ev);
16825         
16826         this.calevents.push(ev);
16827     },
16828     
16829     clearEvents: function() {
16830         
16831         if(!this.calevents){
16832             return;
16833         }
16834         
16835         Roo.each(this.cells.elements, function(c){
16836             c.row = 0;
16837             c.events = [];
16838             c.more = [];
16839         });
16840         
16841         Roo.each(this.calevents, function(e) {
16842             Roo.each(e.els, function(el) {
16843                 el.un('mouseenter' ,this.onEventEnter, this);
16844                 el.un('mouseleave' ,this.onEventLeave, this);
16845                 el.remove();
16846             },this);
16847         },this);
16848         
16849         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16850             e.remove();
16851         });
16852         
16853     },
16854     
16855     renderEvents: function()
16856     {   
16857         var _this = this;
16858         
16859         this.cells.each(function(c) {
16860             
16861             if(c.row < 5){
16862                 return;
16863             }
16864             
16865             var ev = c.events;
16866             
16867             var r = 4;
16868             if(c.row != c.events.length){
16869                 r = 4 - (4 - (c.row - c.events.length));
16870             }
16871             
16872             c.events = ev.slice(0, r);
16873             c.more = ev.slice(r);
16874             
16875             if(c.more.length && c.more.length == 1){
16876                 c.events.push(c.more.pop());
16877             }
16878             
16879             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16880             
16881         });
16882             
16883         this.cells.each(function(c) {
16884             
16885             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16886             
16887             
16888             for (var e = 0; e < c.events.length; e++){
16889                 var ev = c.events[e];
16890                 var rows = ev.rows;
16891                 
16892                 for(var i = 0; i < rows.length; i++) {
16893                 
16894                     // how many rows should it span..
16895
16896                     var  cfg = {
16897                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16898                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16899
16900                         unselectable : "on",
16901                         cn : [
16902                             {
16903                                 cls: 'fc-event-inner',
16904                                 cn : [
16905     //                                {
16906     //                                  tag:'span',
16907     //                                  cls: 'fc-event-time',
16908     //                                  html : cells.length > 1 ? '' : ev.time
16909     //                                },
16910                                     {
16911                                       tag:'span',
16912                                       cls: 'fc-event-title',
16913                                       html : String.format('{0}', ev.title)
16914                                     }
16915
16916
16917                                 ]
16918                             },
16919                             {
16920                                 cls: 'ui-resizable-handle ui-resizable-e',
16921                                 html : '&nbsp;&nbsp;&nbsp'
16922                             }
16923
16924                         ]
16925                     };
16926
16927                     if (i == 0) {
16928                         cfg.cls += ' fc-event-start';
16929                     }
16930                     if ((i+1) == rows.length) {
16931                         cfg.cls += ' fc-event-end';
16932                     }
16933
16934                     var ctr = _this.el.select('.fc-event-container',true).first();
16935                     var cg = ctr.createChild(cfg);
16936
16937                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16938                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16939
16940                     var r = (c.more.length) ? 1 : 0;
16941                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16942                     cg.setWidth(ebox.right - sbox.x -2);
16943
16944                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16945                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16946                     cg.on('click', _this.onEventClick, _this, ev);
16947
16948                     ev.els.push(cg);
16949                     
16950                 }
16951                 
16952             }
16953             
16954             
16955             if(c.more.length){
16956                 var  cfg = {
16957                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16958                     style : 'position: absolute',
16959                     unselectable : "on",
16960                     cn : [
16961                         {
16962                             cls: 'fc-event-inner',
16963                             cn : [
16964                                 {
16965                                   tag:'span',
16966                                   cls: 'fc-event-title',
16967                                   html : 'More'
16968                                 }
16969
16970
16971                             ]
16972                         },
16973                         {
16974                             cls: 'ui-resizable-handle ui-resizable-e',
16975                             html : '&nbsp;&nbsp;&nbsp'
16976                         }
16977
16978                     ]
16979                 };
16980
16981                 var ctr = _this.el.select('.fc-event-container',true).first();
16982                 var cg = ctr.createChild(cfg);
16983
16984                 var sbox = c.select('.fc-day-content',true).first().getBox();
16985                 var ebox = c.select('.fc-day-content',true).first().getBox();
16986                 //Roo.log(cg);
16987                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16988                 cg.setWidth(ebox.right - sbox.x -2);
16989
16990                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16991                 
16992             }
16993             
16994         });
16995         
16996         
16997         
16998     },
16999     
17000     onEventEnter: function (e, el,event,d) {
17001         this.fireEvent('evententer', this, el, event);
17002     },
17003     
17004     onEventLeave: function (e, el,event,d) {
17005         this.fireEvent('eventleave', this, el, event);
17006     },
17007     
17008     onEventClick: function (e, el,event,d) {
17009         this.fireEvent('eventclick', this, el, event);
17010     },
17011     
17012     onMonthChange: function () {
17013         this.store.load();
17014     },
17015     
17016     onMoreEventClick: function(e, el, more)
17017     {
17018         var _this = this;
17019         
17020         this.calpopover.placement = 'right';
17021         this.calpopover.setTitle('More');
17022         
17023         this.calpopover.setContent('');
17024         
17025         var ctr = this.calpopover.el.select('.popover-content', true).first();
17026         
17027         Roo.each(more, function(m){
17028             var cfg = {
17029                 cls : 'fc-event-hori fc-event-draggable',
17030                 html : m.title
17031             };
17032             var cg = ctr.createChild(cfg);
17033             
17034             cg.on('click', _this.onEventClick, _this, m);
17035         });
17036         
17037         this.calpopover.show(el);
17038         
17039         
17040     },
17041     
17042     onLoad: function () 
17043     {   
17044         this.calevents = [];
17045         var cal = this;
17046         
17047         if(this.store.getCount() > 0){
17048             this.store.data.each(function(d){
17049                cal.addItem({
17050                     id : d.data.id,
17051                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17052                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17053                     time : d.data.start_time,
17054                     title : d.data.title,
17055                     description : d.data.description,
17056                     venue : d.data.venue
17057                 });
17058             });
17059         }
17060         
17061         this.renderEvents();
17062         
17063         if(this.calevents.length && this.loadMask){
17064             this.maskEl.hide();
17065         }
17066     },
17067     
17068     onBeforeLoad: function()
17069     {
17070         this.clearEvents();
17071         if(this.loadMask){
17072             this.maskEl.show();
17073         }
17074     }
17075 });
17076
17077  
17078  /*
17079  * - LGPL
17080  *
17081  * element
17082  * 
17083  */
17084
17085 /**
17086  * @class Roo.bootstrap.Popover
17087  * @extends Roo.bootstrap.Component
17088  * Bootstrap Popover class
17089  * @cfg {String} html contents of the popover   (or false to use children..)
17090  * @cfg {String} title of popover (or false to hide)
17091  * @cfg {String} placement how it is placed
17092  * @cfg {String} trigger click || hover (or false to trigger manually)
17093  * @cfg {String} over what (parent or false to trigger manually.)
17094  * @cfg {Number} delay - delay before showing
17095  
17096  * @constructor
17097  * Create a new Popover
17098  * @param {Object} config The config object
17099  */
17100
17101 Roo.bootstrap.Popover = function(config){
17102     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17103     
17104     this.addEvents({
17105         // raw events
17106          /**
17107          * @event show
17108          * After the popover show
17109          * 
17110          * @param {Roo.bootstrap.Popover} this
17111          */
17112         "show" : true,
17113         /**
17114          * @event hide
17115          * After the popover hide
17116          * 
17117          * @param {Roo.bootstrap.Popover} this
17118          */
17119         "hide" : true
17120     });
17121 };
17122
17123 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17124     
17125     title: 'Fill in a title',
17126     html: false,
17127     
17128     placement : 'right',
17129     trigger : 'hover', // hover
17130     
17131     delay : 0,
17132     
17133     over: 'parent',
17134     
17135     can_build_overlaid : false,
17136     
17137     getChildContainer : function()
17138     {
17139         return this.el.select('.popover-content',true).first();
17140     },
17141     
17142     getAutoCreate : function(){
17143          
17144         var cfg = {
17145            cls : 'popover roo-dynamic',
17146            style: 'display:block',
17147            cn : [
17148                 {
17149                     cls : 'arrow'
17150                 },
17151                 {
17152                     cls : 'popover-inner',
17153                     cn : [
17154                         {
17155                             tag: 'h3',
17156                             cls: 'popover-title',
17157                             html : this.title
17158                         },
17159                         {
17160                             cls : 'popover-content',
17161                             html : this.html
17162                         }
17163                     ]
17164                     
17165                 }
17166            ]
17167         };
17168         
17169         return cfg;
17170     },
17171     setTitle: function(str)
17172     {
17173         this.title = str;
17174         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17175     },
17176     setContent: function(str)
17177     {
17178         this.html = str;
17179         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17180     },
17181     // as it get's added to the bottom of the page.
17182     onRender : function(ct, position)
17183     {
17184         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17185         if(!this.el){
17186             var cfg = Roo.apply({},  this.getAutoCreate());
17187             cfg.id = Roo.id();
17188             
17189             if (this.cls) {
17190                 cfg.cls += ' ' + this.cls;
17191             }
17192             if (this.style) {
17193                 cfg.style = this.style;
17194             }
17195             //Roo.log("adding to ");
17196             this.el = Roo.get(document.body).createChild(cfg, position);
17197 //            Roo.log(this.el);
17198         }
17199         this.initEvents();
17200     },
17201     
17202     initEvents : function()
17203     {
17204         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17205         this.el.enableDisplayMode('block');
17206         this.el.hide();
17207         if (this.over === false) {
17208             return; 
17209         }
17210         if (this.triggers === false) {
17211             return;
17212         }
17213         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17214         var triggers = this.trigger ? this.trigger.split(' ') : [];
17215         Roo.each(triggers, function(trigger) {
17216         
17217             if (trigger == 'click') {
17218                 on_el.on('click', this.toggle, this);
17219             } else if (trigger != 'manual') {
17220                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17221                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17222       
17223                 on_el.on(eventIn  ,this.enter, this);
17224                 on_el.on(eventOut, this.leave, this);
17225             }
17226         }, this);
17227         
17228     },
17229     
17230     
17231     // private
17232     timeout : null,
17233     hoverState : null,
17234     
17235     toggle : function () {
17236         this.hoverState == 'in' ? this.leave() : this.enter();
17237     },
17238     
17239     enter : function () {
17240         
17241         clearTimeout(this.timeout);
17242     
17243         this.hoverState = 'in';
17244     
17245         if (!this.delay || !this.delay.show) {
17246             this.show();
17247             return;
17248         }
17249         var _t = this;
17250         this.timeout = setTimeout(function () {
17251             if (_t.hoverState == 'in') {
17252                 _t.show();
17253             }
17254         }, this.delay.show)
17255     },
17256     
17257     leave : function() {
17258         clearTimeout(this.timeout);
17259     
17260         this.hoverState = 'out';
17261     
17262         if (!this.delay || !this.delay.hide) {
17263             this.hide();
17264             return;
17265         }
17266         var _t = this;
17267         this.timeout = setTimeout(function () {
17268             if (_t.hoverState == 'out') {
17269                 _t.hide();
17270             }
17271         }, this.delay.hide)
17272     },
17273     
17274     show : function (on_el)
17275     {
17276         if (!on_el) {
17277             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17278         }
17279         
17280         // set content.
17281         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17282         if (this.html !== false) {
17283             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17284         }
17285         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17286         if (!this.title.length) {
17287             this.el.select('.popover-title',true).hide();
17288         }
17289         
17290         var placement = typeof this.placement == 'function' ?
17291             this.placement.call(this, this.el, on_el) :
17292             this.placement;
17293             
17294         var autoToken = /\s?auto?\s?/i;
17295         var autoPlace = autoToken.test(placement);
17296         if (autoPlace) {
17297             placement = placement.replace(autoToken, '') || 'top';
17298         }
17299         
17300         //this.el.detach()
17301         //this.el.setXY([0,0]);
17302         this.el.show();
17303         this.el.dom.style.display='block';
17304         this.el.addClass(placement);
17305         
17306         //this.el.appendTo(on_el);
17307         
17308         var p = this.getPosition();
17309         var box = this.el.getBox();
17310         
17311         if (autoPlace) {
17312             // fixme..
17313         }
17314         var align = Roo.bootstrap.Popover.alignment[placement];
17315         this.el.alignTo(on_el, align[0],align[1]);
17316         //var arrow = this.el.select('.arrow',true).first();
17317         //arrow.set(align[2], 
17318         
17319         this.el.addClass('in');
17320         
17321         
17322         if (this.el.hasClass('fade')) {
17323             // fade it?
17324         }
17325         
17326         this.hoverState = 'in';
17327         
17328         this.fireEvent('show', this);
17329         
17330     },
17331     hide : function()
17332     {
17333         this.el.setXY([0,0]);
17334         this.el.removeClass('in');
17335         this.el.hide();
17336         this.hoverState = null;
17337         
17338         this.fireEvent('hide', this);
17339     }
17340     
17341 });
17342
17343 Roo.bootstrap.Popover.alignment = {
17344     'left' : ['r-l', [-10,0], 'right'],
17345     'right' : ['l-r', [10,0], 'left'],
17346     'bottom' : ['t-b', [0,10], 'top'],
17347     'top' : [ 'b-t', [0,-10], 'bottom']
17348 };
17349
17350  /*
17351  * - LGPL
17352  *
17353  * Progress
17354  * 
17355  */
17356
17357 /**
17358  * @class Roo.bootstrap.Progress
17359  * @extends Roo.bootstrap.Component
17360  * Bootstrap Progress class
17361  * @cfg {Boolean} striped striped of the progress bar
17362  * @cfg {Boolean} active animated of the progress bar
17363  * 
17364  * 
17365  * @constructor
17366  * Create a new Progress
17367  * @param {Object} config The config object
17368  */
17369
17370 Roo.bootstrap.Progress = function(config){
17371     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17372 };
17373
17374 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17375     
17376     striped : false,
17377     active: false,
17378     
17379     getAutoCreate : function(){
17380         var cfg = {
17381             tag: 'div',
17382             cls: 'progress'
17383         };
17384         
17385         
17386         if(this.striped){
17387             cfg.cls += ' progress-striped';
17388         }
17389       
17390         if(this.active){
17391             cfg.cls += ' active';
17392         }
17393         
17394         
17395         return cfg;
17396     }
17397    
17398 });
17399
17400  
17401
17402  /*
17403  * - LGPL
17404  *
17405  * ProgressBar
17406  * 
17407  */
17408
17409 /**
17410  * @class Roo.bootstrap.ProgressBar
17411  * @extends Roo.bootstrap.Component
17412  * Bootstrap ProgressBar class
17413  * @cfg {Number} aria_valuenow aria-value now
17414  * @cfg {Number} aria_valuemin aria-value min
17415  * @cfg {Number} aria_valuemax aria-value max
17416  * @cfg {String} label label for the progress bar
17417  * @cfg {String} panel (success | info | warning | danger )
17418  * @cfg {String} role role of the progress bar
17419  * @cfg {String} sr_only text
17420  * 
17421  * 
17422  * @constructor
17423  * Create a new ProgressBar
17424  * @param {Object} config The config object
17425  */
17426
17427 Roo.bootstrap.ProgressBar = function(config){
17428     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17429 };
17430
17431 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17432     
17433     aria_valuenow : 0,
17434     aria_valuemin : 0,
17435     aria_valuemax : 100,
17436     label : false,
17437     panel : false,
17438     role : false,
17439     sr_only: false,
17440     
17441     getAutoCreate : function()
17442     {
17443         
17444         var cfg = {
17445             tag: 'div',
17446             cls: 'progress-bar',
17447             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17448         };
17449         
17450         if(this.sr_only){
17451             cfg.cn = {
17452                 tag: 'span',
17453                 cls: 'sr-only',
17454                 html: this.sr_only
17455             }
17456         }
17457         
17458         if(this.role){
17459             cfg.role = this.role;
17460         }
17461         
17462         if(this.aria_valuenow){
17463             cfg['aria-valuenow'] = this.aria_valuenow;
17464         }
17465         
17466         if(this.aria_valuemin){
17467             cfg['aria-valuemin'] = this.aria_valuemin;
17468         }
17469         
17470         if(this.aria_valuemax){
17471             cfg['aria-valuemax'] = this.aria_valuemax;
17472         }
17473         
17474         if(this.label && !this.sr_only){
17475             cfg.html = this.label;
17476         }
17477         
17478         if(this.panel){
17479             cfg.cls += ' progress-bar-' + this.panel;
17480         }
17481         
17482         return cfg;
17483     },
17484     
17485     update : function(aria_valuenow)
17486     {
17487         this.aria_valuenow = aria_valuenow;
17488         
17489         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17490     }
17491    
17492 });
17493
17494  
17495
17496  /*
17497  * - LGPL
17498  *
17499  * column
17500  * 
17501  */
17502
17503 /**
17504  * @class Roo.bootstrap.TabGroup
17505  * @extends Roo.bootstrap.Column
17506  * Bootstrap Column class
17507  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17508  * @cfg {Boolean} carousel true to make the group behave like a carousel
17509  * @cfg {Boolean} bullets show bullets for the panels
17510  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17511  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17512  * @cfg {Boolean} showarrow (true|false) show arrow default true
17513  * 
17514  * @constructor
17515  * Create a new TabGroup
17516  * @param {Object} config The config object
17517  */
17518
17519 Roo.bootstrap.TabGroup = function(config){
17520     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17521     if (!this.navId) {
17522         this.navId = Roo.id();
17523     }
17524     this.tabs = [];
17525     Roo.bootstrap.TabGroup.register(this);
17526     
17527 };
17528
17529 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17530     
17531     carousel : false,
17532     transition : false,
17533     bullets : 0,
17534     timer : 0,
17535     autoslide : false,
17536     slideFn : false,
17537     slideOnTouch : false,
17538     showarrow : true,
17539     
17540     getAutoCreate : function()
17541     {
17542         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17543         
17544         cfg.cls += ' tab-content';
17545         
17546         if (this.carousel) {
17547             cfg.cls += ' carousel slide';
17548             
17549             cfg.cn = [{
17550                cls : 'carousel-inner',
17551                cn : []
17552             }];
17553         
17554             if(this.bullets  && !Roo.isTouch){
17555                 
17556                 var bullets = {
17557                     cls : 'carousel-bullets',
17558                     cn : []
17559                 };
17560                
17561                 if(this.bullets_cls){
17562                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17563                 }
17564                 
17565                 bullets.cn.push({
17566                     cls : 'clear'
17567                 });
17568                 
17569                 cfg.cn[0].cn.push(bullets);
17570             }
17571             
17572             if(this.showarrow){
17573                 cfg.cn[0].cn.push({
17574                     tag : 'div',
17575                     class : 'carousel-arrow',
17576                     cn : [
17577                         {
17578                             tag : 'div',
17579                             class : 'carousel-prev',
17580                             cn : [
17581                                 {
17582                                     tag : 'i',
17583                                     class : 'fa fa-chevron-left'
17584                                 }
17585                             ]
17586                         },
17587                         {
17588                             tag : 'div',
17589                             class : 'carousel-next',
17590                             cn : [
17591                                 {
17592                                     tag : 'i',
17593                                     class : 'fa fa-chevron-right'
17594                                 }
17595                             ]
17596                         }
17597                     ]
17598                 });
17599             }
17600             
17601         }
17602         
17603         return cfg;
17604     },
17605     
17606     initEvents:  function()
17607     {
17608 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17609 //            this.el.on("touchstart", this.onTouchStart, this);
17610 //        }
17611         
17612         if(this.autoslide){
17613             var _this = this;
17614             
17615             this.slideFn = window.setInterval(function() {
17616                 _this.showPanelNext();
17617             }, this.timer);
17618         }
17619         
17620         if(this.showarrow){
17621             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17622             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17623         }
17624         
17625         
17626     },
17627     
17628 //    onTouchStart : function(e, el, o)
17629 //    {
17630 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17631 //            return;
17632 //        }
17633 //        
17634 //        this.showPanelNext();
17635 //    },
17636     
17637     
17638     getChildContainer : function()
17639     {
17640         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17641     },
17642     
17643     /**
17644     * register a Navigation item
17645     * @param {Roo.bootstrap.NavItem} the navitem to add
17646     */
17647     register : function(item)
17648     {
17649         this.tabs.push( item);
17650         item.navId = this.navId; // not really needed..
17651         this.addBullet();
17652     
17653     },
17654     
17655     getActivePanel : function()
17656     {
17657         var r = false;
17658         Roo.each(this.tabs, function(t) {
17659             if (t.active) {
17660                 r = t;
17661                 return false;
17662             }
17663             return null;
17664         });
17665         return r;
17666         
17667     },
17668     getPanelByName : function(n)
17669     {
17670         var r = false;
17671         Roo.each(this.tabs, function(t) {
17672             if (t.tabId == n) {
17673                 r = t;
17674                 return false;
17675             }
17676             return null;
17677         });
17678         return r;
17679     },
17680     indexOfPanel : function(p)
17681     {
17682         var r = false;
17683         Roo.each(this.tabs, function(t,i) {
17684             if (t.tabId == p.tabId) {
17685                 r = i;
17686                 return false;
17687             }
17688             return null;
17689         });
17690         return r;
17691     },
17692     /**
17693      * show a specific panel
17694      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17695      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17696      */
17697     showPanel : function (pan)
17698     {
17699         if(this.transition || typeof(pan) == 'undefined'){
17700             Roo.log("waiting for the transitionend");
17701             return;
17702         }
17703         
17704         if (typeof(pan) == 'number') {
17705             pan = this.tabs[pan];
17706         }
17707         
17708         if (typeof(pan) == 'string') {
17709             pan = this.getPanelByName(pan);
17710         }
17711         
17712         var cur = this.getActivePanel();
17713         
17714         if(!pan || !cur){
17715             Roo.log('pan or acitve pan is undefined');
17716             return false;
17717         }
17718         
17719         if (pan.tabId == this.getActivePanel().tabId) {
17720             return true;
17721         }
17722         
17723         if (false === cur.fireEvent('beforedeactivate')) {
17724             return false;
17725         }
17726         
17727         if(this.bullets > 0 && !Roo.isTouch){
17728             this.setActiveBullet(this.indexOfPanel(pan));
17729         }
17730         
17731         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17732             
17733             this.transition = true;
17734             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17735             var lr = dir == 'next' ? 'left' : 'right';
17736             pan.el.addClass(dir); // or prev
17737             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17738             cur.el.addClass(lr); // or right
17739             pan.el.addClass(lr);
17740             
17741             var _this = this;
17742             cur.el.on('transitionend', function() {
17743                 Roo.log("trans end?");
17744                 
17745                 pan.el.removeClass([lr,dir]);
17746                 pan.setActive(true);
17747                 
17748                 cur.el.removeClass([lr]);
17749                 cur.setActive(false);
17750                 
17751                 _this.transition = false;
17752                 
17753             }, this, { single:  true } );
17754             
17755             return true;
17756         }
17757         
17758         cur.setActive(false);
17759         pan.setActive(true);
17760         
17761         return true;
17762         
17763     },
17764     showPanelNext : function()
17765     {
17766         var i = this.indexOfPanel(this.getActivePanel());
17767         
17768         if (i >= this.tabs.length - 1 && !this.autoslide) {
17769             return;
17770         }
17771         
17772         if (i >= this.tabs.length - 1 && this.autoslide) {
17773             i = -1;
17774         }
17775         
17776         this.showPanel(this.tabs[i+1]);
17777     },
17778     
17779     showPanelPrev : function()
17780     {
17781         var i = this.indexOfPanel(this.getActivePanel());
17782         
17783         if (i  < 1 && !this.autoslide) {
17784             return;
17785         }
17786         
17787         if (i < 1 && this.autoslide) {
17788             i = this.tabs.length;
17789         }
17790         
17791         this.showPanel(this.tabs[i-1]);
17792     },
17793     
17794     
17795     addBullet: function()
17796     {
17797         if(!this.bullets || Roo.isTouch){
17798             return;
17799         }
17800         var ctr = this.el.select('.carousel-bullets',true).first();
17801         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17802         var bullet = ctr.createChild({
17803             cls : 'bullet bullet-' + i
17804         },ctr.dom.lastChild);
17805         
17806         
17807         var _this = this;
17808         
17809         bullet.on('click', (function(e, el, o, ii, t){
17810
17811             e.preventDefault();
17812
17813             this.showPanel(ii);
17814
17815             if(this.autoslide && this.slideFn){
17816                 clearInterval(this.slideFn);
17817                 this.slideFn = window.setInterval(function() {
17818                     _this.showPanelNext();
17819                 }, this.timer);
17820             }
17821
17822         }).createDelegate(this, [i, bullet], true));
17823                 
17824         
17825     },
17826      
17827     setActiveBullet : function(i)
17828     {
17829         if(Roo.isTouch){
17830             return;
17831         }
17832         
17833         Roo.each(this.el.select('.bullet', true).elements, function(el){
17834             el.removeClass('selected');
17835         });
17836
17837         var bullet = this.el.select('.bullet-' + i, true).first();
17838         
17839         if(!bullet){
17840             return;
17841         }
17842         
17843         bullet.addClass('selected');
17844     }
17845     
17846     
17847   
17848 });
17849
17850  
17851
17852  
17853  
17854 Roo.apply(Roo.bootstrap.TabGroup, {
17855     
17856     groups: {},
17857      /**
17858     * register a Navigation Group
17859     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17860     */
17861     register : function(navgrp)
17862     {
17863         this.groups[navgrp.navId] = navgrp;
17864         
17865     },
17866     /**
17867     * fetch a Navigation Group based on the navigation ID
17868     * if one does not exist , it will get created.
17869     * @param {string} the navgroup to add
17870     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17871     */
17872     get: function(navId) {
17873         if (typeof(this.groups[navId]) == 'undefined') {
17874             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17875         }
17876         return this.groups[navId] ;
17877     }
17878     
17879     
17880     
17881 });
17882
17883  /*
17884  * - LGPL
17885  *
17886  * TabPanel
17887  * 
17888  */
17889
17890 /**
17891  * @class Roo.bootstrap.TabPanel
17892  * @extends Roo.bootstrap.Component
17893  * Bootstrap TabPanel class
17894  * @cfg {Boolean} active panel active
17895  * @cfg {String} html panel content
17896  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17897  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17898  * @cfg {String} href click to link..
17899  * 
17900  * 
17901  * @constructor
17902  * Create a new TabPanel
17903  * @param {Object} config The config object
17904  */
17905
17906 Roo.bootstrap.TabPanel = function(config){
17907     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17908     this.addEvents({
17909         /**
17910              * @event changed
17911              * Fires when the active status changes
17912              * @param {Roo.bootstrap.TabPanel} this
17913              * @param {Boolean} state the new state
17914             
17915          */
17916         'changed': true,
17917         /**
17918              * @event beforedeactivate
17919              * Fires before a tab is de-activated - can be used to do validation on a form.
17920              * @param {Roo.bootstrap.TabPanel} this
17921              * @return {Boolean} false if there is an error
17922             
17923          */
17924         'beforedeactivate': true
17925      });
17926     
17927     this.tabId = this.tabId || Roo.id();
17928   
17929 };
17930
17931 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17932     
17933     active: false,
17934     html: false,
17935     tabId: false,
17936     navId : false,
17937     href : '',
17938     
17939     getAutoCreate : function(){
17940         var cfg = {
17941             tag: 'div',
17942             // item is needed for carousel - not sure if it has any effect otherwise
17943             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17944             html: this.html || ''
17945         };
17946         
17947         if(this.active){
17948             cfg.cls += ' active';
17949         }
17950         
17951         if(this.tabId){
17952             cfg.tabId = this.tabId;
17953         }
17954         
17955         
17956         return cfg;
17957     },
17958     
17959     initEvents:  function()
17960     {
17961         var p = this.parent();
17962         
17963         this.navId = this.navId || p.navId;
17964         
17965         if (typeof(this.navId) != 'undefined') {
17966             // not really needed.. but just in case.. parent should be a NavGroup.
17967             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17968             
17969             tg.register(this);
17970             
17971             var i = tg.tabs.length - 1;
17972             
17973             if(this.active && tg.bullets > 0 && i < tg.bullets){
17974                 tg.setActiveBullet(i);
17975             }
17976         }
17977         
17978         this.el.on('click', this.onClick, this);
17979         
17980         if(Roo.isTouch){
17981             this.el.on("touchstart", this.onTouchStart, this);
17982             this.el.on("touchmove", this.onTouchMove, this);
17983             this.el.on("touchend", this.onTouchEnd, this);
17984         }
17985         
17986     },
17987     
17988     onRender : function(ct, position)
17989     {
17990         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17991     },
17992     
17993     setActive : function(state)
17994     {
17995         Roo.log("panel - set active " + this.tabId + "=" + state);
17996         
17997         this.active = state;
17998         if (!state) {
17999             this.el.removeClass('active');
18000             
18001         } else  if (!this.el.hasClass('active')) {
18002             this.el.addClass('active');
18003         }
18004         
18005         this.fireEvent('changed', this, state);
18006     },
18007     
18008     onClick : function(e)
18009     {
18010         e.preventDefault();
18011         
18012         if(!this.href.length){
18013             return;
18014         }
18015         
18016         window.location.href = this.href;
18017     },
18018     
18019     startX : 0,
18020     startY : 0,
18021     endX : 0,
18022     endY : 0,
18023     swiping : false,
18024     
18025     onTouchStart : function(e)
18026     {
18027         this.swiping = false;
18028         
18029         this.startX = e.browserEvent.touches[0].clientX;
18030         this.startY = e.browserEvent.touches[0].clientY;
18031     },
18032     
18033     onTouchMove : function(e)
18034     {
18035         this.swiping = true;
18036         
18037         this.endX = e.browserEvent.touches[0].clientX;
18038         this.endY = e.browserEvent.touches[0].clientY;
18039     },
18040     
18041     onTouchEnd : function(e)
18042     {
18043         if(!this.swiping){
18044             this.onClick(e);
18045             return;
18046         }
18047         
18048         var tabGroup = this.parent();
18049         
18050         if(this.endX > this.startX){ // swiping right
18051             tabGroup.showPanelPrev();
18052             return;
18053         }
18054         
18055         if(this.startX > this.endX){ // swiping left
18056             tabGroup.showPanelNext();
18057             return;
18058         }
18059     }
18060     
18061     
18062 });
18063  
18064
18065  
18066
18067  /*
18068  * - LGPL
18069  *
18070  * DateField
18071  * 
18072  */
18073
18074 /**
18075  * @class Roo.bootstrap.DateField
18076  * @extends Roo.bootstrap.Input
18077  * Bootstrap DateField class
18078  * @cfg {Number} weekStart default 0
18079  * @cfg {String} viewMode default empty, (months|years)
18080  * @cfg {String} minViewMode default empty, (months|years)
18081  * @cfg {Number} startDate default -Infinity
18082  * @cfg {Number} endDate default Infinity
18083  * @cfg {Boolean} todayHighlight default false
18084  * @cfg {Boolean} todayBtn default false
18085  * @cfg {Boolean} calendarWeeks default false
18086  * @cfg {Object} daysOfWeekDisabled default empty
18087  * @cfg {Boolean} singleMode default false (true | false)
18088  * 
18089  * @cfg {Boolean} keyboardNavigation default true
18090  * @cfg {String} language default en
18091  * 
18092  * @constructor
18093  * Create a new DateField
18094  * @param {Object} config The config object
18095  */
18096
18097 Roo.bootstrap.DateField = function(config){
18098     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18099      this.addEvents({
18100             /**
18101              * @event show
18102              * Fires when this field show.
18103              * @param {Roo.bootstrap.DateField} this
18104              * @param {Mixed} date The date value
18105              */
18106             show : true,
18107             /**
18108              * @event show
18109              * Fires when this field hide.
18110              * @param {Roo.bootstrap.DateField} this
18111              * @param {Mixed} date The date value
18112              */
18113             hide : true,
18114             /**
18115              * @event select
18116              * Fires when select a date.
18117              * @param {Roo.bootstrap.DateField} this
18118              * @param {Mixed} date The date value
18119              */
18120             select : true,
18121             /**
18122              * @event beforeselect
18123              * Fires when before select a date.
18124              * @param {Roo.bootstrap.DateField} this
18125              * @param {Mixed} date The date value
18126              */
18127             beforeselect : true
18128         });
18129 };
18130
18131 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18132     
18133     /**
18134      * @cfg {String} format
18135      * The default date format string which can be overriden for localization support.  The format must be
18136      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18137      */
18138     format : "m/d/y",
18139     /**
18140      * @cfg {String} altFormats
18141      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18142      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18143      */
18144     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18145     
18146     weekStart : 0,
18147     
18148     viewMode : '',
18149     
18150     minViewMode : '',
18151     
18152     todayHighlight : false,
18153     
18154     todayBtn: false,
18155     
18156     language: 'en',
18157     
18158     keyboardNavigation: true,
18159     
18160     calendarWeeks: false,
18161     
18162     startDate: -Infinity,
18163     
18164     endDate: Infinity,
18165     
18166     daysOfWeekDisabled: [],
18167     
18168     _events: [],
18169     
18170     singleMode : false,
18171     
18172     UTCDate: function()
18173     {
18174         return new Date(Date.UTC.apply(Date, arguments));
18175     },
18176     
18177     UTCToday: function()
18178     {
18179         var today = new Date();
18180         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18181     },
18182     
18183     getDate: function() {
18184             var d = this.getUTCDate();
18185             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18186     },
18187     
18188     getUTCDate: function() {
18189             return this.date;
18190     },
18191     
18192     setDate: function(d) {
18193             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18194     },
18195     
18196     setUTCDate: function(d) {
18197             this.date = d;
18198             this.setValue(this.formatDate(this.date));
18199     },
18200         
18201     onRender: function(ct, position)
18202     {
18203         
18204         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18205         
18206         this.language = this.language || 'en';
18207         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18208         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18209         
18210         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18211         this.format = this.format || 'm/d/y';
18212         this.isInline = false;
18213         this.isInput = true;
18214         this.component = this.el.select('.add-on', true).first() || false;
18215         this.component = (this.component && this.component.length === 0) ? false : this.component;
18216         this.hasInput = this.component && this.inputEl().length;
18217         
18218         if (typeof(this.minViewMode === 'string')) {
18219             switch (this.minViewMode) {
18220                 case 'months':
18221                     this.minViewMode = 1;
18222                     break;
18223                 case 'years':
18224                     this.minViewMode = 2;
18225                     break;
18226                 default:
18227                     this.minViewMode = 0;
18228                     break;
18229             }
18230         }
18231         
18232         if (typeof(this.viewMode === 'string')) {
18233             switch (this.viewMode) {
18234                 case 'months':
18235                     this.viewMode = 1;
18236                     break;
18237                 case 'years':
18238                     this.viewMode = 2;
18239                     break;
18240                 default:
18241                     this.viewMode = 0;
18242                     break;
18243             }
18244         }
18245                 
18246         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18247         
18248 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18249         
18250         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18251         
18252         this.picker().on('mousedown', this.onMousedown, this);
18253         this.picker().on('click', this.onClick, this);
18254         
18255         this.picker().addClass('datepicker-dropdown');
18256         
18257         this.startViewMode = this.viewMode;
18258         
18259         if(this.singleMode){
18260             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18261                 v.setVisibilityMode(Roo.Element.DISPLAY);
18262                 v.hide();
18263             });
18264             
18265             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18266                 v.setStyle('width', '189px');
18267             });
18268         }
18269         
18270         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18271             if(!this.calendarWeeks){
18272                 v.remove();
18273                 return;
18274             }
18275             
18276             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18277             v.attr('colspan', function(i, val){
18278                 return parseInt(val) + 1;
18279             });
18280         });
18281                         
18282         
18283         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18284         
18285         this.setStartDate(this.startDate);
18286         this.setEndDate(this.endDate);
18287         
18288         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18289         
18290         this.fillDow();
18291         this.fillMonths();
18292         this.update();
18293         this.showMode();
18294         
18295         if(this.isInline) {
18296             this.show();
18297         }
18298     },
18299     
18300     picker : function()
18301     {
18302         return this.pickerEl;
18303 //        return this.el.select('.datepicker', true).first();
18304     },
18305     
18306     fillDow: function()
18307     {
18308         var dowCnt = this.weekStart;
18309         
18310         var dow = {
18311             tag: 'tr',
18312             cn: [
18313                 
18314             ]
18315         };
18316         
18317         if(this.calendarWeeks){
18318             dow.cn.push({
18319                 tag: 'th',
18320                 cls: 'cw',
18321                 html: '&nbsp;'
18322             })
18323         }
18324         
18325         while (dowCnt < this.weekStart + 7) {
18326             dow.cn.push({
18327                 tag: 'th',
18328                 cls: 'dow',
18329                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18330             });
18331         }
18332         
18333         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18334     },
18335     
18336     fillMonths: function()
18337     {    
18338         var i = 0;
18339         var months = this.picker().select('>.datepicker-months td', true).first();
18340         
18341         months.dom.innerHTML = '';
18342         
18343         while (i < 12) {
18344             var month = {
18345                 tag: 'span',
18346                 cls: 'month',
18347                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18348             };
18349             
18350             months.createChild(month);
18351         }
18352         
18353     },
18354     
18355     update: function()
18356     {
18357         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;
18358         
18359         if (this.date < this.startDate) {
18360             this.viewDate = new Date(this.startDate);
18361         } else if (this.date > this.endDate) {
18362             this.viewDate = new Date(this.endDate);
18363         } else {
18364             this.viewDate = new Date(this.date);
18365         }
18366         
18367         this.fill();
18368     },
18369     
18370     fill: function() 
18371     {
18372         var d = new Date(this.viewDate),
18373                 year = d.getUTCFullYear(),
18374                 month = d.getUTCMonth(),
18375                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18376                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18377                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18378                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18379                 currentDate = this.date && this.date.valueOf(),
18380                 today = this.UTCToday();
18381         
18382         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18383         
18384 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18385         
18386 //        this.picker.select('>tfoot th.today').
18387 //                                              .text(dates[this.language].today)
18388 //                                              .toggle(this.todayBtn !== false);
18389     
18390         this.updateNavArrows();
18391         this.fillMonths();
18392                                                 
18393         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18394         
18395         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18396          
18397         prevMonth.setUTCDate(day);
18398         
18399         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18400         
18401         var nextMonth = new Date(prevMonth);
18402         
18403         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18404         
18405         nextMonth = nextMonth.valueOf();
18406         
18407         var fillMonths = false;
18408         
18409         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18410         
18411         while(prevMonth.valueOf() < nextMonth) {
18412             var clsName = '';
18413             
18414             if (prevMonth.getUTCDay() === this.weekStart) {
18415                 if(fillMonths){
18416                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18417                 }
18418                     
18419                 fillMonths = {
18420                     tag: 'tr',
18421                     cn: []
18422                 };
18423                 
18424                 if(this.calendarWeeks){
18425                     // ISO 8601: First week contains first thursday.
18426                     // ISO also states week starts on Monday, but we can be more abstract here.
18427                     var
18428                     // Start of current week: based on weekstart/current date
18429                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18430                     // Thursday of this week
18431                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18432                     // First Thursday of year, year from thursday
18433                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18434                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18435                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18436                     
18437                     fillMonths.cn.push({
18438                         tag: 'td',
18439                         cls: 'cw',
18440                         html: calWeek
18441                     });
18442                 }
18443             }
18444             
18445             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18446                 clsName += ' old';
18447             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18448                 clsName += ' new';
18449             }
18450             if (this.todayHighlight &&
18451                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18452                 prevMonth.getUTCMonth() == today.getMonth() &&
18453                 prevMonth.getUTCDate() == today.getDate()) {
18454                 clsName += ' today';
18455             }
18456             
18457             if (currentDate && prevMonth.valueOf() === currentDate) {
18458                 clsName += ' active';
18459             }
18460             
18461             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18462                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18463                     clsName += ' disabled';
18464             }
18465             
18466             fillMonths.cn.push({
18467                 tag: 'td',
18468                 cls: 'day ' + clsName,
18469                 html: prevMonth.getDate()
18470             });
18471             
18472             prevMonth.setDate(prevMonth.getDate()+1);
18473         }
18474           
18475         var currentYear = this.date && this.date.getUTCFullYear();
18476         var currentMonth = this.date && this.date.getUTCMonth();
18477         
18478         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18479         
18480         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18481             v.removeClass('active');
18482             
18483             if(currentYear === year && k === currentMonth){
18484                 v.addClass('active');
18485             }
18486             
18487             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18488                 v.addClass('disabled');
18489             }
18490             
18491         });
18492         
18493         
18494         year = parseInt(year/10, 10) * 10;
18495         
18496         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18497         
18498         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18499         
18500         year -= 1;
18501         for (var i = -1; i < 11; i++) {
18502             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18503                 tag: 'span',
18504                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18505                 html: year
18506             });
18507             
18508             year += 1;
18509         }
18510     },
18511     
18512     showMode: function(dir) 
18513     {
18514         if (dir) {
18515             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18516         }
18517         
18518         Roo.each(this.picker().select('>div',true).elements, function(v){
18519             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18520             v.hide();
18521         });
18522         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18523     },
18524     
18525     place: function()
18526     {
18527         if(this.isInline) {
18528             return;
18529         }
18530         
18531         this.picker().removeClass(['bottom', 'top']);
18532         
18533         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18534             /*
18535              * place to the top of element!
18536              *
18537              */
18538             
18539             this.picker().addClass('top');
18540             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18541             
18542             return;
18543         }
18544         
18545         this.picker().addClass('bottom');
18546         
18547         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18548     },
18549     
18550     parseDate : function(value)
18551     {
18552         if(!value || value instanceof Date){
18553             return value;
18554         }
18555         var v = Date.parseDate(value, this.format);
18556         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18557             v = Date.parseDate(value, 'Y-m-d');
18558         }
18559         if(!v && this.altFormats){
18560             if(!this.altFormatsArray){
18561                 this.altFormatsArray = this.altFormats.split("|");
18562             }
18563             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18564                 v = Date.parseDate(value, this.altFormatsArray[i]);
18565             }
18566         }
18567         return v;
18568     },
18569     
18570     formatDate : function(date, fmt)
18571     {   
18572         return (!date || !(date instanceof Date)) ?
18573         date : date.dateFormat(fmt || this.format);
18574     },
18575     
18576     onFocus : function()
18577     {
18578         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18579         this.show();
18580     },
18581     
18582     onBlur : function()
18583     {
18584         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18585         
18586         var d = this.inputEl().getValue();
18587         
18588         this.setValue(d);
18589                 
18590         this.hide();
18591     },
18592     
18593     show : function()
18594     {
18595         this.picker().show();
18596         this.update();
18597         this.place();
18598         
18599         this.fireEvent('show', this, this.date);
18600     },
18601     
18602     hide : function()
18603     {
18604         if(this.isInline) {
18605             return;
18606         }
18607         this.picker().hide();
18608         this.viewMode = this.startViewMode;
18609         this.showMode();
18610         
18611         this.fireEvent('hide', this, this.date);
18612         
18613     },
18614     
18615     onMousedown: function(e)
18616     {
18617         e.stopPropagation();
18618         e.preventDefault();
18619     },
18620     
18621     keyup: function(e)
18622     {
18623         Roo.bootstrap.DateField.superclass.keyup.call(this);
18624         this.update();
18625     },
18626
18627     setValue: function(v)
18628     {
18629         if(this.fireEvent('beforeselect', this, v) !== false){
18630             var d = new Date(this.parseDate(v) ).clearTime();
18631         
18632             if(isNaN(d.getTime())){
18633                 this.date = this.viewDate = '';
18634                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18635                 return;
18636             }
18637
18638             v = this.formatDate(d);
18639
18640             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18641
18642             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18643
18644             this.update();
18645
18646             this.fireEvent('select', this, this.date);
18647         }
18648     },
18649     
18650     getValue: function()
18651     {
18652         return this.formatDate(this.date);
18653     },
18654     
18655     fireKey: function(e)
18656     {
18657         if (!this.picker().isVisible()){
18658             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18659                 this.show();
18660             }
18661             return;
18662         }
18663         
18664         var dateChanged = false,
18665         dir, day, month,
18666         newDate, newViewDate;
18667         
18668         switch(e.keyCode){
18669             case 27: // escape
18670                 this.hide();
18671                 e.preventDefault();
18672                 break;
18673             case 37: // left
18674             case 39: // right
18675                 if (!this.keyboardNavigation) {
18676                     break;
18677                 }
18678                 dir = e.keyCode == 37 ? -1 : 1;
18679                 
18680                 if (e.ctrlKey){
18681                     newDate = this.moveYear(this.date, dir);
18682                     newViewDate = this.moveYear(this.viewDate, dir);
18683                 } else if (e.shiftKey){
18684                     newDate = this.moveMonth(this.date, dir);
18685                     newViewDate = this.moveMonth(this.viewDate, dir);
18686                 } else {
18687                     newDate = new Date(this.date);
18688                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18689                     newViewDate = new Date(this.viewDate);
18690                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18691                 }
18692                 if (this.dateWithinRange(newDate)){
18693                     this.date = newDate;
18694                     this.viewDate = newViewDate;
18695                     this.setValue(this.formatDate(this.date));
18696 //                    this.update();
18697                     e.preventDefault();
18698                     dateChanged = true;
18699                 }
18700                 break;
18701             case 38: // up
18702             case 40: // down
18703                 if (!this.keyboardNavigation) {
18704                     break;
18705                 }
18706                 dir = e.keyCode == 38 ? -1 : 1;
18707                 if (e.ctrlKey){
18708                     newDate = this.moveYear(this.date, dir);
18709                     newViewDate = this.moveYear(this.viewDate, dir);
18710                 } else if (e.shiftKey){
18711                     newDate = this.moveMonth(this.date, dir);
18712                     newViewDate = this.moveMonth(this.viewDate, dir);
18713                 } else {
18714                     newDate = new Date(this.date);
18715                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18716                     newViewDate = new Date(this.viewDate);
18717                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18718                 }
18719                 if (this.dateWithinRange(newDate)){
18720                     this.date = newDate;
18721                     this.viewDate = newViewDate;
18722                     this.setValue(this.formatDate(this.date));
18723 //                    this.update();
18724                     e.preventDefault();
18725                     dateChanged = true;
18726                 }
18727                 break;
18728             case 13: // enter
18729                 this.setValue(this.formatDate(this.date));
18730                 this.hide();
18731                 e.preventDefault();
18732                 break;
18733             case 9: // tab
18734                 this.setValue(this.formatDate(this.date));
18735                 this.hide();
18736                 break;
18737             case 16: // shift
18738             case 17: // ctrl
18739             case 18: // alt
18740                 break;
18741             default :
18742                 this.hide();
18743                 
18744         }
18745     },
18746     
18747     
18748     onClick: function(e) 
18749     {
18750         e.stopPropagation();
18751         e.preventDefault();
18752         
18753         var target = e.getTarget();
18754         
18755         if(target.nodeName.toLowerCase() === 'i'){
18756             target = Roo.get(target).dom.parentNode;
18757         }
18758         
18759         var nodeName = target.nodeName;
18760         var className = target.className;
18761         var html = target.innerHTML;
18762         //Roo.log(nodeName);
18763         
18764         switch(nodeName.toLowerCase()) {
18765             case 'th':
18766                 switch(className) {
18767                     case 'switch':
18768                         this.showMode(1);
18769                         break;
18770                     case 'prev':
18771                     case 'next':
18772                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18773                         switch(this.viewMode){
18774                                 case 0:
18775                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18776                                         break;
18777                                 case 1:
18778                                 case 2:
18779                                         this.viewDate = this.moveYear(this.viewDate, dir);
18780                                         break;
18781                         }
18782                         this.fill();
18783                         break;
18784                     case 'today':
18785                         var date = new Date();
18786                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18787 //                        this.fill()
18788                         this.setValue(this.formatDate(this.date));
18789                         
18790                         this.hide();
18791                         break;
18792                 }
18793                 break;
18794             case 'span':
18795                 if (className.indexOf('disabled') < 0) {
18796                     this.viewDate.setUTCDate(1);
18797                     if (className.indexOf('month') > -1) {
18798                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18799                     } else {
18800                         var year = parseInt(html, 10) || 0;
18801                         this.viewDate.setUTCFullYear(year);
18802                         
18803                     }
18804                     
18805                     if(this.singleMode){
18806                         this.setValue(this.formatDate(this.viewDate));
18807                         this.hide();
18808                         return;
18809                     }
18810                     
18811                     this.showMode(-1);
18812                     this.fill();
18813                 }
18814                 break;
18815                 
18816             case 'td':
18817                 //Roo.log(className);
18818                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18819                     var day = parseInt(html, 10) || 1;
18820                     var year = this.viewDate.getUTCFullYear(),
18821                         month = this.viewDate.getUTCMonth();
18822
18823                     if (className.indexOf('old') > -1) {
18824                         if(month === 0 ){
18825                             month = 11;
18826                             year -= 1;
18827                         }else{
18828                             month -= 1;
18829                         }
18830                     } else if (className.indexOf('new') > -1) {
18831                         if (month == 11) {
18832                             month = 0;
18833                             year += 1;
18834                         } else {
18835                             month += 1;
18836                         }
18837                     }
18838                     //Roo.log([year,month,day]);
18839                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18840                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18841 //                    this.fill();
18842                     //Roo.log(this.formatDate(this.date));
18843                     this.setValue(this.formatDate(this.date));
18844                     this.hide();
18845                 }
18846                 break;
18847         }
18848     },
18849     
18850     setStartDate: function(startDate)
18851     {
18852         this.startDate = startDate || -Infinity;
18853         if (this.startDate !== -Infinity) {
18854             this.startDate = this.parseDate(this.startDate);
18855         }
18856         this.update();
18857         this.updateNavArrows();
18858     },
18859
18860     setEndDate: function(endDate)
18861     {
18862         this.endDate = endDate || Infinity;
18863         if (this.endDate !== Infinity) {
18864             this.endDate = this.parseDate(this.endDate);
18865         }
18866         this.update();
18867         this.updateNavArrows();
18868     },
18869     
18870     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18871     {
18872         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18873         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18874             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18875         }
18876         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18877             return parseInt(d, 10);
18878         });
18879         this.update();
18880         this.updateNavArrows();
18881     },
18882     
18883     updateNavArrows: function() 
18884     {
18885         if(this.singleMode){
18886             return;
18887         }
18888         
18889         var d = new Date(this.viewDate),
18890         year = d.getUTCFullYear(),
18891         month = d.getUTCMonth();
18892         
18893         Roo.each(this.picker().select('.prev', true).elements, function(v){
18894             v.show();
18895             switch (this.viewMode) {
18896                 case 0:
18897
18898                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18899                         v.hide();
18900                     }
18901                     break;
18902                 case 1:
18903                 case 2:
18904                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18905                         v.hide();
18906                     }
18907                     break;
18908             }
18909         });
18910         
18911         Roo.each(this.picker().select('.next', true).elements, function(v){
18912             v.show();
18913             switch (this.viewMode) {
18914                 case 0:
18915
18916                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18917                         v.hide();
18918                     }
18919                     break;
18920                 case 1:
18921                 case 2:
18922                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18923                         v.hide();
18924                     }
18925                     break;
18926             }
18927         })
18928     },
18929     
18930     moveMonth: function(date, dir)
18931     {
18932         if (!dir) {
18933             return date;
18934         }
18935         var new_date = new Date(date.valueOf()),
18936         day = new_date.getUTCDate(),
18937         month = new_date.getUTCMonth(),
18938         mag = Math.abs(dir),
18939         new_month, test;
18940         dir = dir > 0 ? 1 : -1;
18941         if (mag == 1){
18942             test = dir == -1
18943             // If going back one month, make sure month is not current month
18944             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18945             ? function(){
18946                 return new_date.getUTCMonth() == month;
18947             }
18948             // If going forward one month, make sure month is as expected
18949             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18950             : function(){
18951                 return new_date.getUTCMonth() != new_month;
18952             };
18953             new_month = month + dir;
18954             new_date.setUTCMonth(new_month);
18955             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18956             if (new_month < 0 || new_month > 11) {
18957                 new_month = (new_month + 12) % 12;
18958             }
18959         } else {
18960             // For magnitudes >1, move one month at a time...
18961             for (var i=0; i<mag; i++) {
18962                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18963                 new_date = this.moveMonth(new_date, dir);
18964             }
18965             // ...then reset the day, keeping it in the new month
18966             new_month = new_date.getUTCMonth();
18967             new_date.setUTCDate(day);
18968             test = function(){
18969                 return new_month != new_date.getUTCMonth();
18970             };
18971         }
18972         // Common date-resetting loop -- if date is beyond end of month, make it
18973         // end of month
18974         while (test()){
18975             new_date.setUTCDate(--day);
18976             new_date.setUTCMonth(new_month);
18977         }
18978         return new_date;
18979     },
18980
18981     moveYear: function(date, dir)
18982     {
18983         return this.moveMonth(date, dir*12);
18984     },
18985
18986     dateWithinRange: function(date)
18987     {
18988         return date >= this.startDate && date <= this.endDate;
18989     },
18990
18991     
18992     remove: function() 
18993     {
18994         this.picker().remove();
18995     },
18996     
18997     validateValue : function(value)
18998     {
18999         if(value.length < 1)  {
19000             if(this.allowBlank){
19001                 return true;
19002             }
19003             return false;
19004         }
19005         
19006         if(value.length < this.minLength){
19007             return false;
19008         }
19009         if(value.length > this.maxLength){
19010             return false;
19011         }
19012         if(this.vtype){
19013             var vt = Roo.form.VTypes;
19014             if(!vt[this.vtype](value, this)){
19015                 return false;
19016             }
19017         }
19018         if(typeof this.validator == "function"){
19019             var msg = this.validator(value);
19020             if(msg !== true){
19021                 return false;
19022             }
19023         }
19024         
19025         if(this.regex && !this.regex.test(value)){
19026             return false;
19027         }
19028         
19029         if(typeof(this.parseDate(value)) == 'undefined'){
19030             return false;
19031         }
19032         
19033         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19034             return false;
19035         }      
19036         
19037         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19038             return false;
19039         } 
19040         
19041         
19042         return true;
19043     }
19044    
19045 });
19046
19047 Roo.apply(Roo.bootstrap.DateField,  {
19048     
19049     head : {
19050         tag: 'thead',
19051         cn: [
19052         {
19053             tag: 'tr',
19054             cn: [
19055             {
19056                 tag: 'th',
19057                 cls: 'prev',
19058                 html: '<i class="fa fa-arrow-left"/>'
19059             },
19060             {
19061                 tag: 'th',
19062                 cls: 'switch',
19063                 colspan: '5'
19064             },
19065             {
19066                 tag: 'th',
19067                 cls: 'next',
19068                 html: '<i class="fa fa-arrow-right"/>'
19069             }
19070
19071             ]
19072         }
19073         ]
19074     },
19075     
19076     content : {
19077         tag: 'tbody',
19078         cn: [
19079         {
19080             tag: 'tr',
19081             cn: [
19082             {
19083                 tag: 'td',
19084                 colspan: '7'
19085             }
19086             ]
19087         }
19088         ]
19089     },
19090     
19091     footer : {
19092         tag: 'tfoot',
19093         cn: [
19094         {
19095             tag: 'tr',
19096             cn: [
19097             {
19098                 tag: 'th',
19099                 colspan: '7',
19100                 cls: 'today'
19101             }
19102                     
19103             ]
19104         }
19105         ]
19106     },
19107     
19108     dates:{
19109         en: {
19110             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19111             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19112             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19113             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19114             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19115             today: "Today"
19116         }
19117     },
19118     
19119     modes: [
19120     {
19121         clsName: 'days',
19122         navFnc: 'Month',
19123         navStep: 1
19124     },
19125     {
19126         clsName: 'months',
19127         navFnc: 'FullYear',
19128         navStep: 1
19129     },
19130     {
19131         clsName: 'years',
19132         navFnc: 'FullYear',
19133         navStep: 10
19134     }]
19135 });
19136
19137 Roo.apply(Roo.bootstrap.DateField,  {
19138   
19139     template : {
19140         tag: 'div',
19141         cls: 'datepicker dropdown-menu roo-dynamic',
19142         cn: [
19143         {
19144             tag: 'div',
19145             cls: 'datepicker-days',
19146             cn: [
19147             {
19148                 tag: 'table',
19149                 cls: 'table-condensed',
19150                 cn:[
19151                 Roo.bootstrap.DateField.head,
19152                 {
19153                     tag: 'tbody'
19154                 },
19155                 Roo.bootstrap.DateField.footer
19156                 ]
19157             }
19158             ]
19159         },
19160         {
19161             tag: 'div',
19162             cls: 'datepicker-months',
19163             cn: [
19164             {
19165                 tag: 'table',
19166                 cls: 'table-condensed',
19167                 cn:[
19168                 Roo.bootstrap.DateField.head,
19169                 Roo.bootstrap.DateField.content,
19170                 Roo.bootstrap.DateField.footer
19171                 ]
19172             }
19173             ]
19174         },
19175         {
19176             tag: 'div',
19177             cls: 'datepicker-years',
19178             cn: [
19179             {
19180                 tag: 'table',
19181                 cls: 'table-condensed',
19182                 cn:[
19183                 Roo.bootstrap.DateField.head,
19184                 Roo.bootstrap.DateField.content,
19185                 Roo.bootstrap.DateField.footer
19186                 ]
19187             }
19188             ]
19189         }
19190         ]
19191     }
19192 });
19193
19194  
19195
19196  /*
19197  * - LGPL
19198  *
19199  * TimeField
19200  * 
19201  */
19202
19203 /**
19204  * @class Roo.bootstrap.TimeField
19205  * @extends Roo.bootstrap.Input
19206  * Bootstrap DateField class
19207  * 
19208  * 
19209  * @constructor
19210  * Create a new TimeField
19211  * @param {Object} config The config object
19212  */
19213
19214 Roo.bootstrap.TimeField = function(config){
19215     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19216     this.addEvents({
19217             /**
19218              * @event show
19219              * Fires when this field show.
19220              * @param {Roo.bootstrap.DateField} thisthis
19221              * @param {Mixed} date The date value
19222              */
19223             show : true,
19224             /**
19225              * @event show
19226              * Fires when this field hide.
19227              * @param {Roo.bootstrap.DateField} this
19228              * @param {Mixed} date The date value
19229              */
19230             hide : true,
19231             /**
19232              * @event select
19233              * Fires when select a date.
19234              * @param {Roo.bootstrap.DateField} this
19235              * @param {Mixed} date The date value
19236              */
19237             select : true
19238         });
19239 };
19240
19241 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19242     
19243     /**
19244      * @cfg {String} format
19245      * The default time format string which can be overriden for localization support.  The format must be
19246      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19247      */
19248     format : "H:i",
19249        
19250     onRender: function(ct, position)
19251     {
19252         
19253         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19254                 
19255         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19256         
19257         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19258         
19259         this.pop = this.picker().select('>.datepicker-time',true).first();
19260         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19261         
19262         this.picker().on('mousedown', this.onMousedown, this);
19263         this.picker().on('click', this.onClick, this);
19264         
19265         this.picker().addClass('datepicker-dropdown');
19266     
19267         this.fillTime();
19268         this.update();
19269             
19270         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19271         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19272         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19273         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19274         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19275         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19276
19277     },
19278     
19279     fireKey: function(e){
19280         if (!this.picker().isVisible()){
19281             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19282                 this.show();
19283             }
19284             return;
19285         }
19286
19287         e.preventDefault();
19288         
19289         switch(e.keyCode){
19290             case 27: // escape
19291                 this.hide();
19292                 break;
19293             case 37: // left
19294             case 39: // right
19295                 this.onTogglePeriod();
19296                 break;
19297             case 38: // up
19298                 this.onIncrementMinutes();
19299                 break;
19300             case 40: // down
19301                 this.onDecrementMinutes();
19302                 break;
19303             case 13: // enter
19304             case 9: // tab
19305                 this.setTime();
19306                 break;
19307         }
19308     },
19309     
19310     onClick: function(e) {
19311         e.stopPropagation();
19312         e.preventDefault();
19313     },
19314     
19315     picker : function()
19316     {
19317         return this.el.select('.datepicker', true).first();
19318     },
19319     
19320     fillTime: function()
19321     {    
19322         var time = this.pop.select('tbody', true).first();
19323         
19324         time.dom.innerHTML = '';
19325         
19326         time.createChild({
19327             tag: 'tr',
19328             cn: [
19329                 {
19330                     tag: 'td',
19331                     cn: [
19332                         {
19333                             tag: 'a',
19334                             href: '#',
19335                             cls: 'btn',
19336                             cn: [
19337                                 {
19338                                     tag: 'span',
19339                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19340                                 }
19341                             ]
19342                         } 
19343                     ]
19344                 },
19345                 {
19346                     tag: 'td',
19347                     cls: 'separator'
19348                 },
19349                 {
19350                     tag: 'td',
19351                     cn: [
19352                         {
19353                             tag: 'a',
19354                             href: '#',
19355                             cls: 'btn',
19356                             cn: [
19357                                 {
19358                                     tag: 'span',
19359                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19360                                 }
19361                             ]
19362                         }
19363                     ]
19364                 },
19365                 {
19366                     tag: 'td',
19367                     cls: 'separator'
19368                 }
19369             ]
19370         });
19371         
19372         time.createChild({
19373             tag: 'tr',
19374             cn: [
19375                 {
19376                     tag: 'td',
19377                     cn: [
19378                         {
19379                             tag: 'span',
19380                             cls: 'timepicker-hour',
19381                             html: '00'
19382                         }  
19383                     ]
19384                 },
19385                 {
19386                     tag: 'td',
19387                     cls: 'separator',
19388                     html: ':'
19389                 },
19390                 {
19391                     tag: 'td',
19392                     cn: [
19393                         {
19394                             tag: 'span',
19395                             cls: 'timepicker-minute',
19396                             html: '00'
19397                         }  
19398                     ]
19399                 },
19400                 {
19401                     tag: 'td',
19402                     cls: 'separator'
19403                 },
19404                 {
19405                     tag: 'td',
19406                     cn: [
19407                         {
19408                             tag: 'button',
19409                             type: 'button',
19410                             cls: 'btn btn-primary period',
19411                             html: 'AM'
19412                             
19413                         }
19414                     ]
19415                 }
19416             ]
19417         });
19418         
19419         time.createChild({
19420             tag: 'tr',
19421             cn: [
19422                 {
19423                     tag: 'td',
19424                     cn: [
19425                         {
19426                             tag: 'a',
19427                             href: '#',
19428                             cls: 'btn',
19429                             cn: [
19430                                 {
19431                                     tag: 'span',
19432                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19433                                 }
19434                             ]
19435                         }
19436                     ]
19437                 },
19438                 {
19439                     tag: 'td',
19440                     cls: 'separator'
19441                 },
19442                 {
19443                     tag: 'td',
19444                     cn: [
19445                         {
19446                             tag: 'a',
19447                             href: '#',
19448                             cls: 'btn',
19449                             cn: [
19450                                 {
19451                                     tag: 'span',
19452                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19453                                 }
19454                             ]
19455                         }
19456                     ]
19457                 },
19458                 {
19459                     tag: 'td',
19460                     cls: 'separator'
19461                 }
19462             ]
19463         });
19464         
19465     },
19466     
19467     update: function()
19468     {
19469         
19470         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19471         
19472         this.fill();
19473     },
19474     
19475     fill: function() 
19476     {
19477         var hours = this.time.getHours();
19478         var minutes = this.time.getMinutes();
19479         var period = 'AM';
19480         
19481         if(hours > 11){
19482             period = 'PM';
19483         }
19484         
19485         if(hours == 0){
19486             hours = 12;
19487         }
19488         
19489         
19490         if(hours > 12){
19491             hours = hours - 12;
19492         }
19493         
19494         if(hours < 10){
19495             hours = '0' + hours;
19496         }
19497         
19498         if(minutes < 10){
19499             minutes = '0' + minutes;
19500         }
19501         
19502         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19503         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19504         this.pop.select('button', true).first().dom.innerHTML = period;
19505         
19506     },
19507     
19508     place: function()
19509     {   
19510         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19511         
19512         var cls = ['bottom'];
19513         
19514         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19515             cls.pop();
19516             cls.push('top');
19517         }
19518         
19519         cls.push('right');
19520         
19521         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19522             cls.pop();
19523             cls.push('left');
19524         }
19525         
19526         this.picker().addClass(cls.join('-'));
19527         
19528         var _this = this;
19529         
19530         Roo.each(cls, function(c){
19531             if(c == 'bottom'){
19532                 _this.picker().setTop(_this.inputEl().getHeight());
19533                 return;
19534             }
19535             if(c == 'top'){
19536                 _this.picker().setTop(0 - _this.picker().getHeight());
19537                 return;
19538             }
19539             
19540             if(c == 'left'){
19541                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19542                 return;
19543             }
19544             if(c == 'right'){
19545                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19546                 return;
19547             }
19548         });
19549         
19550     },
19551   
19552     onFocus : function()
19553     {
19554         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19555         this.show();
19556     },
19557     
19558     onBlur : function()
19559     {
19560         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19561         this.hide();
19562     },
19563     
19564     show : function()
19565     {
19566         this.picker().show();
19567         this.pop.show();
19568         this.update();
19569         this.place();
19570         
19571         this.fireEvent('show', this, this.date);
19572     },
19573     
19574     hide : function()
19575     {
19576         this.picker().hide();
19577         this.pop.hide();
19578         
19579         this.fireEvent('hide', this, this.date);
19580     },
19581     
19582     setTime : function()
19583     {
19584         this.hide();
19585         this.setValue(this.time.format(this.format));
19586         
19587         this.fireEvent('select', this, this.date);
19588         
19589         
19590     },
19591     
19592     onMousedown: function(e){
19593         e.stopPropagation();
19594         e.preventDefault();
19595     },
19596     
19597     onIncrementHours: function()
19598     {
19599         Roo.log('onIncrementHours');
19600         this.time = this.time.add(Date.HOUR, 1);
19601         this.update();
19602         
19603     },
19604     
19605     onDecrementHours: function()
19606     {
19607         Roo.log('onDecrementHours');
19608         this.time = this.time.add(Date.HOUR, -1);
19609         this.update();
19610     },
19611     
19612     onIncrementMinutes: function()
19613     {
19614         Roo.log('onIncrementMinutes');
19615         this.time = this.time.add(Date.MINUTE, 1);
19616         this.update();
19617     },
19618     
19619     onDecrementMinutes: function()
19620     {
19621         Roo.log('onDecrementMinutes');
19622         this.time = this.time.add(Date.MINUTE, -1);
19623         this.update();
19624     },
19625     
19626     onTogglePeriod: function()
19627     {
19628         Roo.log('onTogglePeriod');
19629         this.time = this.time.add(Date.HOUR, 12);
19630         this.update();
19631     }
19632     
19633    
19634 });
19635
19636 Roo.apply(Roo.bootstrap.TimeField,  {
19637     
19638     content : {
19639         tag: 'tbody',
19640         cn: [
19641             {
19642                 tag: 'tr',
19643                 cn: [
19644                 {
19645                     tag: 'td',
19646                     colspan: '7'
19647                 }
19648                 ]
19649             }
19650         ]
19651     },
19652     
19653     footer : {
19654         tag: 'tfoot',
19655         cn: [
19656             {
19657                 tag: 'tr',
19658                 cn: [
19659                 {
19660                     tag: 'th',
19661                     colspan: '7',
19662                     cls: '',
19663                     cn: [
19664                         {
19665                             tag: 'button',
19666                             cls: 'btn btn-info ok',
19667                             html: 'OK'
19668                         }
19669                     ]
19670                 }
19671
19672                 ]
19673             }
19674         ]
19675     }
19676 });
19677
19678 Roo.apply(Roo.bootstrap.TimeField,  {
19679   
19680     template : {
19681         tag: 'div',
19682         cls: 'datepicker dropdown-menu',
19683         cn: [
19684             {
19685                 tag: 'div',
19686                 cls: 'datepicker-time',
19687                 cn: [
19688                 {
19689                     tag: 'table',
19690                     cls: 'table-condensed',
19691                     cn:[
19692                     Roo.bootstrap.TimeField.content,
19693                     Roo.bootstrap.TimeField.footer
19694                     ]
19695                 }
19696                 ]
19697             }
19698         ]
19699     }
19700 });
19701
19702  
19703
19704  /*
19705  * - LGPL
19706  *
19707  * MonthField
19708  * 
19709  */
19710
19711 /**
19712  * @class Roo.bootstrap.MonthField
19713  * @extends Roo.bootstrap.Input
19714  * Bootstrap MonthField class
19715  * 
19716  * @cfg {String} language default en
19717  * 
19718  * @constructor
19719  * Create a new MonthField
19720  * @param {Object} config The config object
19721  */
19722
19723 Roo.bootstrap.MonthField = function(config){
19724     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19725     
19726     this.addEvents({
19727         /**
19728          * @event show
19729          * Fires when this field show.
19730          * @param {Roo.bootstrap.MonthField} this
19731          * @param {Mixed} date The date value
19732          */
19733         show : true,
19734         /**
19735          * @event show
19736          * Fires when this field hide.
19737          * @param {Roo.bootstrap.MonthField} this
19738          * @param {Mixed} date The date value
19739          */
19740         hide : true,
19741         /**
19742          * @event select
19743          * Fires when select a date.
19744          * @param {Roo.bootstrap.MonthField} this
19745          * @param {String} oldvalue The old value
19746          * @param {String} newvalue The new value
19747          */
19748         select : true
19749     });
19750 };
19751
19752 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19753     
19754     onRender: function(ct, position)
19755     {
19756         
19757         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19758         
19759         this.language = this.language || 'en';
19760         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19761         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19762         
19763         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19764         this.isInline = false;
19765         this.isInput = true;
19766         this.component = this.el.select('.add-on', true).first() || false;
19767         this.component = (this.component && this.component.length === 0) ? false : this.component;
19768         this.hasInput = this.component && this.inputEL().length;
19769         
19770         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19771         
19772         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19773         
19774         this.picker().on('mousedown', this.onMousedown, this);
19775         this.picker().on('click', this.onClick, this);
19776         
19777         this.picker().addClass('datepicker-dropdown');
19778         
19779         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19780             v.setStyle('width', '189px');
19781         });
19782         
19783         this.fillMonths();
19784         
19785         this.update();
19786         
19787         if(this.isInline) {
19788             this.show();
19789         }
19790         
19791     },
19792     
19793     setValue: function(v, suppressEvent)
19794     {   
19795         var o = this.getValue();
19796         
19797         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19798         
19799         this.update();
19800
19801         if(suppressEvent !== true){
19802             this.fireEvent('select', this, o, v);
19803         }
19804         
19805     },
19806     
19807     getValue: function()
19808     {
19809         return this.value;
19810     },
19811     
19812     onClick: function(e) 
19813     {
19814         e.stopPropagation();
19815         e.preventDefault();
19816         
19817         var target = e.getTarget();
19818         
19819         if(target.nodeName.toLowerCase() === 'i'){
19820             target = Roo.get(target).dom.parentNode;
19821         }
19822         
19823         var nodeName = target.nodeName;
19824         var className = target.className;
19825         var html = target.innerHTML;
19826         
19827         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19828             return;
19829         }
19830         
19831         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19832         
19833         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19834         
19835         this.hide();
19836                         
19837     },
19838     
19839     picker : function()
19840     {
19841         return this.pickerEl;
19842     },
19843     
19844     fillMonths: function()
19845     {    
19846         var i = 0;
19847         var months = this.picker().select('>.datepicker-months td', true).first();
19848         
19849         months.dom.innerHTML = '';
19850         
19851         while (i < 12) {
19852             var month = {
19853                 tag: 'span',
19854                 cls: 'month',
19855                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19856             };
19857             
19858             months.createChild(month);
19859         }
19860         
19861     },
19862     
19863     update: function()
19864     {
19865         var _this = this;
19866         
19867         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19868             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19869         }
19870         
19871         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19872             e.removeClass('active');
19873             
19874             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19875                 e.addClass('active');
19876             }
19877         })
19878     },
19879     
19880     place: function()
19881     {
19882         if(this.isInline) {
19883             return;
19884         }
19885         
19886         this.picker().removeClass(['bottom', 'top']);
19887         
19888         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19889             /*
19890              * place to the top of element!
19891              *
19892              */
19893             
19894             this.picker().addClass('top');
19895             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19896             
19897             return;
19898         }
19899         
19900         this.picker().addClass('bottom');
19901         
19902         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19903     },
19904     
19905     onFocus : function()
19906     {
19907         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19908         this.show();
19909     },
19910     
19911     onBlur : function()
19912     {
19913         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19914         
19915         var d = this.inputEl().getValue();
19916         
19917         this.setValue(d);
19918                 
19919         this.hide();
19920     },
19921     
19922     show : function()
19923     {
19924         this.picker().show();
19925         this.picker().select('>.datepicker-months', true).first().show();
19926         this.update();
19927         this.place();
19928         
19929         this.fireEvent('show', this, this.date);
19930     },
19931     
19932     hide : function()
19933     {
19934         if(this.isInline) {
19935             return;
19936         }
19937         this.picker().hide();
19938         this.fireEvent('hide', this, this.date);
19939         
19940     },
19941     
19942     onMousedown: function(e)
19943     {
19944         e.stopPropagation();
19945         e.preventDefault();
19946     },
19947     
19948     keyup: function(e)
19949     {
19950         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19951         this.update();
19952     },
19953
19954     fireKey: function(e)
19955     {
19956         if (!this.picker().isVisible()){
19957             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19958                 this.show();
19959             }
19960             return;
19961         }
19962         
19963         var dir;
19964         
19965         switch(e.keyCode){
19966             case 27: // escape
19967                 this.hide();
19968                 e.preventDefault();
19969                 break;
19970             case 37: // left
19971             case 39: // right
19972                 dir = e.keyCode == 37 ? -1 : 1;
19973                 
19974                 this.vIndex = this.vIndex + dir;
19975                 
19976                 if(this.vIndex < 0){
19977                     this.vIndex = 0;
19978                 }
19979                 
19980                 if(this.vIndex > 11){
19981                     this.vIndex = 11;
19982                 }
19983                 
19984                 if(isNaN(this.vIndex)){
19985                     this.vIndex = 0;
19986                 }
19987                 
19988                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19989                 
19990                 break;
19991             case 38: // up
19992             case 40: // down
19993                 
19994                 dir = e.keyCode == 38 ? -1 : 1;
19995                 
19996                 this.vIndex = this.vIndex + dir * 4;
19997                 
19998                 if(this.vIndex < 0){
19999                     this.vIndex = 0;
20000                 }
20001                 
20002                 if(this.vIndex > 11){
20003                     this.vIndex = 11;
20004                 }
20005                 
20006                 if(isNaN(this.vIndex)){
20007                     this.vIndex = 0;
20008                 }
20009                 
20010                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20011                 break;
20012                 
20013             case 13: // enter
20014                 
20015                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20016                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20017                 }
20018                 
20019                 this.hide();
20020                 e.preventDefault();
20021                 break;
20022             case 9: // tab
20023                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20024                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20025                 }
20026                 this.hide();
20027                 break;
20028             case 16: // shift
20029             case 17: // ctrl
20030             case 18: // alt
20031                 break;
20032             default :
20033                 this.hide();
20034                 
20035         }
20036     },
20037     
20038     remove: function() 
20039     {
20040         this.picker().remove();
20041     }
20042    
20043 });
20044
20045 Roo.apply(Roo.bootstrap.MonthField,  {
20046     
20047     content : {
20048         tag: 'tbody',
20049         cn: [
20050         {
20051             tag: 'tr',
20052             cn: [
20053             {
20054                 tag: 'td',
20055                 colspan: '7'
20056             }
20057             ]
20058         }
20059         ]
20060     },
20061     
20062     dates:{
20063         en: {
20064             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20065             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20066         }
20067     }
20068 });
20069
20070 Roo.apply(Roo.bootstrap.MonthField,  {
20071   
20072     template : {
20073         tag: 'div',
20074         cls: 'datepicker dropdown-menu roo-dynamic',
20075         cn: [
20076             {
20077                 tag: 'div',
20078                 cls: 'datepicker-months',
20079                 cn: [
20080                 {
20081                     tag: 'table',
20082                     cls: 'table-condensed',
20083                     cn:[
20084                         Roo.bootstrap.DateField.content
20085                     ]
20086                 }
20087                 ]
20088             }
20089         ]
20090     }
20091 });
20092
20093  
20094
20095  
20096  /*
20097  * - LGPL
20098  *
20099  * CheckBox
20100  * 
20101  */
20102
20103 /**
20104  * @class Roo.bootstrap.CheckBox
20105  * @extends Roo.bootstrap.Input
20106  * Bootstrap CheckBox class
20107  * 
20108  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20109  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20110  * @cfg {String} boxLabel The text that appears beside the checkbox
20111  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20112  * @cfg {Boolean} checked initnal the element
20113  * @cfg {Boolean} inline inline the element (default false)
20114  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20115  * 
20116  * @constructor
20117  * Create a new CheckBox
20118  * @param {Object} config The config object
20119  */
20120
20121 Roo.bootstrap.CheckBox = function(config){
20122     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20123    
20124     this.addEvents({
20125         /**
20126         * @event check
20127         * Fires when the element is checked or unchecked.
20128         * @param {Roo.bootstrap.CheckBox} this This input
20129         * @param {Boolean} checked The new checked value
20130         */
20131        check : true
20132     });
20133     
20134 };
20135
20136 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20137   
20138     inputType: 'checkbox',
20139     inputValue: 1,
20140     valueOff: 0,
20141     boxLabel: false,
20142     checked: false,
20143     weight : false,
20144     inline: false,
20145     
20146     getAutoCreate : function()
20147     {
20148         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20149         
20150         var id = Roo.id();
20151         
20152         var cfg = {};
20153         
20154         cfg.cls = 'form-group ' + this.inputType; //input-group
20155         
20156         if(this.inline){
20157             cfg.cls += ' ' + this.inputType + '-inline';
20158         }
20159         
20160         var input =  {
20161             tag: 'input',
20162             id : id,
20163             type : this.inputType,
20164             value : this.inputValue,
20165             cls : 'roo-' + this.inputType, //'form-box',
20166             placeholder : this.placeholder || ''
20167             
20168         };
20169         
20170         if(this.inputType != 'radio'){
20171             var hidden =  {
20172                 tag: 'input',
20173                 type : 'hidden',
20174                 cls : 'roo-hidden-value',
20175                 value : this.checked ? this.valueOff : this.inputValue
20176             };
20177         }
20178         
20179             
20180         if (this.weight) { // Validity check?
20181             cfg.cls += " " + this.inputType + "-" + this.weight;
20182         }
20183         
20184         if (this.disabled) {
20185             input.disabled=true;
20186         }
20187         
20188         if(this.checked){
20189             input.checked = this.checked;
20190             
20191         }
20192         
20193         
20194         if (this.name) {
20195             
20196             input.name = this.name;
20197             
20198             if(this.inputType != 'radio'){
20199                 hidden.name = this.name;
20200                 input.name = '_hidden_' + this.name;
20201             }
20202         }
20203         
20204         if (this.size) {
20205             input.cls += ' input-' + this.size;
20206         }
20207         
20208         var settings=this;
20209         
20210         ['xs','sm','md','lg'].map(function(size){
20211             if (settings[size]) {
20212                 cfg.cls += ' col-' + size + '-' + settings[size];
20213             }
20214         });
20215         
20216         var inputblock = input;
20217          
20218         if (this.before || this.after) {
20219             
20220             inputblock = {
20221                 cls : 'input-group',
20222                 cn :  [] 
20223             };
20224             
20225             if (this.before) {
20226                 inputblock.cn.push({
20227                     tag :'span',
20228                     cls : 'input-group-addon',
20229                     html : this.before
20230                 });
20231             }
20232             
20233             inputblock.cn.push(input);
20234             
20235             if(this.inputType != 'radio'){
20236                 inputblock.cn.push(hidden);
20237             }
20238             
20239             if (this.after) {
20240                 inputblock.cn.push({
20241                     tag :'span',
20242                     cls : 'input-group-addon',
20243                     html : this.after
20244                 });
20245             }
20246             
20247         }
20248         
20249         if (align ==='left' && this.fieldLabel.length) {
20250 //                Roo.log("left and has label");
20251             cfg.cn = [
20252                 {
20253                     tag: 'label',
20254                     'for' :  id,
20255                     cls : 'control-label',
20256                     html : this.fieldLabel
20257
20258                 },
20259                 {
20260                     cls : "", 
20261                     cn: [
20262                         inputblock
20263                     ]
20264                 }
20265             ];
20266             
20267             if(this.labelWidth > 12){
20268                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20269             }
20270             
20271             if(this.labelWidth < 13 && this.labelmd == 0){
20272                 this.labelmd = this.labelWidth;
20273             }
20274             
20275             if(this.labellg > 0){
20276                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20277                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20278             }
20279             
20280             if(this.labelmd > 0){
20281                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20282                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20283             }
20284             
20285             if(this.labelsm > 0){
20286                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20287                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20288             }
20289             
20290             if(this.labelxs > 0){
20291                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20292                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20293             }
20294             
20295         } else if ( this.fieldLabel.length) {
20296 //                Roo.log(" label");
20297                 cfg.cn = [
20298                    
20299                     {
20300                         tag: this.boxLabel ? 'span' : 'label',
20301                         'for': id,
20302                         cls: 'control-label box-input-label',
20303                         //cls : 'input-group-addon',
20304                         html : this.fieldLabel
20305                         
20306                     },
20307                     
20308                     inputblock
20309                     
20310                 ];
20311
20312         } else {
20313             
20314 //                Roo.log(" no label && no align");
20315                 cfg.cn = [  inputblock ] ;
20316                 
20317                 
20318         }
20319         
20320         if(this.boxLabel){
20321              var boxLabelCfg = {
20322                 tag: 'label',
20323                 //'for': id, // box label is handled by onclick - so no for...
20324                 cls: 'box-label',
20325                 html: this.boxLabel
20326             };
20327             
20328             if(this.tooltip){
20329                 boxLabelCfg.tooltip = this.tooltip;
20330             }
20331              
20332             cfg.cn.push(boxLabelCfg);
20333         }
20334         
20335         if(this.inputType != 'radio'){
20336             cfg.cn.push(hidden);
20337         }
20338         
20339         return cfg;
20340         
20341     },
20342     
20343     /**
20344      * return the real input element.
20345      */
20346     inputEl: function ()
20347     {
20348         return this.el.select('input.roo-' + this.inputType,true).first();
20349     },
20350     hiddenEl: function ()
20351     {
20352         return this.el.select('input.roo-hidden-value',true).first();
20353     },
20354     
20355     labelEl: function()
20356     {
20357         return this.el.select('label.control-label',true).first();
20358     },
20359     /* depricated... */
20360     
20361     label: function()
20362     {
20363         return this.labelEl();
20364     },
20365     
20366     boxLabelEl: function()
20367     {
20368         return this.el.select('label.box-label',true).first();
20369     },
20370     
20371     initEvents : function()
20372     {
20373 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20374         
20375         this.inputEl().on('click', this.onClick,  this);
20376         
20377         if (this.boxLabel) { 
20378             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20379         }
20380         
20381         this.startValue = this.getValue();
20382         
20383         if(this.groupId){
20384             Roo.bootstrap.CheckBox.register(this);
20385         }
20386     },
20387     
20388     onClick : function()
20389     {   
20390         this.setChecked(!this.checked);
20391     },
20392     
20393     setChecked : function(state,suppressEvent)
20394     {
20395         this.startValue = this.getValue();
20396
20397         if(this.inputType == 'radio'){
20398             
20399             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20400                 e.dom.checked = false;
20401             });
20402             
20403             this.inputEl().dom.checked = true;
20404             
20405             this.inputEl().dom.value = this.inputValue;
20406             
20407             if(suppressEvent !== true){
20408                 this.fireEvent('check', this, true);
20409             }
20410             
20411             this.validate();
20412             
20413             return;
20414         }
20415         
20416         this.checked = state;
20417         
20418         this.inputEl().dom.checked = state;
20419         
20420         
20421         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20422         
20423         if(suppressEvent !== true){
20424             this.fireEvent('check', this, state);
20425         }
20426         
20427         this.validate();
20428     },
20429     
20430     getValue : function()
20431     {
20432         if(this.inputType == 'radio'){
20433             return this.getGroupValue();
20434         }
20435         
20436         return this.hiddenEl().dom.value;
20437         
20438     },
20439     
20440     getGroupValue : function()
20441     {
20442         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20443             return '';
20444         }
20445         
20446         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20447     },
20448     
20449     setValue : function(v,suppressEvent)
20450     {
20451         if(this.inputType == 'radio'){
20452             this.setGroupValue(v, suppressEvent);
20453             return;
20454         }
20455         
20456         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20457         
20458         this.validate();
20459     },
20460     
20461     setGroupValue : function(v, suppressEvent)
20462     {
20463         this.startValue = this.getValue();
20464         
20465         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20466             e.dom.checked = false;
20467             
20468             if(e.dom.value == v){
20469                 e.dom.checked = true;
20470             }
20471         });
20472         
20473         if(suppressEvent !== true){
20474             this.fireEvent('check', this, true);
20475         }
20476
20477         this.validate();
20478         
20479         return;
20480     },
20481     
20482     validate : function()
20483     {
20484         if(
20485                 this.disabled || 
20486                 (this.inputType == 'radio' && this.validateRadio()) ||
20487                 (this.inputType == 'checkbox' && this.validateCheckbox())
20488         ){
20489             this.markValid();
20490             return true;
20491         }
20492         
20493         this.markInvalid();
20494         return false;
20495     },
20496     
20497     validateRadio : function()
20498     {
20499         if(this.allowBlank){
20500             return true;
20501         }
20502         
20503         var valid = false;
20504         
20505         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20506             if(!e.dom.checked){
20507                 return;
20508             }
20509             
20510             valid = true;
20511             
20512             return false;
20513         });
20514         
20515         return valid;
20516     },
20517     
20518     validateCheckbox : function()
20519     {
20520         if(!this.groupId){
20521             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20522             //return (this.getValue() == this.inputValue) ? true : false;
20523         }
20524         
20525         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20526         
20527         if(!group){
20528             return false;
20529         }
20530         
20531         var r = false;
20532         
20533         for(var i in group){
20534             if(r){
20535                 break;
20536             }
20537             
20538             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20539         }
20540         
20541         return r;
20542     },
20543     
20544     /**
20545      * Mark this field as valid
20546      */
20547     markValid : function()
20548     {
20549         var _this = this;
20550         
20551         this.fireEvent('valid', this);
20552         
20553         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20554         
20555         if(this.groupId){
20556             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20557         }
20558         
20559         if(label){
20560             label.markValid();
20561         }
20562
20563         if(this.inputType == 'radio'){
20564             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20565                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20566                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20567             });
20568             
20569             return;
20570         }
20571
20572         if(!this.groupId){
20573             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20574             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20575             return;
20576         }
20577         
20578         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20579         
20580         if(!group){
20581             return;
20582         }
20583         
20584         for(var i in group){
20585             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20586             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20587         }
20588     },
20589     
20590      /**
20591      * Mark this field as invalid
20592      * @param {String} msg The validation message
20593      */
20594     markInvalid : function(msg)
20595     {
20596         if(this.allowBlank){
20597             return;
20598         }
20599         
20600         var _this = this;
20601         
20602         this.fireEvent('invalid', this, msg);
20603         
20604         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20605         
20606         if(this.groupId){
20607             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20608         }
20609         
20610         if(label){
20611             label.markInvalid();
20612         }
20613             
20614         if(this.inputType == 'radio'){
20615             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20616                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20617                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20618             });
20619             
20620             return;
20621         }
20622         
20623         if(!this.groupId){
20624             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20625             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20626             return;
20627         }
20628         
20629         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20630         
20631         if(!group){
20632             return;
20633         }
20634         
20635         for(var i in group){
20636             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20637             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20638         }
20639         
20640     },
20641     
20642     clearInvalid : function()
20643     {
20644         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20645         
20646         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20647         
20648         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20649         
20650         if (label) {
20651             label.iconEl.removeClass(label.validClass);
20652             label.iconEl.removeClass(label.invalidClass);
20653         }
20654     },
20655     
20656     disable : function()
20657     {
20658         if(this.inputType != 'radio'){
20659             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20660             return;
20661         }
20662         
20663         var _this = this;
20664         
20665         if(this.rendered){
20666             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20667                 _this.getActionEl().addClass(this.disabledClass);
20668                 e.dom.disabled = true;
20669             });
20670         }
20671         
20672         this.disabled = true;
20673         this.fireEvent("disable", this);
20674         return this;
20675     },
20676
20677     enable : function()
20678     {
20679         if(this.inputType != 'radio'){
20680             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20681             return;
20682         }
20683         
20684         var _this = this;
20685         
20686         if(this.rendered){
20687             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20688                 _this.getActionEl().removeClass(this.disabledClass);
20689                 e.dom.disabled = false;
20690             });
20691         }
20692         
20693         this.disabled = false;
20694         this.fireEvent("enable", this);
20695         return this;
20696     }
20697
20698 });
20699
20700 Roo.apply(Roo.bootstrap.CheckBox, {
20701     
20702     groups: {},
20703     
20704      /**
20705     * register a CheckBox Group
20706     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20707     */
20708     register : function(checkbox)
20709     {
20710         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20711             this.groups[checkbox.groupId] = {};
20712         }
20713         
20714         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20715             return;
20716         }
20717         
20718         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20719         
20720     },
20721     /**
20722     * fetch a CheckBox Group based on the group ID
20723     * @param {string} the group ID
20724     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20725     */
20726     get: function(groupId) {
20727         if (typeof(this.groups[groupId]) == 'undefined') {
20728             return false;
20729         }
20730         
20731         return this.groups[groupId] ;
20732     }
20733     
20734     
20735 });
20736 /*
20737  * - LGPL
20738  *
20739  * RadioItem
20740  * 
20741  */
20742
20743 /**
20744  * @class Roo.bootstrap.Radio
20745  * @extends Roo.bootstrap.Component
20746  * Bootstrap Radio class
20747  * @cfg {String} boxLabel - the label associated
20748  * @cfg {String} value - the value of radio
20749  * 
20750  * @constructor
20751  * Create a new Radio
20752  * @param {Object} config The config object
20753  */
20754 Roo.bootstrap.Radio = function(config){
20755     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20756     
20757 };
20758
20759 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20760     
20761     boxLabel : '',
20762     
20763     value : '',
20764     
20765     getAutoCreate : function()
20766     {
20767         var cfg = {
20768             tag : 'div',
20769             cls : 'form-group radio',
20770             cn : [
20771                 {
20772                     tag : 'label',
20773                     cls : 'box-label',
20774                     html : this.boxLabel
20775                 }
20776             ]
20777         };
20778         
20779         return cfg;
20780     },
20781     
20782     initEvents : function() 
20783     {
20784         this.parent().register(this);
20785         
20786         this.el.on('click', this.onClick, this);
20787         
20788     },
20789     
20790     onClick : function()
20791     {
20792         this.setChecked(true);
20793     },
20794     
20795     setChecked : function(state, suppressEvent)
20796     {
20797         this.parent().setValue(this.value, suppressEvent);
20798         
20799     },
20800     
20801     setBoxLabel : function(v)
20802     {
20803         this.boxLabel = v;
20804         
20805         if(this.rendered){
20806             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20807         }
20808     }
20809     
20810 });
20811  
20812
20813  /*
20814  * - LGPL
20815  *
20816  * Input
20817  * 
20818  */
20819
20820 /**
20821  * @class Roo.bootstrap.SecurePass
20822  * @extends Roo.bootstrap.Input
20823  * Bootstrap SecurePass class
20824  *
20825  * 
20826  * @constructor
20827  * Create a new SecurePass
20828  * @param {Object} config The config object
20829  */
20830  
20831 Roo.bootstrap.SecurePass = function (config) {
20832     // these go here, so the translation tool can replace them..
20833     this.errors = {
20834         PwdEmpty: "Please type a password, and then retype it to confirm.",
20835         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20836         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20837         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20838         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20839         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20840         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20841         TooWeak: "Your password is Too Weak."
20842     },
20843     this.meterLabel = "Password strength:";
20844     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20845     this.meterClass = [
20846         "roo-password-meter-tooweak", 
20847         "roo-password-meter-weak", 
20848         "roo-password-meter-medium", 
20849         "roo-password-meter-strong", 
20850         "roo-password-meter-grey"
20851     ];
20852     
20853     this.errors = {};
20854     
20855     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20856 }
20857
20858 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20859     /**
20860      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20861      * {
20862      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20863      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20864      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20865      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20866      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20867      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20868      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20869      * })
20870      */
20871     // private
20872     
20873     meterWidth: 300,
20874     errorMsg :'',    
20875     errors: false,
20876     imageRoot: '/',
20877     /**
20878      * @cfg {String/Object} Label for the strength meter (defaults to
20879      * 'Password strength:')
20880      */
20881     // private
20882     meterLabel: '',
20883     /**
20884      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20885      * ['Weak', 'Medium', 'Strong'])
20886      */
20887     // private    
20888     pwdStrengths: false,    
20889     // private
20890     strength: 0,
20891     // private
20892     _lastPwd: null,
20893     // private
20894     kCapitalLetter: 0,
20895     kSmallLetter: 1,
20896     kDigit: 2,
20897     kPunctuation: 3,
20898     
20899     insecure: false,
20900     // private
20901     initEvents: function ()
20902     {
20903         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20904
20905         if (this.el.is('input[type=password]') && Roo.isSafari) {
20906             this.el.on('keydown', this.SafariOnKeyDown, this);
20907         }
20908
20909         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20910     },
20911     // private
20912     onRender: function (ct, position)
20913     {
20914         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20915         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20916         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20917
20918         this.trigger.createChild({
20919                    cn: [
20920                     {
20921                     //id: 'PwdMeter',
20922                     tag: 'div',
20923                     cls: 'roo-password-meter-grey col-xs-12',
20924                     style: {
20925                         //width: 0,
20926                         //width: this.meterWidth + 'px'                                                
20927                         }
20928                     },
20929                     {                            
20930                          cls: 'roo-password-meter-text'                          
20931                     }
20932                 ]            
20933         });
20934
20935          
20936         if (this.hideTrigger) {
20937             this.trigger.setDisplayed(false);
20938         }
20939         this.setSize(this.width || '', this.height || '');
20940     },
20941     // private
20942     onDestroy: function ()
20943     {
20944         if (this.trigger) {
20945             this.trigger.removeAllListeners();
20946             this.trigger.remove();
20947         }
20948         if (this.wrap) {
20949             this.wrap.remove();
20950         }
20951         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20952     },
20953     // private
20954     checkStrength: function ()
20955     {
20956         var pwd = this.inputEl().getValue();
20957         if (pwd == this._lastPwd) {
20958             return;
20959         }
20960
20961         var strength;
20962         if (this.ClientSideStrongPassword(pwd)) {
20963             strength = 3;
20964         } else if (this.ClientSideMediumPassword(pwd)) {
20965             strength = 2;
20966         } else if (this.ClientSideWeakPassword(pwd)) {
20967             strength = 1;
20968         } else {
20969             strength = 0;
20970         }
20971         
20972         Roo.log('strength1: ' + strength);
20973         
20974         //var pm = this.trigger.child('div/div/div').dom;
20975         var pm = this.trigger.child('div/div');
20976         pm.removeClass(this.meterClass);
20977         pm.addClass(this.meterClass[strength]);
20978                 
20979         
20980         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20981                 
20982         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20983         
20984         this._lastPwd = pwd;
20985     },
20986     reset: function ()
20987     {
20988         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20989         
20990         this._lastPwd = '';
20991         
20992         var pm = this.trigger.child('div/div');
20993         pm.removeClass(this.meterClass);
20994         pm.addClass('roo-password-meter-grey');        
20995         
20996         
20997         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20998         
20999         pt.innerHTML = '';
21000         this.inputEl().dom.type='password';
21001     },
21002     // private
21003     validateValue: function (value)
21004     {
21005         
21006         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21007             return false;
21008         }
21009         if (value.length == 0) {
21010             if (this.allowBlank) {
21011                 this.clearInvalid();
21012                 return true;
21013             }
21014
21015             this.markInvalid(this.errors.PwdEmpty);
21016             this.errorMsg = this.errors.PwdEmpty;
21017             return false;
21018         }
21019         
21020         if(this.insecure){
21021             return true;
21022         }
21023         
21024         if ('[\x21-\x7e]*'.match(value)) {
21025             this.markInvalid(this.errors.PwdBadChar);
21026             this.errorMsg = this.errors.PwdBadChar;
21027             return false;
21028         }
21029         if (value.length < 6) {
21030             this.markInvalid(this.errors.PwdShort);
21031             this.errorMsg = this.errors.PwdShort;
21032             return false;
21033         }
21034         if (value.length > 16) {
21035             this.markInvalid(this.errors.PwdLong);
21036             this.errorMsg = this.errors.PwdLong;
21037             return false;
21038         }
21039         var strength;
21040         if (this.ClientSideStrongPassword(value)) {
21041             strength = 3;
21042         } else if (this.ClientSideMediumPassword(value)) {
21043             strength = 2;
21044         } else if (this.ClientSideWeakPassword(value)) {
21045             strength = 1;
21046         } else {
21047             strength = 0;
21048         }
21049
21050         
21051         if (strength < 2) {
21052             //this.markInvalid(this.errors.TooWeak);
21053             this.errorMsg = this.errors.TooWeak;
21054             //return false;
21055         }
21056         
21057         
21058         console.log('strength2: ' + strength);
21059         
21060         //var pm = this.trigger.child('div/div/div').dom;
21061         
21062         var pm = this.trigger.child('div/div');
21063         pm.removeClass(this.meterClass);
21064         pm.addClass(this.meterClass[strength]);
21065                 
21066         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21067                 
21068         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21069         
21070         this.errorMsg = ''; 
21071         return true;
21072     },
21073     // private
21074     CharacterSetChecks: function (type)
21075     {
21076         this.type = type;
21077         this.fResult = false;
21078     },
21079     // private
21080     isctype: function (character, type)
21081     {
21082         switch (type) {  
21083             case this.kCapitalLetter:
21084                 if (character >= 'A' && character <= 'Z') {
21085                     return true;
21086                 }
21087                 break;
21088             
21089             case this.kSmallLetter:
21090                 if (character >= 'a' && character <= 'z') {
21091                     return true;
21092                 }
21093                 break;
21094             
21095             case this.kDigit:
21096                 if (character >= '0' && character <= '9') {
21097                     return true;
21098                 }
21099                 break;
21100             
21101             case this.kPunctuation:
21102                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21103                     return true;
21104                 }
21105                 break;
21106             
21107             default:
21108                 return false;
21109         }
21110
21111     },
21112     // private
21113     IsLongEnough: function (pwd, size)
21114     {
21115         return !(pwd == null || isNaN(size) || pwd.length < size);
21116     },
21117     // private
21118     SpansEnoughCharacterSets: function (word, nb)
21119     {
21120         if (!this.IsLongEnough(word, nb))
21121         {
21122             return false;
21123         }
21124
21125         var characterSetChecks = new Array(
21126             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21127             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21128         );
21129         
21130         for (var index = 0; index < word.length; ++index) {
21131             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21132                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21133                     characterSetChecks[nCharSet].fResult = true;
21134                     break;
21135                 }
21136             }
21137         }
21138
21139         var nCharSets = 0;
21140         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21141             if (characterSetChecks[nCharSet].fResult) {
21142                 ++nCharSets;
21143             }
21144         }
21145
21146         if (nCharSets < nb) {
21147             return false;
21148         }
21149         return true;
21150     },
21151     // private
21152     ClientSideStrongPassword: function (pwd)
21153     {
21154         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21155     },
21156     // private
21157     ClientSideMediumPassword: function (pwd)
21158     {
21159         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21160     },
21161     // private
21162     ClientSideWeakPassword: function (pwd)
21163     {
21164         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21165     }
21166           
21167 })//<script type="text/javascript">
21168
21169 /*
21170  * Based  Ext JS Library 1.1.1
21171  * Copyright(c) 2006-2007, Ext JS, LLC.
21172  * LGPL
21173  *
21174  */
21175  
21176 /**
21177  * @class Roo.HtmlEditorCore
21178  * @extends Roo.Component
21179  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21180  *
21181  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21182  */
21183
21184 Roo.HtmlEditorCore = function(config){
21185     
21186     
21187     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21188     
21189     
21190     this.addEvents({
21191         /**
21192          * @event initialize
21193          * Fires when the editor is fully initialized (including the iframe)
21194          * @param {Roo.HtmlEditorCore} this
21195          */
21196         initialize: true,
21197         /**
21198          * @event activate
21199          * Fires when the editor is first receives the focus. Any insertion must wait
21200          * until after this event.
21201          * @param {Roo.HtmlEditorCore} this
21202          */
21203         activate: true,
21204          /**
21205          * @event beforesync
21206          * Fires before the textarea is updated with content from the editor iframe. Return false
21207          * to cancel the sync.
21208          * @param {Roo.HtmlEditorCore} this
21209          * @param {String} html
21210          */
21211         beforesync: true,
21212          /**
21213          * @event beforepush
21214          * Fires before the iframe editor is updated with content from the textarea. Return false
21215          * to cancel the push.
21216          * @param {Roo.HtmlEditorCore} this
21217          * @param {String} html
21218          */
21219         beforepush: true,
21220          /**
21221          * @event sync
21222          * Fires when the textarea is updated with content from the editor iframe.
21223          * @param {Roo.HtmlEditorCore} this
21224          * @param {String} html
21225          */
21226         sync: true,
21227          /**
21228          * @event push
21229          * Fires when the iframe editor is updated with content from the textarea.
21230          * @param {Roo.HtmlEditorCore} this
21231          * @param {String} html
21232          */
21233         push: true,
21234         
21235         /**
21236          * @event editorevent
21237          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21238          * @param {Roo.HtmlEditorCore} this
21239          */
21240         editorevent: true
21241         
21242     });
21243     
21244     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21245     
21246     // defaults : white / black...
21247     this.applyBlacklists();
21248     
21249     
21250     
21251 };
21252
21253
21254 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21255
21256
21257      /**
21258      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21259      */
21260     
21261     owner : false,
21262     
21263      /**
21264      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21265      *                        Roo.resizable.
21266      */
21267     resizable : false,
21268      /**
21269      * @cfg {Number} height (in pixels)
21270      */   
21271     height: 300,
21272    /**
21273      * @cfg {Number} width (in pixels)
21274      */   
21275     width: 500,
21276     
21277     /**
21278      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21279      * 
21280      */
21281     stylesheets: false,
21282     
21283     // id of frame..
21284     frameId: false,
21285     
21286     // private properties
21287     validationEvent : false,
21288     deferHeight: true,
21289     initialized : false,
21290     activated : false,
21291     sourceEditMode : false,
21292     onFocus : Roo.emptyFn,
21293     iframePad:3,
21294     hideMode:'offsets',
21295     
21296     clearUp: true,
21297     
21298     // blacklist + whitelisted elements..
21299     black: false,
21300     white: false,
21301      
21302     bodyCls : '',
21303
21304     /**
21305      * Protected method that will not generally be called directly. It
21306      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21307      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21308      */
21309     getDocMarkup : function(){
21310         // body styles..
21311         var st = '';
21312         
21313         // inherit styels from page...?? 
21314         if (this.stylesheets === false) {
21315             
21316             Roo.get(document.head).select('style').each(function(node) {
21317                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21318             });
21319             
21320             Roo.get(document.head).select('link').each(function(node) { 
21321                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21322             });
21323             
21324         } else if (!this.stylesheets.length) {
21325                 // simple..
21326                 st = '<style type="text/css">' +
21327                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21328                    '</style>';
21329         } else { 
21330             st = '<style type="text/css">' +
21331                     this.stylesheets +
21332                 '</style>';
21333         }
21334         
21335         st +=  '<style type="text/css">' +
21336             'IMG { cursor: pointer } ' +
21337         '</style>';
21338
21339         var cls = 'roo-htmleditor-body';
21340         
21341         if(this.bodyCls.length){
21342             cls += ' ' + this.bodyCls;
21343         }
21344         
21345         return '<html><head>' + st  +
21346             //<style type="text/css">' +
21347             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21348             //'</style>' +
21349             ' </head><body class="' +  cls + '"></body></html>';
21350     },
21351
21352     // private
21353     onRender : function(ct, position)
21354     {
21355         var _t = this;
21356         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21357         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21358         
21359         
21360         this.el.dom.style.border = '0 none';
21361         this.el.dom.setAttribute('tabIndex', -1);
21362         this.el.addClass('x-hidden hide');
21363         
21364         
21365         
21366         if(Roo.isIE){ // fix IE 1px bogus margin
21367             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21368         }
21369        
21370         
21371         this.frameId = Roo.id();
21372         
21373          
21374         
21375         var iframe = this.owner.wrap.createChild({
21376             tag: 'iframe',
21377             cls: 'form-control', // bootstrap..
21378             id: this.frameId,
21379             name: this.frameId,
21380             frameBorder : 'no',
21381             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21382         }, this.el
21383         );
21384         
21385         
21386         this.iframe = iframe.dom;
21387
21388          this.assignDocWin();
21389         
21390         this.doc.designMode = 'on';
21391        
21392         this.doc.open();
21393         this.doc.write(this.getDocMarkup());
21394         this.doc.close();
21395
21396         
21397         var task = { // must defer to wait for browser to be ready
21398             run : function(){
21399                 //console.log("run task?" + this.doc.readyState);
21400                 this.assignDocWin();
21401                 if(this.doc.body || this.doc.readyState == 'complete'){
21402                     try {
21403                         this.doc.designMode="on";
21404                     } catch (e) {
21405                         return;
21406                     }
21407                     Roo.TaskMgr.stop(task);
21408                     this.initEditor.defer(10, this);
21409                 }
21410             },
21411             interval : 10,
21412             duration: 10000,
21413             scope: this
21414         };
21415         Roo.TaskMgr.start(task);
21416
21417     },
21418
21419     // private
21420     onResize : function(w, h)
21421     {
21422          Roo.log('resize: ' +w + ',' + h );
21423         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21424         if(!this.iframe){
21425             return;
21426         }
21427         if(typeof w == 'number'){
21428             
21429             this.iframe.style.width = w + 'px';
21430         }
21431         if(typeof h == 'number'){
21432             
21433             this.iframe.style.height = h + 'px';
21434             if(this.doc){
21435                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21436             }
21437         }
21438         
21439     },
21440
21441     /**
21442      * Toggles the editor between standard and source edit mode.
21443      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21444      */
21445     toggleSourceEdit : function(sourceEditMode){
21446         
21447         this.sourceEditMode = sourceEditMode === true;
21448         
21449         if(this.sourceEditMode){
21450  
21451             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21452             
21453         }else{
21454             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21455             //this.iframe.className = '';
21456             this.deferFocus();
21457         }
21458         //this.setSize(this.owner.wrap.getSize());
21459         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21460     },
21461
21462     
21463   
21464
21465     /**
21466      * Protected method that will not generally be called directly. If you need/want
21467      * custom HTML cleanup, this is the method you should override.
21468      * @param {String} html The HTML to be cleaned
21469      * return {String} The cleaned HTML
21470      */
21471     cleanHtml : function(html){
21472         html = String(html);
21473         if(html.length > 5){
21474             if(Roo.isSafari){ // strip safari nonsense
21475                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21476             }
21477         }
21478         if(html == '&nbsp;'){
21479             html = '';
21480         }
21481         return html;
21482     },
21483
21484     /**
21485      * HTML Editor -> Textarea
21486      * Protected method that will not generally be called directly. Syncs the contents
21487      * of the editor iframe with the textarea.
21488      */
21489     syncValue : function(){
21490         if(this.initialized){
21491             var bd = (this.doc.body || this.doc.documentElement);
21492             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21493             var html = bd.innerHTML;
21494             if(Roo.isSafari){
21495                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21496                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21497                 if(m && m[1]){
21498                     html = '<div style="'+m[0]+'">' + html + '</div>';
21499                 }
21500             }
21501             html = this.cleanHtml(html);
21502             // fix up the special chars.. normaly like back quotes in word...
21503             // however we do not want to do this with chinese..
21504             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21505                 var cc = b.charCodeAt();
21506                 if (
21507                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21508                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21509                     (cc >= 0xf900 && cc < 0xfb00 )
21510                 ) {
21511                         return b;
21512                 }
21513                 return "&#"+cc+";" 
21514             });
21515             if(this.owner.fireEvent('beforesync', this, html) !== false){
21516                 this.el.dom.value = html;
21517                 this.owner.fireEvent('sync', this, html);
21518             }
21519         }
21520     },
21521
21522     /**
21523      * Protected method that will not generally be called directly. Pushes the value of the textarea
21524      * into the iframe editor.
21525      */
21526     pushValue : function(){
21527         if(this.initialized){
21528             var v = this.el.dom.value.trim();
21529             
21530 //            if(v.length < 1){
21531 //                v = '&#160;';
21532 //            }
21533             
21534             if(this.owner.fireEvent('beforepush', this, v) !== false){
21535                 var d = (this.doc.body || this.doc.documentElement);
21536                 d.innerHTML = v;
21537                 this.cleanUpPaste();
21538                 this.el.dom.value = d.innerHTML;
21539                 this.owner.fireEvent('push', this, v);
21540             }
21541         }
21542     },
21543
21544     // private
21545     deferFocus : function(){
21546         this.focus.defer(10, this);
21547     },
21548
21549     // doc'ed in Field
21550     focus : function(){
21551         if(this.win && !this.sourceEditMode){
21552             this.win.focus();
21553         }else{
21554             this.el.focus();
21555         }
21556     },
21557     
21558     assignDocWin: function()
21559     {
21560         var iframe = this.iframe;
21561         
21562          if(Roo.isIE){
21563             this.doc = iframe.contentWindow.document;
21564             this.win = iframe.contentWindow;
21565         } else {
21566 //            if (!Roo.get(this.frameId)) {
21567 //                return;
21568 //            }
21569 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21570 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21571             
21572             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21573                 return;
21574             }
21575             
21576             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21577             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21578         }
21579     },
21580     
21581     // private
21582     initEditor : function(){
21583         //console.log("INIT EDITOR");
21584         this.assignDocWin();
21585         
21586         
21587         
21588         this.doc.designMode="on";
21589         this.doc.open();
21590         this.doc.write(this.getDocMarkup());
21591         this.doc.close();
21592         
21593         var dbody = (this.doc.body || this.doc.documentElement);
21594         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21595         // this copies styles from the containing element into thsi one..
21596         // not sure why we need all of this..
21597         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21598         
21599         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21600         //ss['background-attachment'] = 'fixed'; // w3c
21601         dbody.bgProperties = 'fixed'; // ie
21602         //Roo.DomHelper.applyStyles(dbody, ss);
21603         Roo.EventManager.on(this.doc, {
21604             //'mousedown': this.onEditorEvent,
21605             'mouseup': this.onEditorEvent,
21606             'dblclick': this.onEditorEvent,
21607             'click': this.onEditorEvent,
21608             'keyup': this.onEditorEvent,
21609             buffer:100,
21610             scope: this
21611         });
21612         if(Roo.isGecko){
21613             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21614         }
21615         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21616             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21617         }
21618         this.initialized = true;
21619
21620         this.owner.fireEvent('initialize', this);
21621         this.pushValue();
21622     },
21623
21624     // private
21625     onDestroy : function(){
21626         
21627         
21628         
21629         if(this.rendered){
21630             
21631             //for (var i =0; i < this.toolbars.length;i++) {
21632             //    // fixme - ask toolbars for heights?
21633             //    this.toolbars[i].onDestroy();
21634            // }
21635             
21636             //this.wrap.dom.innerHTML = '';
21637             //this.wrap.remove();
21638         }
21639     },
21640
21641     // private
21642     onFirstFocus : function(){
21643         
21644         this.assignDocWin();
21645         
21646         
21647         this.activated = true;
21648          
21649     
21650         if(Roo.isGecko){ // prevent silly gecko errors
21651             this.win.focus();
21652             var s = this.win.getSelection();
21653             if(!s.focusNode || s.focusNode.nodeType != 3){
21654                 var r = s.getRangeAt(0);
21655                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21656                 r.collapse(true);
21657                 this.deferFocus();
21658             }
21659             try{
21660                 this.execCmd('useCSS', true);
21661                 this.execCmd('styleWithCSS', false);
21662             }catch(e){}
21663         }
21664         this.owner.fireEvent('activate', this);
21665     },
21666
21667     // private
21668     adjustFont: function(btn){
21669         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21670         //if(Roo.isSafari){ // safari
21671         //    adjust *= 2;
21672        // }
21673         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21674         if(Roo.isSafari){ // safari
21675             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21676             v =  (v < 10) ? 10 : v;
21677             v =  (v > 48) ? 48 : v;
21678             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21679             
21680         }
21681         
21682         
21683         v = Math.max(1, v+adjust);
21684         
21685         this.execCmd('FontSize', v  );
21686     },
21687
21688     onEditorEvent : function(e)
21689     {
21690         this.owner.fireEvent('editorevent', this, e);
21691       //  this.updateToolbar();
21692         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21693     },
21694
21695     insertTag : function(tg)
21696     {
21697         // could be a bit smarter... -> wrap the current selected tRoo..
21698         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21699             
21700             range = this.createRange(this.getSelection());
21701             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21702             wrappingNode.appendChild(range.extractContents());
21703             range.insertNode(wrappingNode);
21704
21705             return;
21706             
21707             
21708             
21709         }
21710         this.execCmd("formatblock",   tg);
21711         
21712     },
21713     
21714     insertText : function(txt)
21715     {
21716         
21717         
21718         var range = this.createRange();
21719         range.deleteContents();
21720                //alert(Sender.getAttribute('label'));
21721                
21722         range.insertNode(this.doc.createTextNode(txt));
21723     } ,
21724     
21725      
21726
21727     /**
21728      * Executes a Midas editor command on the editor document and performs necessary focus and
21729      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21730      * @param {String} cmd The Midas command
21731      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21732      */
21733     relayCmd : function(cmd, value){
21734         this.win.focus();
21735         this.execCmd(cmd, value);
21736         this.owner.fireEvent('editorevent', this);
21737         //this.updateToolbar();
21738         this.owner.deferFocus();
21739     },
21740
21741     /**
21742      * Executes a Midas editor command directly on the editor document.
21743      * For visual commands, you should use {@link #relayCmd} instead.
21744      * <b>This should only be called after the editor is initialized.</b>
21745      * @param {String} cmd The Midas command
21746      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21747      */
21748     execCmd : function(cmd, value){
21749         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21750         this.syncValue();
21751     },
21752  
21753  
21754    
21755     /**
21756      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21757      * to insert tRoo.
21758      * @param {String} text | dom node.. 
21759      */
21760     insertAtCursor : function(text)
21761     {
21762         
21763         if(!this.activated){
21764             return;
21765         }
21766         /*
21767         if(Roo.isIE){
21768             this.win.focus();
21769             var r = this.doc.selection.createRange();
21770             if(r){
21771                 r.collapse(true);
21772                 r.pasteHTML(text);
21773                 this.syncValue();
21774                 this.deferFocus();
21775             
21776             }
21777             return;
21778         }
21779         */
21780         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21781             this.win.focus();
21782             
21783             
21784             // from jquery ui (MIT licenced)
21785             var range, node;
21786             var win = this.win;
21787             
21788             if (win.getSelection && win.getSelection().getRangeAt) {
21789                 range = win.getSelection().getRangeAt(0);
21790                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21791                 range.insertNode(node);
21792             } else if (win.document.selection && win.document.selection.createRange) {
21793                 // no firefox support
21794                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21795                 win.document.selection.createRange().pasteHTML(txt);
21796             } else {
21797                 // no firefox support
21798                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21799                 this.execCmd('InsertHTML', txt);
21800             } 
21801             
21802             this.syncValue();
21803             
21804             this.deferFocus();
21805         }
21806     },
21807  // private
21808     mozKeyPress : function(e){
21809         if(e.ctrlKey){
21810             var c = e.getCharCode(), cmd;
21811           
21812             if(c > 0){
21813                 c = String.fromCharCode(c).toLowerCase();
21814                 switch(c){
21815                     case 'b':
21816                         cmd = 'bold';
21817                         break;
21818                     case 'i':
21819                         cmd = 'italic';
21820                         break;
21821                     
21822                     case 'u':
21823                         cmd = 'underline';
21824                         break;
21825                     
21826                     case 'v':
21827                         this.cleanUpPaste.defer(100, this);
21828                         return;
21829                         
21830                 }
21831                 if(cmd){
21832                     this.win.focus();
21833                     this.execCmd(cmd);
21834                     this.deferFocus();
21835                     e.preventDefault();
21836                 }
21837                 
21838             }
21839         }
21840     },
21841
21842     // private
21843     fixKeys : function(){ // load time branching for fastest keydown performance
21844         if(Roo.isIE){
21845             return function(e){
21846                 var k = e.getKey(), r;
21847                 if(k == e.TAB){
21848                     e.stopEvent();
21849                     r = this.doc.selection.createRange();
21850                     if(r){
21851                         r.collapse(true);
21852                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21853                         this.deferFocus();
21854                     }
21855                     return;
21856                 }
21857                 
21858                 if(k == e.ENTER){
21859                     r = this.doc.selection.createRange();
21860                     if(r){
21861                         var target = r.parentElement();
21862                         if(!target || target.tagName.toLowerCase() != 'li'){
21863                             e.stopEvent();
21864                             r.pasteHTML('<br />');
21865                             r.collapse(false);
21866                             r.select();
21867                         }
21868                     }
21869                 }
21870                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21871                     this.cleanUpPaste.defer(100, this);
21872                     return;
21873                 }
21874                 
21875                 
21876             };
21877         }else if(Roo.isOpera){
21878             return function(e){
21879                 var k = e.getKey();
21880                 if(k == e.TAB){
21881                     e.stopEvent();
21882                     this.win.focus();
21883                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21884                     this.deferFocus();
21885                 }
21886                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21887                     this.cleanUpPaste.defer(100, this);
21888                     return;
21889                 }
21890                 
21891             };
21892         }else if(Roo.isSafari){
21893             return function(e){
21894                 var k = e.getKey();
21895                 
21896                 if(k == e.TAB){
21897                     e.stopEvent();
21898                     this.execCmd('InsertText','\t');
21899                     this.deferFocus();
21900                     return;
21901                 }
21902                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21903                     this.cleanUpPaste.defer(100, this);
21904                     return;
21905                 }
21906                 
21907              };
21908         }
21909     }(),
21910     
21911     getAllAncestors: function()
21912     {
21913         var p = this.getSelectedNode();
21914         var a = [];
21915         if (!p) {
21916             a.push(p); // push blank onto stack..
21917             p = this.getParentElement();
21918         }
21919         
21920         
21921         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21922             a.push(p);
21923             p = p.parentNode;
21924         }
21925         a.push(this.doc.body);
21926         return a;
21927     },
21928     lastSel : false,
21929     lastSelNode : false,
21930     
21931     
21932     getSelection : function() 
21933     {
21934         this.assignDocWin();
21935         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21936     },
21937     
21938     getSelectedNode: function() 
21939     {
21940         // this may only work on Gecko!!!
21941         
21942         // should we cache this!!!!
21943         
21944         
21945         
21946          
21947         var range = this.createRange(this.getSelection()).cloneRange();
21948         
21949         if (Roo.isIE) {
21950             var parent = range.parentElement();
21951             while (true) {
21952                 var testRange = range.duplicate();
21953                 testRange.moveToElementText(parent);
21954                 if (testRange.inRange(range)) {
21955                     break;
21956                 }
21957                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21958                     break;
21959                 }
21960                 parent = parent.parentElement;
21961             }
21962             return parent;
21963         }
21964         
21965         // is ancestor a text element.
21966         var ac =  range.commonAncestorContainer;
21967         if (ac.nodeType == 3) {
21968             ac = ac.parentNode;
21969         }
21970         
21971         var ar = ac.childNodes;
21972          
21973         var nodes = [];
21974         var other_nodes = [];
21975         var has_other_nodes = false;
21976         for (var i=0;i<ar.length;i++) {
21977             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21978                 continue;
21979             }
21980             // fullly contained node.
21981             
21982             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21983                 nodes.push(ar[i]);
21984                 continue;
21985             }
21986             
21987             // probably selected..
21988             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21989                 other_nodes.push(ar[i]);
21990                 continue;
21991             }
21992             // outer..
21993             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21994                 continue;
21995             }
21996             
21997             
21998             has_other_nodes = true;
21999         }
22000         if (!nodes.length && other_nodes.length) {
22001             nodes= other_nodes;
22002         }
22003         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22004             return false;
22005         }
22006         
22007         return nodes[0];
22008     },
22009     createRange: function(sel)
22010     {
22011         // this has strange effects when using with 
22012         // top toolbar - not sure if it's a great idea.
22013         //this.editor.contentWindow.focus();
22014         if (typeof sel != "undefined") {
22015             try {
22016                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22017             } catch(e) {
22018                 return this.doc.createRange();
22019             }
22020         } else {
22021             return this.doc.createRange();
22022         }
22023     },
22024     getParentElement: function()
22025     {
22026         
22027         this.assignDocWin();
22028         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22029         
22030         var range = this.createRange(sel);
22031          
22032         try {
22033             var p = range.commonAncestorContainer;
22034             while (p.nodeType == 3) { // text node
22035                 p = p.parentNode;
22036             }
22037             return p;
22038         } catch (e) {
22039             return null;
22040         }
22041     
22042     },
22043     /***
22044      *
22045      * Range intersection.. the hard stuff...
22046      *  '-1' = before
22047      *  '0' = hits..
22048      *  '1' = after.
22049      *         [ -- selected range --- ]
22050      *   [fail]                        [fail]
22051      *
22052      *    basically..
22053      *      if end is before start or  hits it. fail.
22054      *      if start is after end or hits it fail.
22055      *
22056      *   if either hits (but other is outside. - then it's not 
22057      *   
22058      *    
22059      **/
22060     
22061     
22062     // @see http://www.thismuchiknow.co.uk/?p=64.
22063     rangeIntersectsNode : function(range, node)
22064     {
22065         var nodeRange = node.ownerDocument.createRange();
22066         try {
22067             nodeRange.selectNode(node);
22068         } catch (e) {
22069             nodeRange.selectNodeContents(node);
22070         }
22071     
22072         var rangeStartRange = range.cloneRange();
22073         rangeStartRange.collapse(true);
22074     
22075         var rangeEndRange = range.cloneRange();
22076         rangeEndRange.collapse(false);
22077     
22078         var nodeStartRange = nodeRange.cloneRange();
22079         nodeStartRange.collapse(true);
22080     
22081         var nodeEndRange = nodeRange.cloneRange();
22082         nodeEndRange.collapse(false);
22083     
22084         return rangeStartRange.compareBoundaryPoints(
22085                  Range.START_TO_START, nodeEndRange) == -1 &&
22086                rangeEndRange.compareBoundaryPoints(
22087                  Range.START_TO_START, nodeStartRange) == 1;
22088         
22089          
22090     },
22091     rangeCompareNode : function(range, node)
22092     {
22093         var nodeRange = node.ownerDocument.createRange();
22094         try {
22095             nodeRange.selectNode(node);
22096         } catch (e) {
22097             nodeRange.selectNodeContents(node);
22098         }
22099         
22100         
22101         range.collapse(true);
22102     
22103         nodeRange.collapse(true);
22104      
22105         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22106         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22107          
22108         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22109         
22110         var nodeIsBefore   =  ss == 1;
22111         var nodeIsAfter    = ee == -1;
22112         
22113         if (nodeIsBefore && nodeIsAfter) {
22114             return 0; // outer
22115         }
22116         if (!nodeIsBefore && nodeIsAfter) {
22117             return 1; //right trailed.
22118         }
22119         
22120         if (nodeIsBefore && !nodeIsAfter) {
22121             return 2;  // left trailed.
22122         }
22123         // fully contined.
22124         return 3;
22125     },
22126
22127     // private? - in a new class?
22128     cleanUpPaste :  function()
22129     {
22130         // cleans up the whole document..
22131         Roo.log('cleanuppaste');
22132         
22133         this.cleanUpChildren(this.doc.body);
22134         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22135         if (clean != this.doc.body.innerHTML) {
22136             this.doc.body.innerHTML = clean;
22137         }
22138         
22139     },
22140     
22141     cleanWordChars : function(input) {// change the chars to hex code
22142         var he = Roo.HtmlEditorCore;
22143         
22144         var output = input;
22145         Roo.each(he.swapCodes, function(sw) { 
22146             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22147             
22148             output = output.replace(swapper, sw[1]);
22149         });
22150         
22151         return output;
22152     },
22153     
22154     
22155     cleanUpChildren : function (n)
22156     {
22157         if (!n.childNodes.length) {
22158             return;
22159         }
22160         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22161            this.cleanUpChild(n.childNodes[i]);
22162         }
22163     },
22164     
22165     
22166         
22167     
22168     cleanUpChild : function (node)
22169     {
22170         var ed = this;
22171         //console.log(node);
22172         if (node.nodeName == "#text") {
22173             // clean up silly Windows -- stuff?
22174             return; 
22175         }
22176         if (node.nodeName == "#comment") {
22177             node.parentNode.removeChild(node);
22178             // clean up silly Windows -- stuff?
22179             return; 
22180         }
22181         var lcname = node.tagName.toLowerCase();
22182         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22183         // whitelist of tags..
22184         
22185         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22186             // remove node.
22187             node.parentNode.removeChild(node);
22188             return;
22189             
22190         }
22191         
22192         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22193         
22194         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22195         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22196         
22197         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22198         //    remove_keep_children = true;
22199         //}
22200         
22201         if (remove_keep_children) {
22202             this.cleanUpChildren(node);
22203             // inserts everything just before this node...
22204             while (node.childNodes.length) {
22205                 var cn = node.childNodes[0];
22206                 node.removeChild(cn);
22207                 node.parentNode.insertBefore(cn, node);
22208             }
22209             node.parentNode.removeChild(node);
22210             return;
22211         }
22212         
22213         if (!node.attributes || !node.attributes.length) {
22214             this.cleanUpChildren(node);
22215             return;
22216         }
22217         
22218         function cleanAttr(n,v)
22219         {
22220             
22221             if (v.match(/^\./) || v.match(/^\//)) {
22222                 return;
22223             }
22224             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22225                 return;
22226             }
22227             if (v.match(/^#/)) {
22228                 return;
22229             }
22230 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22231             node.removeAttribute(n);
22232             
22233         }
22234         
22235         var cwhite = this.cwhite;
22236         var cblack = this.cblack;
22237             
22238         function cleanStyle(n,v)
22239         {
22240             if (v.match(/expression/)) { //XSS?? should we even bother..
22241                 node.removeAttribute(n);
22242                 return;
22243             }
22244             
22245             var parts = v.split(/;/);
22246             var clean = [];
22247             
22248             Roo.each(parts, function(p) {
22249                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22250                 if (!p.length) {
22251                     return true;
22252                 }
22253                 var l = p.split(':').shift().replace(/\s+/g,'');
22254                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22255                 
22256                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22257 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22258                     //node.removeAttribute(n);
22259                     return true;
22260                 }
22261                 //Roo.log()
22262                 // only allow 'c whitelisted system attributes'
22263                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22264 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22265                     //node.removeAttribute(n);
22266                     return true;
22267                 }
22268                 
22269                 
22270                  
22271                 
22272                 clean.push(p);
22273                 return true;
22274             });
22275             if (clean.length) { 
22276                 node.setAttribute(n, clean.join(';'));
22277             } else {
22278                 node.removeAttribute(n);
22279             }
22280             
22281         }
22282         
22283         
22284         for (var i = node.attributes.length-1; i > -1 ; i--) {
22285             var a = node.attributes[i];
22286             //console.log(a);
22287             
22288             if (a.name.toLowerCase().substr(0,2)=='on')  {
22289                 node.removeAttribute(a.name);
22290                 continue;
22291             }
22292             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22293                 node.removeAttribute(a.name);
22294                 continue;
22295             }
22296             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22297                 cleanAttr(a.name,a.value); // fixme..
22298                 continue;
22299             }
22300             if (a.name == 'style') {
22301                 cleanStyle(a.name,a.value);
22302                 continue;
22303             }
22304             /// clean up MS crap..
22305             // tecnically this should be a list of valid class'es..
22306             
22307             
22308             if (a.name == 'class') {
22309                 if (a.value.match(/^Mso/)) {
22310                     node.className = '';
22311                 }
22312                 
22313                 if (a.value.match(/^body$/)) {
22314                     node.className = '';
22315                 }
22316                 continue;
22317             }
22318             
22319             // style cleanup!?
22320             // class cleanup?
22321             
22322         }
22323         
22324         
22325         this.cleanUpChildren(node);
22326         
22327         
22328     },
22329     
22330     /**
22331      * Clean up MS wordisms...
22332      */
22333     cleanWord : function(node)
22334     {
22335         
22336         
22337         if (!node) {
22338             this.cleanWord(this.doc.body);
22339             return;
22340         }
22341         if (node.nodeName == "#text") {
22342             // clean up silly Windows -- stuff?
22343             return; 
22344         }
22345         if (node.nodeName == "#comment") {
22346             node.parentNode.removeChild(node);
22347             // clean up silly Windows -- stuff?
22348             return; 
22349         }
22350         
22351         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22352             node.parentNode.removeChild(node);
22353             return;
22354         }
22355         
22356         // remove - but keep children..
22357         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22358             while (node.childNodes.length) {
22359                 var cn = node.childNodes[0];
22360                 node.removeChild(cn);
22361                 node.parentNode.insertBefore(cn, node);
22362             }
22363             node.parentNode.removeChild(node);
22364             this.iterateChildren(node, this.cleanWord);
22365             return;
22366         }
22367         // clean styles
22368         if (node.className.length) {
22369             
22370             var cn = node.className.split(/\W+/);
22371             var cna = [];
22372             Roo.each(cn, function(cls) {
22373                 if (cls.match(/Mso[a-zA-Z]+/)) {
22374                     return;
22375                 }
22376                 cna.push(cls);
22377             });
22378             node.className = cna.length ? cna.join(' ') : '';
22379             if (!cna.length) {
22380                 node.removeAttribute("class");
22381             }
22382         }
22383         
22384         if (node.hasAttribute("lang")) {
22385             node.removeAttribute("lang");
22386         }
22387         
22388         if (node.hasAttribute("style")) {
22389             
22390             var styles = node.getAttribute("style").split(";");
22391             var nstyle = [];
22392             Roo.each(styles, function(s) {
22393                 if (!s.match(/:/)) {
22394                     return;
22395                 }
22396                 var kv = s.split(":");
22397                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22398                     return;
22399                 }
22400                 // what ever is left... we allow.
22401                 nstyle.push(s);
22402             });
22403             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22404             if (!nstyle.length) {
22405                 node.removeAttribute('style');
22406             }
22407         }
22408         this.iterateChildren(node, this.cleanWord);
22409         
22410         
22411         
22412     },
22413     /**
22414      * iterateChildren of a Node, calling fn each time, using this as the scole..
22415      * @param {DomNode} node node to iterate children of.
22416      * @param {Function} fn method of this class to call on each item.
22417      */
22418     iterateChildren : function(node, fn)
22419     {
22420         if (!node.childNodes.length) {
22421                 return;
22422         }
22423         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22424            fn.call(this, node.childNodes[i])
22425         }
22426     },
22427     
22428     
22429     /**
22430      * cleanTableWidths.
22431      *
22432      * Quite often pasting from word etc.. results in tables with column and widths.
22433      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22434      *
22435      */
22436     cleanTableWidths : function(node)
22437     {
22438          
22439          
22440         if (!node) {
22441             this.cleanTableWidths(this.doc.body);
22442             return;
22443         }
22444         
22445         // ignore list...
22446         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22447             return; 
22448         }
22449         Roo.log(node.tagName);
22450         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22451             this.iterateChildren(node, this.cleanTableWidths);
22452             return;
22453         }
22454         if (node.hasAttribute('width')) {
22455             node.removeAttribute('width');
22456         }
22457         
22458          
22459         if (node.hasAttribute("style")) {
22460             // pretty basic...
22461             
22462             var styles = node.getAttribute("style").split(";");
22463             var nstyle = [];
22464             Roo.each(styles, function(s) {
22465                 if (!s.match(/:/)) {
22466                     return;
22467                 }
22468                 var kv = s.split(":");
22469                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22470                     return;
22471                 }
22472                 // what ever is left... we allow.
22473                 nstyle.push(s);
22474             });
22475             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22476             if (!nstyle.length) {
22477                 node.removeAttribute('style');
22478             }
22479         }
22480         
22481         this.iterateChildren(node, this.cleanTableWidths);
22482         
22483         
22484     },
22485     
22486     
22487     
22488     
22489     domToHTML : function(currentElement, depth, nopadtext) {
22490         
22491         depth = depth || 0;
22492         nopadtext = nopadtext || false;
22493     
22494         if (!currentElement) {
22495             return this.domToHTML(this.doc.body);
22496         }
22497         
22498         //Roo.log(currentElement);
22499         var j;
22500         var allText = false;
22501         var nodeName = currentElement.nodeName;
22502         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22503         
22504         if  (nodeName == '#text') {
22505             
22506             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22507         }
22508         
22509         
22510         var ret = '';
22511         if (nodeName != 'BODY') {
22512              
22513             var i = 0;
22514             // Prints the node tagName, such as <A>, <IMG>, etc
22515             if (tagName) {
22516                 var attr = [];
22517                 for(i = 0; i < currentElement.attributes.length;i++) {
22518                     // quoting?
22519                     var aname = currentElement.attributes.item(i).name;
22520                     if (!currentElement.attributes.item(i).value.length) {
22521                         continue;
22522                     }
22523                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22524                 }
22525                 
22526                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22527             } 
22528             else {
22529                 
22530                 // eack
22531             }
22532         } else {
22533             tagName = false;
22534         }
22535         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22536             return ret;
22537         }
22538         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22539             nopadtext = true;
22540         }
22541         
22542         
22543         // Traverse the tree
22544         i = 0;
22545         var currentElementChild = currentElement.childNodes.item(i);
22546         var allText = true;
22547         var innerHTML  = '';
22548         lastnode = '';
22549         while (currentElementChild) {
22550             // Formatting code (indent the tree so it looks nice on the screen)
22551             var nopad = nopadtext;
22552             if (lastnode == 'SPAN') {
22553                 nopad  = true;
22554             }
22555             // text
22556             if  (currentElementChild.nodeName == '#text') {
22557                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22558                 toadd = nopadtext ? toadd : toadd.trim();
22559                 if (!nopad && toadd.length > 80) {
22560                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22561                 }
22562                 innerHTML  += toadd;
22563                 
22564                 i++;
22565                 currentElementChild = currentElement.childNodes.item(i);
22566                 lastNode = '';
22567                 continue;
22568             }
22569             allText = false;
22570             
22571             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22572                 
22573             // Recursively traverse the tree structure of the child node
22574             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22575             lastnode = currentElementChild.nodeName;
22576             i++;
22577             currentElementChild=currentElement.childNodes.item(i);
22578         }
22579         
22580         ret += innerHTML;
22581         
22582         if (!allText) {
22583                 // The remaining code is mostly for formatting the tree
22584             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22585         }
22586         
22587         
22588         if (tagName) {
22589             ret+= "</"+tagName+">";
22590         }
22591         return ret;
22592         
22593     },
22594         
22595     applyBlacklists : function()
22596     {
22597         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22598         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22599         
22600         this.white = [];
22601         this.black = [];
22602         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22603             if (b.indexOf(tag) > -1) {
22604                 return;
22605             }
22606             this.white.push(tag);
22607             
22608         }, this);
22609         
22610         Roo.each(w, function(tag) {
22611             if (b.indexOf(tag) > -1) {
22612                 return;
22613             }
22614             if (this.white.indexOf(tag) > -1) {
22615                 return;
22616             }
22617             this.white.push(tag);
22618             
22619         }, this);
22620         
22621         
22622         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22623             if (w.indexOf(tag) > -1) {
22624                 return;
22625             }
22626             this.black.push(tag);
22627             
22628         }, this);
22629         
22630         Roo.each(b, function(tag) {
22631             if (w.indexOf(tag) > -1) {
22632                 return;
22633             }
22634             if (this.black.indexOf(tag) > -1) {
22635                 return;
22636             }
22637             this.black.push(tag);
22638             
22639         }, this);
22640         
22641         
22642         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22643         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22644         
22645         this.cwhite = [];
22646         this.cblack = [];
22647         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22648             if (b.indexOf(tag) > -1) {
22649                 return;
22650             }
22651             this.cwhite.push(tag);
22652             
22653         }, this);
22654         
22655         Roo.each(w, function(tag) {
22656             if (b.indexOf(tag) > -1) {
22657                 return;
22658             }
22659             if (this.cwhite.indexOf(tag) > -1) {
22660                 return;
22661             }
22662             this.cwhite.push(tag);
22663             
22664         }, this);
22665         
22666         
22667         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22668             if (w.indexOf(tag) > -1) {
22669                 return;
22670             }
22671             this.cblack.push(tag);
22672             
22673         }, this);
22674         
22675         Roo.each(b, function(tag) {
22676             if (w.indexOf(tag) > -1) {
22677                 return;
22678             }
22679             if (this.cblack.indexOf(tag) > -1) {
22680                 return;
22681             }
22682             this.cblack.push(tag);
22683             
22684         }, this);
22685     },
22686     
22687     setStylesheets : function(stylesheets)
22688     {
22689         if(typeof(stylesheets) == 'string'){
22690             Roo.get(this.iframe.contentDocument.head).createChild({
22691                 tag : 'link',
22692                 rel : 'stylesheet',
22693                 type : 'text/css',
22694                 href : stylesheets
22695             });
22696             
22697             return;
22698         }
22699         var _this = this;
22700      
22701         Roo.each(stylesheets, function(s) {
22702             if(!s.length){
22703                 return;
22704             }
22705             
22706             Roo.get(_this.iframe.contentDocument.head).createChild({
22707                 tag : 'link',
22708                 rel : 'stylesheet',
22709                 type : 'text/css',
22710                 href : s
22711             });
22712         });
22713
22714         
22715     },
22716     
22717     removeStylesheets : function()
22718     {
22719         var _this = this;
22720         
22721         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22722             s.remove();
22723         });
22724     },
22725     
22726     setStyle : function(style)
22727     {
22728         Roo.get(this.iframe.contentDocument.head).createChild({
22729             tag : 'style',
22730             type : 'text/css',
22731             html : style
22732         });
22733
22734         return;
22735     }
22736     
22737     // hide stuff that is not compatible
22738     /**
22739      * @event blur
22740      * @hide
22741      */
22742     /**
22743      * @event change
22744      * @hide
22745      */
22746     /**
22747      * @event focus
22748      * @hide
22749      */
22750     /**
22751      * @event specialkey
22752      * @hide
22753      */
22754     /**
22755      * @cfg {String} fieldClass @hide
22756      */
22757     /**
22758      * @cfg {String} focusClass @hide
22759      */
22760     /**
22761      * @cfg {String} autoCreate @hide
22762      */
22763     /**
22764      * @cfg {String} inputType @hide
22765      */
22766     /**
22767      * @cfg {String} invalidClass @hide
22768      */
22769     /**
22770      * @cfg {String} invalidText @hide
22771      */
22772     /**
22773      * @cfg {String} msgFx @hide
22774      */
22775     /**
22776      * @cfg {String} validateOnBlur @hide
22777      */
22778 });
22779
22780 Roo.HtmlEditorCore.white = [
22781         'area', 'br', 'img', 'input', 'hr', 'wbr',
22782         
22783        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22784        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22785        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22786        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22787        'table',   'ul',         'xmp', 
22788        
22789        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22790       'thead',   'tr', 
22791      
22792       'dir', 'menu', 'ol', 'ul', 'dl',
22793        
22794       'embed',  'object'
22795 ];
22796
22797
22798 Roo.HtmlEditorCore.black = [
22799     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22800         'applet', // 
22801         'base',   'basefont', 'bgsound', 'blink',  'body', 
22802         'frame',  'frameset', 'head',    'html',   'ilayer', 
22803         'iframe', 'layer',  'link',     'meta',    'object',   
22804         'script', 'style' ,'title',  'xml' // clean later..
22805 ];
22806 Roo.HtmlEditorCore.clean = [
22807     'script', 'style', 'title', 'xml'
22808 ];
22809 Roo.HtmlEditorCore.remove = [
22810     'font'
22811 ];
22812 // attributes..
22813
22814 Roo.HtmlEditorCore.ablack = [
22815     'on'
22816 ];
22817     
22818 Roo.HtmlEditorCore.aclean = [ 
22819     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22820 ];
22821
22822 // protocols..
22823 Roo.HtmlEditorCore.pwhite= [
22824         'http',  'https',  'mailto'
22825 ];
22826
22827 // white listed style attributes.
22828 Roo.HtmlEditorCore.cwhite= [
22829       //  'text-align', /// default is to allow most things..
22830       
22831          
22832 //        'font-size'//??
22833 ];
22834
22835 // black listed style attributes.
22836 Roo.HtmlEditorCore.cblack= [
22837       //  'font-size' -- this can be set by the project 
22838 ];
22839
22840
22841 Roo.HtmlEditorCore.swapCodes   =[ 
22842     [    8211, "--" ], 
22843     [    8212, "--" ], 
22844     [    8216,  "'" ],  
22845     [    8217, "'" ],  
22846     [    8220, '"' ],  
22847     [    8221, '"' ],  
22848     [    8226, "*" ],  
22849     [    8230, "..." ]
22850 ]; 
22851
22852     /*
22853  * - LGPL
22854  *
22855  * HtmlEditor
22856  * 
22857  */
22858
22859 /**
22860  * @class Roo.bootstrap.HtmlEditor
22861  * @extends Roo.bootstrap.TextArea
22862  * Bootstrap HtmlEditor class
22863
22864  * @constructor
22865  * Create a new HtmlEditor
22866  * @param {Object} config The config object
22867  */
22868
22869 Roo.bootstrap.HtmlEditor = function(config){
22870     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22871     if (!this.toolbars) {
22872         this.toolbars = [];
22873     }
22874     
22875     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22876     this.addEvents({
22877             /**
22878              * @event initialize
22879              * Fires when the editor is fully initialized (including the iframe)
22880              * @param {HtmlEditor} this
22881              */
22882             initialize: true,
22883             /**
22884              * @event activate
22885              * Fires when the editor is first receives the focus. Any insertion must wait
22886              * until after this event.
22887              * @param {HtmlEditor} this
22888              */
22889             activate: true,
22890              /**
22891              * @event beforesync
22892              * Fires before the textarea is updated with content from the editor iframe. Return false
22893              * to cancel the sync.
22894              * @param {HtmlEditor} this
22895              * @param {String} html
22896              */
22897             beforesync: true,
22898              /**
22899              * @event beforepush
22900              * Fires before the iframe editor is updated with content from the textarea. Return false
22901              * to cancel the push.
22902              * @param {HtmlEditor} this
22903              * @param {String} html
22904              */
22905             beforepush: true,
22906              /**
22907              * @event sync
22908              * Fires when the textarea is updated with content from the editor iframe.
22909              * @param {HtmlEditor} this
22910              * @param {String} html
22911              */
22912             sync: true,
22913              /**
22914              * @event push
22915              * Fires when the iframe editor is updated with content from the textarea.
22916              * @param {HtmlEditor} this
22917              * @param {String} html
22918              */
22919             push: true,
22920              /**
22921              * @event editmodechange
22922              * Fires when the editor switches edit modes
22923              * @param {HtmlEditor} this
22924              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22925              */
22926             editmodechange: true,
22927             /**
22928              * @event editorevent
22929              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22930              * @param {HtmlEditor} this
22931              */
22932             editorevent: true,
22933             /**
22934              * @event firstfocus
22935              * Fires when on first focus - needed by toolbars..
22936              * @param {HtmlEditor} this
22937              */
22938             firstfocus: true,
22939             /**
22940              * @event autosave
22941              * Auto save the htmlEditor value as a file into Events
22942              * @param {HtmlEditor} this
22943              */
22944             autosave: true,
22945             /**
22946              * @event savedpreview
22947              * preview the saved version of htmlEditor
22948              * @param {HtmlEditor} this
22949              */
22950             savedpreview: true
22951         });
22952 };
22953
22954
22955 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22956     
22957     
22958       /**
22959      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22960      */
22961     toolbars : false,
22962     
22963      /**
22964     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22965     */
22966     btns : [],
22967    
22968      /**
22969      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22970      *                        Roo.resizable.
22971      */
22972     resizable : false,
22973      /**
22974      * @cfg {Number} height (in pixels)
22975      */   
22976     height: 300,
22977    /**
22978      * @cfg {Number} width (in pixels)
22979      */   
22980     width: false,
22981     
22982     /**
22983      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22984      * 
22985      */
22986     stylesheets: false,
22987     
22988     // id of frame..
22989     frameId: false,
22990     
22991     // private properties
22992     validationEvent : false,
22993     deferHeight: true,
22994     initialized : false,
22995     activated : false,
22996     
22997     onFocus : Roo.emptyFn,
22998     iframePad:3,
22999     hideMode:'offsets',
23000     
23001     tbContainer : false,
23002     
23003     bodyCls : '',
23004     
23005     toolbarContainer :function() {
23006         return this.wrap.select('.x-html-editor-tb',true).first();
23007     },
23008
23009     /**
23010      * Protected method that will not generally be called directly. It
23011      * is called when the editor creates its toolbar. Override this method if you need to
23012      * add custom toolbar buttons.
23013      * @param {HtmlEditor} editor
23014      */
23015     createToolbar : function(){
23016         Roo.log('renewing');
23017         Roo.log("create toolbars");
23018         
23019         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23020         this.toolbars[0].render(this.toolbarContainer());
23021         
23022         return;
23023         
23024 //        if (!editor.toolbars || !editor.toolbars.length) {
23025 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23026 //        }
23027 //        
23028 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23029 //            editor.toolbars[i] = Roo.factory(
23030 //                    typeof(editor.toolbars[i]) == 'string' ?
23031 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23032 //                Roo.bootstrap.HtmlEditor);
23033 //            editor.toolbars[i].init(editor);
23034 //        }
23035     },
23036
23037      
23038     // private
23039     onRender : function(ct, position)
23040     {
23041        // Roo.log("Call onRender: " + this.xtype);
23042         var _t = this;
23043         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23044       
23045         this.wrap = this.inputEl().wrap({
23046             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23047         });
23048         
23049         this.editorcore.onRender(ct, position);
23050          
23051         if (this.resizable) {
23052             this.resizeEl = new Roo.Resizable(this.wrap, {
23053                 pinned : true,
23054                 wrap: true,
23055                 dynamic : true,
23056                 minHeight : this.height,
23057                 height: this.height,
23058                 handles : this.resizable,
23059                 width: this.width,
23060                 listeners : {
23061                     resize : function(r, w, h) {
23062                         _t.onResize(w,h); // -something
23063                     }
23064                 }
23065             });
23066             
23067         }
23068         this.createToolbar(this);
23069        
23070         
23071         if(!this.width && this.resizable){
23072             this.setSize(this.wrap.getSize());
23073         }
23074         if (this.resizeEl) {
23075             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23076             // should trigger onReize..
23077         }
23078         
23079     },
23080
23081     // private
23082     onResize : function(w, h)
23083     {
23084         Roo.log('resize: ' +w + ',' + h );
23085         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23086         var ew = false;
23087         var eh = false;
23088         
23089         if(this.inputEl() ){
23090             if(typeof w == 'number'){
23091                 var aw = w - this.wrap.getFrameWidth('lr');
23092                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23093                 ew = aw;
23094             }
23095             if(typeof h == 'number'){
23096                  var tbh = -11;  // fixme it needs to tool bar size!
23097                 for (var i =0; i < this.toolbars.length;i++) {
23098                     // fixme - ask toolbars for heights?
23099                     tbh += this.toolbars[i].el.getHeight();
23100                     //if (this.toolbars[i].footer) {
23101                     //    tbh += this.toolbars[i].footer.el.getHeight();
23102                     //}
23103                 }
23104               
23105                 
23106                 
23107                 
23108                 
23109                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23110                 ah -= 5; // knock a few pixes off for look..
23111                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23112                 var eh = ah;
23113             }
23114         }
23115         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23116         this.editorcore.onResize(ew,eh);
23117         
23118     },
23119
23120     /**
23121      * Toggles the editor between standard and source edit mode.
23122      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23123      */
23124     toggleSourceEdit : function(sourceEditMode)
23125     {
23126         this.editorcore.toggleSourceEdit(sourceEditMode);
23127         
23128         if(this.editorcore.sourceEditMode){
23129             Roo.log('editor - showing textarea');
23130             
23131 //            Roo.log('in');
23132 //            Roo.log(this.syncValue());
23133             this.syncValue();
23134             this.inputEl().removeClass(['hide', 'x-hidden']);
23135             this.inputEl().dom.removeAttribute('tabIndex');
23136             this.inputEl().focus();
23137         }else{
23138             Roo.log('editor - hiding textarea');
23139 //            Roo.log('out')
23140 //            Roo.log(this.pushValue()); 
23141             this.pushValue();
23142             
23143             this.inputEl().addClass(['hide', 'x-hidden']);
23144             this.inputEl().dom.setAttribute('tabIndex', -1);
23145             //this.deferFocus();
23146         }
23147          
23148         if(this.resizable){
23149             this.setSize(this.wrap.getSize());
23150         }
23151         
23152         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23153     },
23154  
23155     // private (for BoxComponent)
23156     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23157
23158     // private (for BoxComponent)
23159     getResizeEl : function(){
23160         return this.wrap;
23161     },
23162
23163     // private (for BoxComponent)
23164     getPositionEl : function(){
23165         return this.wrap;
23166     },
23167
23168     // private
23169     initEvents : function(){
23170         this.originalValue = this.getValue();
23171     },
23172
23173 //    /**
23174 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23175 //     * @method
23176 //     */
23177 //    markInvalid : Roo.emptyFn,
23178 //    /**
23179 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23180 //     * @method
23181 //     */
23182 //    clearInvalid : Roo.emptyFn,
23183
23184     setValue : function(v){
23185         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23186         this.editorcore.pushValue();
23187     },
23188
23189      
23190     // private
23191     deferFocus : function(){
23192         this.focus.defer(10, this);
23193     },
23194
23195     // doc'ed in Field
23196     focus : function(){
23197         this.editorcore.focus();
23198         
23199     },
23200       
23201
23202     // private
23203     onDestroy : function(){
23204         
23205         
23206         
23207         if(this.rendered){
23208             
23209             for (var i =0; i < this.toolbars.length;i++) {
23210                 // fixme - ask toolbars for heights?
23211                 this.toolbars[i].onDestroy();
23212             }
23213             
23214             this.wrap.dom.innerHTML = '';
23215             this.wrap.remove();
23216         }
23217     },
23218
23219     // private
23220     onFirstFocus : function(){
23221         //Roo.log("onFirstFocus");
23222         this.editorcore.onFirstFocus();
23223          for (var i =0; i < this.toolbars.length;i++) {
23224             this.toolbars[i].onFirstFocus();
23225         }
23226         
23227     },
23228     
23229     // private
23230     syncValue : function()
23231     {   
23232         this.editorcore.syncValue();
23233     },
23234     
23235     pushValue : function()
23236     {   
23237         this.editorcore.pushValue();
23238     }
23239      
23240     
23241     // hide stuff that is not compatible
23242     /**
23243      * @event blur
23244      * @hide
23245      */
23246     /**
23247      * @event change
23248      * @hide
23249      */
23250     /**
23251      * @event focus
23252      * @hide
23253      */
23254     /**
23255      * @event specialkey
23256      * @hide
23257      */
23258     /**
23259      * @cfg {String} fieldClass @hide
23260      */
23261     /**
23262      * @cfg {String} focusClass @hide
23263      */
23264     /**
23265      * @cfg {String} autoCreate @hide
23266      */
23267     /**
23268      * @cfg {String} inputType @hide
23269      */
23270     /**
23271      * @cfg {String} invalidClass @hide
23272      */
23273     /**
23274      * @cfg {String} invalidText @hide
23275      */
23276     /**
23277      * @cfg {String} msgFx @hide
23278      */
23279     /**
23280      * @cfg {String} validateOnBlur @hide
23281      */
23282 });
23283  
23284     
23285    
23286    
23287    
23288       
23289 Roo.namespace('Roo.bootstrap.htmleditor');
23290 /**
23291  * @class Roo.bootstrap.HtmlEditorToolbar1
23292  * Basic Toolbar
23293  * 
23294  * Usage:
23295  *
23296  new Roo.bootstrap.HtmlEditor({
23297     ....
23298     toolbars : [
23299         new Roo.bootstrap.HtmlEditorToolbar1({
23300             disable : { fonts: 1 , format: 1, ..., ... , ...],
23301             btns : [ .... ]
23302         })
23303     }
23304      
23305  * 
23306  * @cfg {Object} disable List of elements to disable..
23307  * @cfg {Array} btns List of additional buttons.
23308  * 
23309  * 
23310  * NEEDS Extra CSS? 
23311  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23312  */
23313  
23314 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23315 {
23316     
23317     Roo.apply(this, config);
23318     
23319     // default disabled, based on 'good practice'..
23320     this.disable = this.disable || {};
23321     Roo.applyIf(this.disable, {
23322         fontSize : true,
23323         colors : true,
23324         specialElements : true
23325     });
23326     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23327     
23328     this.editor = config.editor;
23329     this.editorcore = config.editor.editorcore;
23330     
23331     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23332     
23333     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23334     // dont call parent... till later.
23335 }
23336 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23337      
23338     bar : true,
23339     
23340     editor : false,
23341     editorcore : false,
23342     
23343     
23344     formats : [
23345         "p" ,  
23346         "h1","h2","h3","h4","h5","h6", 
23347         "pre", "code", 
23348         "abbr", "acronym", "address", "cite", "samp", "var",
23349         'div','span'
23350     ],
23351     
23352     onRender : function(ct, position)
23353     {
23354        // Roo.log("Call onRender: " + this.xtype);
23355         
23356        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23357        Roo.log(this.el);
23358        this.el.dom.style.marginBottom = '0';
23359        var _this = this;
23360        var editorcore = this.editorcore;
23361        var editor= this.editor;
23362        
23363        var children = [];
23364        var btn = function(id,cmd , toggle, handler, html){
23365        
23366             var  event = toggle ? 'toggle' : 'click';
23367        
23368             var a = {
23369                 size : 'sm',
23370                 xtype: 'Button',
23371                 xns: Roo.bootstrap,
23372                 glyphicon : id,
23373                 cmd : id || cmd,
23374                 enableToggle:toggle !== false,
23375                 html : html || '',
23376                 pressed : toggle ? false : null,
23377                 listeners : {}
23378             };
23379             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23380                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23381             };
23382             children.push(a);
23383             return a;
23384        }
23385        
23386     //    var cb_box = function...
23387         
23388         var style = {
23389                 xtype: 'Button',
23390                 size : 'sm',
23391                 xns: Roo.bootstrap,
23392                 glyphicon : 'font',
23393                 //html : 'submit'
23394                 menu : {
23395                     xtype: 'Menu',
23396                     xns: Roo.bootstrap,
23397                     items:  []
23398                 }
23399         };
23400         Roo.each(this.formats, function(f) {
23401             style.menu.items.push({
23402                 xtype :'MenuItem',
23403                 xns: Roo.bootstrap,
23404                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23405                 tagname : f,
23406                 listeners : {
23407                     click : function()
23408                     {
23409                         editorcore.insertTag(this.tagname);
23410                         editor.focus();
23411                     }
23412                 }
23413                 
23414             });
23415         });
23416         children.push(style);   
23417         
23418         btn('bold',false,true);
23419         btn('italic',false,true);
23420         btn('align-left', 'justifyleft',true);
23421         btn('align-center', 'justifycenter',true);
23422         btn('align-right' , 'justifyright',true);
23423         btn('link', false, false, function(btn) {
23424             //Roo.log("create link?");
23425             var url = prompt(this.createLinkText, this.defaultLinkValue);
23426             if(url && url != 'http:/'+'/'){
23427                 this.editorcore.relayCmd('createlink', url);
23428             }
23429         }),
23430         btn('list','insertunorderedlist',true);
23431         btn('pencil', false,true, function(btn){
23432                 Roo.log(this);
23433                 this.toggleSourceEdit(btn.pressed);
23434         });
23435         
23436         if (this.editor.btns.length > 0) {
23437             for (var i = 0; i<this.editor.btns.length; i++) {
23438                 children.push(this.editor.btns[i]);
23439             }
23440         }
23441         
23442         /*
23443         var cog = {
23444                 xtype: 'Button',
23445                 size : 'sm',
23446                 xns: Roo.bootstrap,
23447                 glyphicon : 'cog',
23448                 //html : 'submit'
23449                 menu : {
23450                     xtype: 'Menu',
23451                     xns: Roo.bootstrap,
23452                     items:  []
23453                 }
23454         };
23455         
23456         cog.menu.items.push({
23457             xtype :'MenuItem',
23458             xns: Roo.bootstrap,
23459             html : Clean styles,
23460             tagname : f,
23461             listeners : {
23462                 click : function()
23463                 {
23464                     editorcore.insertTag(this.tagname);
23465                     editor.focus();
23466                 }
23467             }
23468             
23469         });
23470        */
23471         
23472          
23473        this.xtype = 'NavSimplebar';
23474         
23475         for(var i=0;i< children.length;i++) {
23476             
23477             this.buttons.add(this.addxtypeChild(children[i]));
23478             
23479         }
23480         
23481         editor.on('editorevent', this.updateToolbar, this);
23482     },
23483     onBtnClick : function(id)
23484     {
23485        this.editorcore.relayCmd(id);
23486        this.editorcore.focus();
23487     },
23488     
23489     /**
23490      * Protected method that will not generally be called directly. It triggers
23491      * a toolbar update by reading the markup state of the current selection in the editor.
23492      */
23493     updateToolbar: function(){
23494
23495         if(!this.editorcore.activated){
23496             this.editor.onFirstFocus(); // is this neeed?
23497             return;
23498         }
23499
23500         var btns = this.buttons; 
23501         var doc = this.editorcore.doc;
23502         btns.get('bold').setActive(doc.queryCommandState('bold'));
23503         btns.get('italic').setActive(doc.queryCommandState('italic'));
23504         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23505         
23506         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23507         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23508         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23509         
23510         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23511         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23512          /*
23513         
23514         var ans = this.editorcore.getAllAncestors();
23515         if (this.formatCombo) {
23516             
23517             
23518             var store = this.formatCombo.store;
23519             this.formatCombo.setValue("");
23520             for (var i =0; i < ans.length;i++) {
23521                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23522                     // select it..
23523                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23524                     break;
23525                 }
23526             }
23527         }
23528         
23529         
23530         
23531         // hides menus... - so this cant be on a menu...
23532         Roo.bootstrap.MenuMgr.hideAll();
23533         */
23534         Roo.bootstrap.MenuMgr.hideAll();
23535         //this.editorsyncValue();
23536     },
23537     onFirstFocus: function() {
23538         this.buttons.each(function(item){
23539            item.enable();
23540         });
23541     },
23542     toggleSourceEdit : function(sourceEditMode){
23543         
23544           
23545         if(sourceEditMode){
23546             Roo.log("disabling buttons");
23547            this.buttons.each( function(item){
23548                 if(item.cmd != 'pencil'){
23549                     item.disable();
23550                 }
23551             });
23552           
23553         }else{
23554             Roo.log("enabling buttons");
23555             if(this.editorcore.initialized){
23556                 this.buttons.each( function(item){
23557                     item.enable();
23558                 });
23559             }
23560             
23561         }
23562         Roo.log("calling toggole on editor");
23563         // tell the editor that it's been pressed..
23564         this.editor.toggleSourceEdit(sourceEditMode);
23565        
23566     }
23567 });
23568
23569
23570
23571
23572
23573 /**
23574  * @class Roo.bootstrap.Table.AbstractSelectionModel
23575  * @extends Roo.util.Observable
23576  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23577  * implemented by descendant classes.  This class should not be directly instantiated.
23578  * @constructor
23579  */
23580 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23581     this.locked = false;
23582     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23583 };
23584
23585
23586 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23587     /** @ignore Called by the grid automatically. Do not call directly. */
23588     init : function(grid){
23589         this.grid = grid;
23590         this.initEvents();
23591     },
23592
23593     /**
23594      * Locks the selections.
23595      */
23596     lock : function(){
23597         this.locked = true;
23598     },
23599
23600     /**
23601      * Unlocks the selections.
23602      */
23603     unlock : function(){
23604         this.locked = false;
23605     },
23606
23607     /**
23608      * Returns true if the selections are locked.
23609      * @return {Boolean}
23610      */
23611     isLocked : function(){
23612         return this.locked;
23613     }
23614 });
23615 /**
23616  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23617  * @class Roo.bootstrap.Table.RowSelectionModel
23618  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23619  * It supports multiple selections and keyboard selection/navigation. 
23620  * @constructor
23621  * @param {Object} config
23622  */
23623
23624 Roo.bootstrap.Table.RowSelectionModel = function(config){
23625     Roo.apply(this, config);
23626     this.selections = new Roo.util.MixedCollection(false, function(o){
23627         return o.id;
23628     });
23629
23630     this.last = false;
23631     this.lastActive = false;
23632
23633     this.addEvents({
23634         /**
23635              * @event selectionchange
23636              * Fires when the selection changes
23637              * @param {SelectionModel} this
23638              */
23639             "selectionchange" : true,
23640         /**
23641              * @event afterselectionchange
23642              * Fires after the selection changes (eg. by key press or clicking)
23643              * @param {SelectionModel} this
23644              */
23645             "afterselectionchange" : true,
23646         /**
23647              * @event beforerowselect
23648              * Fires when a row is selected being selected, return false to cancel.
23649              * @param {SelectionModel} this
23650              * @param {Number} rowIndex The selected index
23651              * @param {Boolean} keepExisting False if other selections will be cleared
23652              */
23653             "beforerowselect" : true,
23654         /**
23655              * @event rowselect
23656              * Fires when a row is selected.
23657              * @param {SelectionModel} this
23658              * @param {Number} rowIndex The selected index
23659              * @param {Roo.data.Record} r The record
23660              */
23661             "rowselect" : true,
23662         /**
23663              * @event rowdeselect
23664              * Fires when a row is deselected.
23665              * @param {SelectionModel} this
23666              * @param {Number} rowIndex The selected index
23667              */
23668         "rowdeselect" : true
23669     });
23670     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23671     this.locked = false;
23672  };
23673
23674 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23675     /**
23676      * @cfg {Boolean} singleSelect
23677      * True to allow selection of only one row at a time (defaults to false)
23678      */
23679     singleSelect : false,
23680
23681     // private
23682     initEvents : function()
23683     {
23684
23685         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23686         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23687         //}else{ // allow click to work like normal
23688          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23689         //}
23690         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23691         this.grid.on("rowclick", this.handleMouseDown, this);
23692         
23693         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23694             "up" : function(e){
23695                 if(!e.shiftKey){
23696                     this.selectPrevious(e.shiftKey);
23697                 }else if(this.last !== false && this.lastActive !== false){
23698                     var last = this.last;
23699                     this.selectRange(this.last,  this.lastActive-1);
23700                     this.grid.getView().focusRow(this.lastActive);
23701                     if(last !== false){
23702                         this.last = last;
23703                     }
23704                 }else{
23705                     this.selectFirstRow();
23706                 }
23707                 this.fireEvent("afterselectionchange", this);
23708             },
23709             "down" : function(e){
23710                 if(!e.shiftKey){
23711                     this.selectNext(e.shiftKey);
23712                 }else if(this.last !== false && this.lastActive !== false){
23713                     var last = this.last;
23714                     this.selectRange(this.last,  this.lastActive+1);
23715                     this.grid.getView().focusRow(this.lastActive);
23716                     if(last !== false){
23717                         this.last = last;
23718                     }
23719                 }else{
23720                     this.selectFirstRow();
23721                 }
23722                 this.fireEvent("afterselectionchange", this);
23723             },
23724             scope: this
23725         });
23726         this.grid.store.on('load', function(){
23727             this.selections.clear();
23728         },this);
23729         /*
23730         var view = this.grid.view;
23731         view.on("refresh", this.onRefresh, this);
23732         view.on("rowupdated", this.onRowUpdated, this);
23733         view.on("rowremoved", this.onRemove, this);
23734         */
23735     },
23736
23737     // private
23738     onRefresh : function()
23739     {
23740         var ds = this.grid.store, i, v = this.grid.view;
23741         var s = this.selections;
23742         s.each(function(r){
23743             if((i = ds.indexOfId(r.id)) != -1){
23744                 v.onRowSelect(i);
23745             }else{
23746                 s.remove(r);
23747             }
23748         });
23749     },
23750
23751     // private
23752     onRemove : function(v, index, r){
23753         this.selections.remove(r);
23754     },
23755
23756     // private
23757     onRowUpdated : function(v, index, r){
23758         if(this.isSelected(r)){
23759             v.onRowSelect(index);
23760         }
23761     },
23762
23763     /**
23764      * Select records.
23765      * @param {Array} records The records to select
23766      * @param {Boolean} keepExisting (optional) True to keep existing selections
23767      */
23768     selectRecords : function(records, keepExisting)
23769     {
23770         if(!keepExisting){
23771             this.clearSelections();
23772         }
23773             var ds = this.grid.store;
23774         for(var i = 0, len = records.length; i < len; i++){
23775             this.selectRow(ds.indexOf(records[i]), true);
23776         }
23777     },
23778
23779     /**
23780      * Gets the number of selected rows.
23781      * @return {Number}
23782      */
23783     getCount : function(){
23784         return this.selections.length;
23785     },
23786
23787     /**
23788      * Selects the first row in the grid.
23789      */
23790     selectFirstRow : function(){
23791         this.selectRow(0);
23792     },
23793
23794     /**
23795      * Select the last row.
23796      * @param {Boolean} keepExisting (optional) True to keep existing selections
23797      */
23798     selectLastRow : function(keepExisting){
23799         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23800         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23801     },
23802
23803     /**
23804      * Selects the row immediately following the last selected row.
23805      * @param {Boolean} keepExisting (optional) True to keep existing selections
23806      */
23807     selectNext : function(keepExisting)
23808     {
23809             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23810             this.selectRow(this.last+1, keepExisting);
23811             this.grid.getView().focusRow(this.last);
23812         }
23813     },
23814
23815     /**
23816      * Selects the row that precedes the last selected row.
23817      * @param {Boolean} keepExisting (optional) True to keep existing selections
23818      */
23819     selectPrevious : function(keepExisting){
23820         if(this.last){
23821             this.selectRow(this.last-1, keepExisting);
23822             this.grid.getView().focusRow(this.last);
23823         }
23824     },
23825
23826     /**
23827      * Returns the selected records
23828      * @return {Array} Array of selected records
23829      */
23830     getSelections : function(){
23831         return [].concat(this.selections.items);
23832     },
23833
23834     /**
23835      * Returns the first selected record.
23836      * @return {Record}
23837      */
23838     getSelected : function(){
23839         return this.selections.itemAt(0);
23840     },
23841
23842
23843     /**
23844      * Clears all selections.
23845      */
23846     clearSelections : function(fast)
23847     {
23848         if(this.locked) {
23849             return;
23850         }
23851         if(fast !== true){
23852                 var ds = this.grid.store;
23853             var s = this.selections;
23854             s.each(function(r){
23855                 this.deselectRow(ds.indexOfId(r.id));
23856             }, this);
23857             s.clear();
23858         }else{
23859             this.selections.clear();
23860         }
23861         this.last = false;
23862     },
23863
23864
23865     /**
23866      * Selects all rows.
23867      */
23868     selectAll : function(){
23869         if(this.locked) {
23870             return;
23871         }
23872         this.selections.clear();
23873         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23874             this.selectRow(i, true);
23875         }
23876     },
23877
23878     /**
23879      * Returns True if there is a selection.
23880      * @return {Boolean}
23881      */
23882     hasSelection : function(){
23883         return this.selections.length > 0;
23884     },
23885
23886     /**
23887      * Returns True if the specified row is selected.
23888      * @param {Number/Record} record The record or index of the record to check
23889      * @return {Boolean}
23890      */
23891     isSelected : function(index){
23892             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23893         return (r && this.selections.key(r.id) ? true : false);
23894     },
23895
23896     /**
23897      * Returns True if the specified record id is selected.
23898      * @param {String} id The id of record to check
23899      * @return {Boolean}
23900      */
23901     isIdSelected : function(id){
23902         return (this.selections.key(id) ? true : false);
23903     },
23904
23905
23906     // private
23907     handleMouseDBClick : function(e, t){
23908         
23909     },
23910     // private
23911     handleMouseDown : function(e, t)
23912     {
23913             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23914         if(this.isLocked() || rowIndex < 0 ){
23915             return;
23916         };
23917         if(e.shiftKey && this.last !== false){
23918             var last = this.last;
23919             this.selectRange(last, rowIndex, e.ctrlKey);
23920             this.last = last; // reset the last
23921             t.focus();
23922     
23923         }else{
23924             var isSelected = this.isSelected(rowIndex);
23925             //Roo.log("select row:" + rowIndex);
23926             if(isSelected){
23927                 this.deselectRow(rowIndex);
23928             } else {
23929                         this.selectRow(rowIndex, true);
23930             }
23931     
23932             /*
23933                 if(e.button !== 0 && isSelected){
23934                 alert('rowIndex 2: ' + rowIndex);
23935                     view.focusRow(rowIndex);
23936                 }else if(e.ctrlKey && isSelected){
23937                     this.deselectRow(rowIndex);
23938                 }else if(!isSelected){
23939                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23940                     view.focusRow(rowIndex);
23941                 }
23942             */
23943         }
23944         this.fireEvent("afterselectionchange", this);
23945     },
23946     // private
23947     handleDragableRowClick :  function(grid, rowIndex, e) 
23948     {
23949         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23950             this.selectRow(rowIndex, false);
23951             grid.view.focusRow(rowIndex);
23952              this.fireEvent("afterselectionchange", this);
23953         }
23954     },
23955     
23956     /**
23957      * Selects multiple rows.
23958      * @param {Array} rows Array of the indexes of the row to select
23959      * @param {Boolean} keepExisting (optional) True to keep existing selections
23960      */
23961     selectRows : function(rows, keepExisting){
23962         if(!keepExisting){
23963             this.clearSelections();
23964         }
23965         for(var i = 0, len = rows.length; i < len; i++){
23966             this.selectRow(rows[i], true);
23967         }
23968     },
23969
23970     /**
23971      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23972      * @param {Number} startRow The index of the first row in the range
23973      * @param {Number} endRow The index of the last row in the range
23974      * @param {Boolean} keepExisting (optional) True to retain existing selections
23975      */
23976     selectRange : function(startRow, endRow, keepExisting){
23977         if(this.locked) {
23978             return;
23979         }
23980         if(!keepExisting){
23981             this.clearSelections();
23982         }
23983         if(startRow <= endRow){
23984             for(var i = startRow; i <= endRow; i++){
23985                 this.selectRow(i, true);
23986             }
23987         }else{
23988             for(var i = startRow; i >= endRow; i--){
23989                 this.selectRow(i, true);
23990             }
23991         }
23992     },
23993
23994     /**
23995      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23996      * @param {Number} startRow The index of the first row in the range
23997      * @param {Number} endRow The index of the last row in the range
23998      */
23999     deselectRange : function(startRow, endRow, preventViewNotify){
24000         if(this.locked) {
24001             return;
24002         }
24003         for(var i = startRow; i <= endRow; i++){
24004             this.deselectRow(i, preventViewNotify);
24005         }
24006     },
24007
24008     /**
24009      * Selects a row.
24010      * @param {Number} row The index of the row to select
24011      * @param {Boolean} keepExisting (optional) True to keep existing selections
24012      */
24013     selectRow : function(index, keepExisting, preventViewNotify)
24014     {
24015             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24016             return;
24017         }
24018         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24019             if(!keepExisting || this.singleSelect){
24020                 this.clearSelections();
24021             }
24022             
24023             var r = this.grid.store.getAt(index);
24024             //console.log('selectRow - record id :' + r.id);
24025             
24026             this.selections.add(r);
24027             this.last = this.lastActive = index;
24028             if(!preventViewNotify){
24029                 var proxy = new Roo.Element(
24030                                 this.grid.getRowDom(index)
24031                 );
24032                 proxy.addClass('bg-info info');
24033             }
24034             this.fireEvent("rowselect", this, index, r);
24035             this.fireEvent("selectionchange", this);
24036         }
24037     },
24038
24039     /**
24040      * Deselects a row.
24041      * @param {Number} row The index of the row to deselect
24042      */
24043     deselectRow : function(index, preventViewNotify)
24044     {
24045         if(this.locked) {
24046             return;
24047         }
24048         if(this.last == index){
24049             this.last = false;
24050         }
24051         if(this.lastActive == index){
24052             this.lastActive = false;
24053         }
24054         
24055         var r = this.grid.store.getAt(index);
24056         if (!r) {
24057             return;
24058         }
24059         
24060         this.selections.remove(r);
24061         //.console.log('deselectRow - record id :' + r.id);
24062         if(!preventViewNotify){
24063         
24064             var proxy = new Roo.Element(
24065                 this.grid.getRowDom(index)
24066             );
24067             proxy.removeClass('bg-info info');
24068         }
24069         this.fireEvent("rowdeselect", this, index);
24070         this.fireEvent("selectionchange", this);
24071     },
24072
24073     // private
24074     restoreLast : function(){
24075         if(this._last){
24076             this.last = this._last;
24077         }
24078     },
24079
24080     // private
24081     acceptsNav : function(row, col, cm){
24082         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24083     },
24084
24085     // private
24086     onEditorKey : function(field, e){
24087         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24088         if(k == e.TAB){
24089             e.stopEvent();
24090             ed.completeEdit();
24091             if(e.shiftKey){
24092                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24093             }else{
24094                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24095             }
24096         }else if(k == e.ENTER && !e.ctrlKey){
24097             e.stopEvent();
24098             ed.completeEdit();
24099             if(e.shiftKey){
24100                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24101             }else{
24102                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24103             }
24104         }else if(k == e.ESC){
24105             ed.cancelEdit();
24106         }
24107         if(newCell){
24108             g.startEditing(newCell[0], newCell[1]);
24109         }
24110     }
24111 });
24112 /*
24113  * Based on:
24114  * Ext JS Library 1.1.1
24115  * Copyright(c) 2006-2007, Ext JS, LLC.
24116  *
24117  * Originally Released Under LGPL - original licence link has changed is not relivant.
24118  *
24119  * Fork - LGPL
24120  * <script type="text/javascript">
24121  */
24122  
24123 /**
24124  * @class Roo.bootstrap.PagingToolbar
24125  * @extends Roo.bootstrap.NavSimplebar
24126  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24127  * @constructor
24128  * Create a new PagingToolbar
24129  * @param {Object} config The config object
24130  * @param {Roo.data.Store} store
24131  */
24132 Roo.bootstrap.PagingToolbar = function(config)
24133 {
24134     // old args format still supported... - xtype is prefered..
24135         // created from xtype...
24136     
24137     this.ds = config.dataSource;
24138     
24139     if (config.store && !this.ds) {
24140         this.store= Roo.factory(config.store, Roo.data);
24141         this.ds = this.store;
24142         this.ds.xmodule = this.xmodule || false;
24143     }
24144     
24145     this.toolbarItems = [];
24146     if (config.items) {
24147         this.toolbarItems = config.items;
24148     }
24149     
24150     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24151     
24152     this.cursor = 0;
24153     
24154     if (this.ds) { 
24155         this.bind(this.ds);
24156     }
24157     
24158     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24159     
24160 };
24161
24162 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24163     /**
24164      * @cfg {Roo.data.Store} dataSource
24165      * The underlying data store providing the paged data
24166      */
24167     /**
24168      * @cfg {String/HTMLElement/Element} container
24169      * container The id or element that will contain the toolbar
24170      */
24171     /**
24172      * @cfg {Boolean} displayInfo
24173      * True to display the displayMsg (defaults to false)
24174      */
24175     /**
24176      * @cfg {Number} pageSize
24177      * The number of records to display per page (defaults to 20)
24178      */
24179     pageSize: 20,
24180     /**
24181      * @cfg {String} displayMsg
24182      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24183      */
24184     displayMsg : 'Displaying {0} - {1} of {2}',
24185     /**
24186      * @cfg {String} emptyMsg
24187      * The message to display when no records are found (defaults to "No data to display")
24188      */
24189     emptyMsg : 'No data to display',
24190     /**
24191      * Customizable piece of the default paging text (defaults to "Page")
24192      * @type String
24193      */
24194     beforePageText : "Page",
24195     /**
24196      * Customizable piece of the default paging text (defaults to "of %0")
24197      * @type String
24198      */
24199     afterPageText : "of {0}",
24200     /**
24201      * Customizable piece of the default paging text (defaults to "First Page")
24202      * @type String
24203      */
24204     firstText : "First Page",
24205     /**
24206      * Customizable piece of the default paging text (defaults to "Previous Page")
24207      * @type String
24208      */
24209     prevText : "Previous Page",
24210     /**
24211      * Customizable piece of the default paging text (defaults to "Next Page")
24212      * @type String
24213      */
24214     nextText : "Next Page",
24215     /**
24216      * Customizable piece of the default paging text (defaults to "Last Page")
24217      * @type String
24218      */
24219     lastText : "Last Page",
24220     /**
24221      * Customizable piece of the default paging text (defaults to "Refresh")
24222      * @type String
24223      */
24224     refreshText : "Refresh",
24225
24226     buttons : false,
24227     // private
24228     onRender : function(ct, position) 
24229     {
24230         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24231         this.navgroup.parentId = this.id;
24232         this.navgroup.onRender(this.el, null);
24233         // add the buttons to the navgroup
24234         
24235         if(this.displayInfo){
24236             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24237             this.displayEl = this.el.select('.x-paging-info', true).first();
24238 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24239 //            this.displayEl = navel.el.select('span',true).first();
24240         }
24241         
24242         var _this = this;
24243         
24244         if(this.buttons){
24245             Roo.each(_this.buttons, function(e){ // this might need to use render????
24246                Roo.factory(e).onRender(_this.el, null);
24247             });
24248         }
24249             
24250         Roo.each(_this.toolbarItems, function(e) {
24251             _this.navgroup.addItem(e);
24252         });
24253         
24254         
24255         this.first = this.navgroup.addItem({
24256             tooltip: this.firstText,
24257             cls: "prev",
24258             icon : 'fa fa-backward',
24259             disabled: true,
24260             preventDefault: true,
24261             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24262         });
24263         
24264         this.prev =  this.navgroup.addItem({
24265             tooltip: this.prevText,
24266             cls: "prev",
24267             icon : 'fa fa-step-backward',
24268             disabled: true,
24269             preventDefault: true,
24270             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24271         });
24272     //this.addSeparator();
24273         
24274         
24275         var field = this.navgroup.addItem( {
24276             tagtype : 'span',
24277             cls : 'x-paging-position',
24278             
24279             html : this.beforePageText  +
24280                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24281                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24282          } ); //?? escaped?
24283         
24284         this.field = field.el.select('input', true).first();
24285         this.field.on("keydown", this.onPagingKeydown, this);
24286         this.field.on("focus", function(){this.dom.select();});
24287     
24288     
24289         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24290         //this.field.setHeight(18);
24291         //this.addSeparator();
24292         this.next = this.navgroup.addItem({
24293             tooltip: this.nextText,
24294             cls: "next",
24295             html : ' <i class="fa fa-step-forward">',
24296             disabled: true,
24297             preventDefault: true,
24298             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24299         });
24300         this.last = this.navgroup.addItem({
24301             tooltip: this.lastText,
24302             icon : 'fa fa-forward',
24303             cls: "next",
24304             disabled: true,
24305             preventDefault: true,
24306             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24307         });
24308     //this.addSeparator();
24309         this.loading = this.navgroup.addItem({
24310             tooltip: this.refreshText,
24311             icon: 'fa fa-refresh',
24312             preventDefault: true,
24313             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24314         });
24315         
24316     },
24317
24318     // private
24319     updateInfo : function(){
24320         if(this.displayEl){
24321             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24322             var msg = count == 0 ?
24323                 this.emptyMsg :
24324                 String.format(
24325                     this.displayMsg,
24326                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24327                 );
24328             this.displayEl.update(msg);
24329         }
24330     },
24331
24332     // private
24333     onLoad : function(ds, r, o)
24334     {
24335         this.cursor = o.params ? o.params.start : 0;
24336         var d = this.getPageData(),
24337             ap = d.activePage,
24338             ps = d.pages;
24339         
24340         
24341         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24342         this.field.dom.value = ap;
24343         this.first.setDisabled(ap == 1);
24344         this.prev.setDisabled(ap == 1);
24345         this.next.setDisabled(ap == ps);
24346         this.last.setDisabled(ap == ps);
24347         this.loading.enable();
24348         this.updateInfo();
24349     },
24350
24351     // private
24352     getPageData : function(){
24353         var total = this.ds.getTotalCount();
24354         return {
24355             total : total,
24356             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24357             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24358         };
24359     },
24360
24361     // private
24362     onLoadError : function(){
24363         this.loading.enable();
24364     },
24365
24366     // private
24367     onPagingKeydown : function(e){
24368         var k = e.getKey();
24369         var d = this.getPageData();
24370         if(k == e.RETURN){
24371             var v = this.field.dom.value, pageNum;
24372             if(!v || isNaN(pageNum = parseInt(v, 10))){
24373                 this.field.dom.value = d.activePage;
24374                 return;
24375             }
24376             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24377             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24378             e.stopEvent();
24379         }
24380         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))
24381         {
24382           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24383           this.field.dom.value = pageNum;
24384           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24385           e.stopEvent();
24386         }
24387         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24388         {
24389           var v = this.field.dom.value, pageNum; 
24390           var increment = (e.shiftKey) ? 10 : 1;
24391           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24392                 increment *= -1;
24393           }
24394           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24395             this.field.dom.value = d.activePage;
24396             return;
24397           }
24398           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24399           {
24400             this.field.dom.value = parseInt(v, 10) + increment;
24401             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24402             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24403           }
24404           e.stopEvent();
24405         }
24406     },
24407
24408     // private
24409     beforeLoad : function(){
24410         if(this.loading){
24411             this.loading.disable();
24412         }
24413     },
24414
24415     // private
24416     onClick : function(which){
24417         
24418         var ds = this.ds;
24419         if (!ds) {
24420             return;
24421         }
24422         
24423         switch(which){
24424             case "first":
24425                 ds.load({params:{start: 0, limit: this.pageSize}});
24426             break;
24427             case "prev":
24428                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24429             break;
24430             case "next":
24431                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24432             break;
24433             case "last":
24434                 var total = ds.getTotalCount();
24435                 var extra = total % this.pageSize;
24436                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24437                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24438             break;
24439             case "refresh":
24440                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24441             break;
24442         }
24443     },
24444
24445     /**
24446      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24447      * @param {Roo.data.Store} store The data store to unbind
24448      */
24449     unbind : function(ds){
24450         ds.un("beforeload", this.beforeLoad, this);
24451         ds.un("load", this.onLoad, this);
24452         ds.un("loadexception", this.onLoadError, this);
24453         ds.un("remove", this.updateInfo, this);
24454         ds.un("add", this.updateInfo, this);
24455         this.ds = undefined;
24456     },
24457
24458     /**
24459      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24460      * @param {Roo.data.Store} store The data store to bind
24461      */
24462     bind : function(ds){
24463         ds.on("beforeload", this.beforeLoad, this);
24464         ds.on("load", this.onLoad, this);
24465         ds.on("loadexception", this.onLoadError, this);
24466         ds.on("remove", this.updateInfo, this);
24467         ds.on("add", this.updateInfo, this);
24468         this.ds = ds;
24469     }
24470 });/*
24471  * - LGPL
24472  *
24473  * element
24474  * 
24475  */
24476
24477 /**
24478  * @class Roo.bootstrap.MessageBar
24479  * @extends Roo.bootstrap.Component
24480  * Bootstrap MessageBar class
24481  * @cfg {String} html contents of the MessageBar
24482  * @cfg {String} weight (info | success | warning | danger) default info
24483  * @cfg {String} beforeClass insert the bar before the given class
24484  * @cfg {Boolean} closable (true | false) default false
24485  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24486  * 
24487  * @constructor
24488  * Create a new Element
24489  * @param {Object} config The config object
24490  */
24491
24492 Roo.bootstrap.MessageBar = function(config){
24493     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24494 };
24495
24496 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24497     
24498     html: '',
24499     weight: 'info',
24500     closable: false,
24501     fixed: false,
24502     beforeClass: 'bootstrap-sticky-wrap',
24503     
24504     getAutoCreate : function(){
24505         
24506         var cfg = {
24507             tag: 'div',
24508             cls: 'alert alert-dismissable alert-' + this.weight,
24509             cn: [
24510                 {
24511                     tag: 'span',
24512                     cls: 'message',
24513                     html: this.html || ''
24514                 }
24515             ]
24516         };
24517         
24518         if(this.fixed){
24519             cfg.cls += ' alert-messages-fixed';
24520         }
24521         
24522         if(this.closable){
24523             cfg.cn.push({
24524                 tag: 'button',
24525                 cls: 'close',
24526                 html: 'x'
24527             });
24528         }
24529         
24530         return cfg;
24531     },
24532     
24533     onRender : function(ct, position)
24534     {
24535         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24536         
24537         if(!this.el){
24538             var cfg = Roo.apply({},  this.getAutoCreate());
24539             cfg.id = Roo.id();
24540             
24541             if (this.cls) {
24542                 cfg.cls += ' ' + this.cls;
24543             }
24544             if (this.style) {
24545                 cfg.style = this.style;
24546             }
24547             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24548             
24549             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24550         }
24551         
24552         this.el.select('>button.close').on('click', this.hide, this);
24553         
24554     },
24555     
24556     show : function()
24557     {
24558         if (!this.rendered) {
24559             this.render();
24560         }
24561         
24562         this.el.show();
24563         
24564         this.fireEvent('show', this);
24565         
24566     },
24567     
24568     hide : function()
24569     {
24570         if (!this.rendered) {
24571             this.render();
24572         }
24573         
24574         this.el.hide();
24575         
24576         this.fireEvent('hide', this);
24577     },
24578     
24579     update : function()
24580     {
24581 //        var e = this.el.dom.firstChild;
24582 //        
24583 //        if(this.closable){
24584 //            e = e.nextSibling;
24585 //        }
24586 //        
24587 //        e.data = this.html || '';
24588
24589         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24590     }
24591    
24592 });
24593
24594  
24595
24596      /*
24597  * - LGPL
24598  *
24599  * Graph
24600  * 
24601  */
24602
24603
24604 /**
24605  * @class Roo.bootstrap.Graph
24606  * @extends Roo.bootstrap.Component
24607  * Bootstrap Graph class
24608 > Prameters
24609  -sm {number} sm 4
24610  -md {number} md 5
24611  @cfg {String} graphtype  bar | vbar | pie
24612  @cfg {number} g_x coodinator | centre x (pie)
24613  @cfg {number} g_y coodinator | centre y (pie)
24614  @cfg {number} g_r radius (pie)
24615  @cfg {number} g_height height of the chart (respected by all elements in the set)
24616  @cfg {number} g_width width of the chart (respected by all elements in the set)
24617  @cfg {Object} title The title of the chart
24618     
24619  -{Array}  values
24620  -opts (object) options for the chart 
24621      o {
24622      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24623      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24624      o vgutter (number)
24625      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.
24626      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24627      o to
24628      o stretch (boolean)
24629      o }
24630  -opts (object) options for the pie
24631      o{
24632      o cut
24633      o startAngle (number)
24634      o endAngle (number)
24635      } 
24636  *
24637  * @constructor
24638  * Create a new Input
24639  * @param {Object} config The config object
24640  */
24641
24642 Roo.bootstrap.Graph = function(config){
24643     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24644     
24645     this.addEvents({
24646         // img events
24647         /**
24648          * @event click
24649          * The img click event for the img.
24650          * @param {Roo.EventObject} e
24651          */
24652         "click" : true
24653     });
24654 };
24655
24656 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24657     
24658     sm: 4,
24659     md: 5,
24660     graphtype: 'bar',
24661     g_height: 250,
24662     g_width: 400,
24663     g_x: 50,
24664     g_y: 50,
24665     g_r: 30,
24666     opts:{
24667         //g_colors: this.colors,
24668         g_type: 'soft',
24669         g_gutter: '20%'
24670
24671     },
24672     title : false,
24673
24674     getAutoCreate : function(){
24675         
24676         var cfg = {
24677             tag: 'div',
24678             html : null
24679         };
24680         
24681         
24682         return  cfg;
24683     },
24684
24685     onRender : function(ct,position){
24686         
24687         
24688         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24689         
24690         if (typeof(Raphael) == 'undefined') {
24691             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24692             return;
24693         }
24694         
24695         this.raphael = Raphael(this.el.dom);
24696         
24697                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24698                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24699                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24700                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24701                 /*
24702                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24703                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24704                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24705                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24706                 
24707                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24708                 r.barchart(330, 10, 300, 220, data1);
24709                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24710                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24711                 */
24712                 
24713                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24714                 // r.barchart(30, 30, 560, 250,  xdata, {
24715                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24716                 //     axis : "0 0 1 1",
24717                 //     axisxlabels :  xdata
24718                 //     //yvalues : cols,
24719                    
24720                 // });
24721 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24722 //        
24723 //        this.load(null,xdata,{
24724 //                axis : "0 0 1 1",
24725 //                axisxlabels :  xdata
24726 //                });
24727
24728     },
24729
24730     load : function(graphtype,xdata,opts)
24731     {
24732         this.raphael.clear();
24733         if(!graphtype) {
24734             graphtype = this.graphtype;
24735         }
24736         if(!opts){
24737             opts = this.opts;
24738         }
24739         var r = this.raphael,
24740             fin = function () {
24741                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24742             },
24743             fout = function () {
24744                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24745             },
24746             pfin = function() {
24747                 this.sector.stop();
24748                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24749
24750                 if (this.label) {
24751                     this.label[0].stop();
24752                     this.label[0].attr({ r: 7.5 });
24753                     this.label[1].attr({ "font-weight": 800 });
24754                 }
24755             },
24756             pfout = function() {
24757                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24758
24759                 if (this.label) {
24760                     this.label[0].animate({ r: 5 }, 500, "bounce");
24761                     this.label[1].attr({ "font-weight": 400 });
24762                 }
24763             };
24764
24765         switch(graphtype){
24766             case 'bar':
24767                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24768                 break;
24769             case 'hbar':
24770                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24771                 break;
24772             case 'pie':
24773 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24774 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24775 //            
24776                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24777                 
24778                 break;
24779
24780         }
24781         
24782         if(this.title){
24783             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24784         }
24785         
24786     },
24787     
24788     setTitle: function(o)
24789     {
24790         this.title = o;
24791     },
24792     
24793     initEvents: function() {
24794         
24795         if(!this.href){
24796             this.el.on('click', this.onClick, this);
24797         }
24798     },
24799     
24800     onClick : function(e)
24801     {
24802         Roo.log('img onclick');
24803         this.fireEvent('click', this, e);
24804     }
24805    
24806 });
24807
24808  
24809 /*
24810  * - LGPL
24811  *
24812  * numberBox
24813  * 
24814  */
24815 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24816
24817 /**
24818  * @class Roo.bootstrap.dash.NumberBox
24819  * @extends Roo.bootstrap.Component
24820  * Bootstrap NumberBox class
24821  * @cfg {String} headline Box headline
24822  * @cfg {String} content Box content
24823  * @cfg {String} icon Box icon
24824  * @cfg {String} footer Footer text
24825  * @cfg {String} fhref Footer href
24826  * 
24827  * @constructor
24828  * Create a new NumberBox
24829  * @param {Object} config The config object
24830  */
24831
24832
24833 Roo.bootstrap.dash.NumberBox = function(config){
24834     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24835     
24836 };
24837
24838 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24839     
24840     headline : '',
24841     content : '',
24842     icon : '',
24843     footer : '',
24844     fhref : '',
24845     ficon : '',
24846     
24847     getAutoCreate : function(){
24848         
24849         var cfg = {
24850             tag : 'div',
24851             cls : 'small-box ',
24852             cn : [
24853                 {
24854                     tag : 'div',
24855                     cls : 'inner',
24856                     cn :[
24857                         {
24858                             tag : 'h3',
24859                             cls : 'roo-headline',
24860                             html : this.headline
24861                         },
24862                         {
24863                             tag : 'p',
24864                             cls : 'roo-content',
24865                             html : this.content
24866                         }
24867                     ]
24868                 }
24869             ]
24870         };
24871         
24872         if(this.icon){
24873             cfg.cn.push({
24874                 tag : 'div',
24875                 cls : 'icon',
24876                 cn :[
24877                     {
24878                         tag : 'i',
24879                         cls : 'ion ' + this.icon
24880                     }
24881                 ]
24882             });
24883         }
24884         
24885         if(this.footer){
24886             var footer = {
24887                 tag : 'a',
24888                 cls : 'small-box-footer',
24889                 href : this.fhref || '#',
24890                 html : this.footer
24891             };
24892             
24893             cfg.cn.push(footer);
24894             
24895         }
24896         
24897         return  cfg;
24898     },
24899
24900     onRender : function(ct,position){
24901         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24902
24903
24904        
24905                 
24906     },
24907
24908     setHeadline: function (value)
24909     {
24910         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24911     },
24912     
24913     setFooter: function (value, href)
24914     {
24915         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24916         
24917         if(href){
24918             this.el.select('a.small-box-footer',true).first().attr('href', href);
24919         }
24920         
24921     },
24922
24923     setContent: function (value)
24924     {
24925         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24926     },
24927
24928     initEvents: function() 
24929     {   
24930         
24931     }
24932     
24933 });
24934
24935  
24936 /*
24937  * - LGPL
24938  *
24939  * TabBox
24940  * 
24941  */
24942 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24943
24944 /**
24945  * @class Roo.bootstrap.dash.TabBox
24946  * @extends Roo.bootstrap.Component
24947  * Bootstrap TabBox class
24948  * @cfg {String} title Title of the TabBox
24949  * @cfg {String} icon Icon of the TabBox
24950  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24951  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24952  * 
24953  * @constructor
24954  * Create a new TabBox
24955  * @param {Object} config The config object
24956  */
24957
24958
24959 Roo.bootstrap.dash.TabBox = function(config){
24960     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24961     this.addEvents({
24962         // raw events
24963         /**
24964          * @event addpane
24965          * When a pane is added
24966          * @param {Roo.bootstrap.dash.TabPane} pane
24967          */
24968         "addpane" : true,
24969         /**
24970          * @event activatepane
24971          * When a pane is activated
24972          * @param {Roo.bootstrap.dash.TabPane} pane
24973          */
24974         "activatepane" : true
24975         
24976          
24977     });
24978     
24979     this.panes = [];
24980 };
24981
24982 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24983
24984     title : '',
24985     icon : false,
24986     showtabs : true,
24987     tabScrollable : false,
24988     
24989     getChildContainer : function()
24990     {
24991         return this.el.select('.tab-content', true).first();
24992     },
24993     
24994     getAutoCreate : function(){
24995         
24996         var header = {
24997             tag: 'li',
24998             cls: 'pull-left header',
24999             html: this.title,
25000             cn : []
25001         };
25002         
25003         if(this.icon){
25004             header.cn.push({
25005                 tag: 'i',
25006                 cls: 'fa ' + this.icon
25007             });
25008         }
25009         
25010         var h = {
25011             tag: 'ul',
25012             cls: 'nav nav-tabs pull-right',
25013             cn: [
25014                 header
25015             ]
25016         };
25017         
25018         if(this.tabScrollable){
25019             h = {
25020                 tag: 'div',
25021                 cls: 'tab-header',
25022                 cn: [
25023                     {
25024                         tag: 'ul',
25025                         cls: 'nav nav-tabs pull-right',
25026                         cn: [
25027                             header
25028                         ]
25029                     }
25030                 ]
25031             };
25032         }
25033         
25034         var cfg = {
25035             tag: 'div',
25036             cls: 'nav-tabs-custom',
25037             cn: [
25038                 h,
25039                 {
25040                     tag: 'div',
25041                     cls: 'tab-content no-padding',
25042                     cn: []
25043                 }
25044             ]
25045         };
25046
25047         return  cfg;
25048     },
25049     initEvents : function()
25050     {
25051         //Roo.log('add add pane handler');
25052         this.on('addpane', this.onAddPane, this);
25053     },
25054      /**
25055      * Updates the box title
25056      * @param {String} html to set the title to.
25057      */
25058     setTitle : function(value)
25059     {
25060         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25061     },
25062     onAddPane : function(pane)
25063     {
25064         this.panes.push(pane);
25065         //Roo.log('addpane');
25066         //Roo.log(pane);
25067         // tabs are rendere left to right..
25068         if(!this.showtabs){
25069             return;
25070         }
25071         
25072         var ctr = this.el.select('.nav-tabs', true).first();
25073          
25074          
25075         var existing = ctr.select('.nav-tab',true);
25076         var qty = existing.getCount();;
25077         
25078         
25079         var tab = ctr.createChild({
25080             tag : 'li',
25081             cls : 'nav-tab' + (qty ? '' : ' active'),
25082             cn : [
25083                 {
25084                     tag : 'a',
25085                     href:'#',
25086                     html : pane.title
25087                 }
25088             ]
25089         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25090         pane.tab = tab;
25091         
25092         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25093         if (!qty) {
25094             pane.el.addClass('active');
25095         }
25096         
25097                 
25098     },
25099     onTabClick : function(ev,un,ob,pane)
25100     {
25101         //Roo.log('tab - prev default');
25102         ev.preventDefault();
25103         
25104         
25105         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25106         pane.tab.addClass('active');
25107         //Roo.log(pane.title);
25108         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25109         // technically we should have a deactivate event.. but maybe add later.
25110         // and it should not de-activate the selected tab...
25111         this.fireEvent('activatepane', pane);
25112         pane.el.addClass('active');
25113         pane.fireEvent('activate');
25114         
25115         
25116     },
25117     
25118     getActivePane : function()
25119     {
25120         var r = false;
25121         Roo.each(this.panes, function(p) {
25122             if(p.el.hasClass('active')){
25123                 r = p;
25124                 return false;
25125             }
25126             
25127             return;
25128         });
25129         
25130         return r;
25131     }
25132     
25133     
25134 });
25135
25136  
25137 /*
25138  * - LGPL
25139  *
25140  * Tab pane
25141  * 
25142  */
25143 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25144 /**
25145  * @class Roo.bootstrap.TabPane
25146  * @extends Roo.bootstrap.Component
25147  * Bootstrap TabPane class
25148  * @cfg {Boolean} active (false | true) Default false
25149  * @cfg {String} title title of panel
25150
25151  * 
25152  * @constructor
25153  * Create a new TabPane
25154  * @param {Object} config The config object
25155  */
25156
25157 Roo.bootstrap.dash.TabPane = function(config){
25158     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25159     
25160     this.addEvents({
25161         // raw events
25162         /**
25163          * @event activate
25164          * When a pane is activated
25165          * @param {Roo.bootstrap.dash.TabPane} pane
25166          */
25167         "activate" : true
25168          
25169     });
25170 };
25171
25172 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25173     
25174     active : false,
25175     title : '',
25176     
25177     // the tabBox that this is attached to.
25178     tab : false,
25179      
25180     getAutoCreate : function() 
25181     {
25182         var cfg = {
25183             tag: 'div',
25184             cls: 'tab-pane'
25185         };
25186         
25187         if(this.active){
25188             cfg.cls += ' active';
25189         }
25190         
25191         return cfg;
25192     },
25193     initEvents  : function()
25194     {
25195         //Roo.log('trigger add pane handler');
25196         this.parent().fireEvent('addpane', this)
25197     },
25198     
25199      /**
25200      * Updates the tab title 
25201      * @param {String} html to set the title to.
25202      */
25203     setTitle: function(str)
25204     {
25205         if (!this.tab) {
25206             return;
25207         }
25208         this.title = str;
25209         this.tab.select('a', true).first().dom.innerHTML = str;
25210         
25211     }
25212     
25213     
25214     
25215 });
25216
25217  
25218
25219
25220  /*
25221  * - LGPL
25222  *
25223  * menu
25224  * 
25225  */
25226 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25227
25228 /**
25229  * @class Roo.bootstrap.menu.Menu
25230  * @extends Roo.bootstrap.Component
25231  * Bootstrap Menu class - container for Menu
25232  * @cfg {String} html Text of the menu
25233  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25234  * @cfg {String} icon Font awesome icon
25235  * @cfg {String} pos Menu align to (top | bottom) default bottom
25236  * 
25237  * 
25238  * @constructor
25239  * Create a new Menu
25240  * @param {Object} config The config object
25241  */
25242
25243
25244 Roo.bootstrap.menu.Menu = function(config){
25245     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25246     
25247     this.addEvents({
25248         /**
25249          * @event beforeshow
25250          * Fires before this menu is displayed
25251          * @param {Roo.bootstrap.menu.Menu} this
25252          */
25253         beforeshow : true,
25254         /**
25255          * @event beforehide
25256          * Fires before this menu is hidden
25257          * @param {Roo.bootstrap.menu.Menu} this
25258          */
25259         beforehide : true,
25260         /**
25261          * @event show
25262          * Fires after this menu is displayed
25263          * @param {Roo.bootstrap.menu.Menu} this
25264          */
25265         show : true,
25266         /**
25267          * @event hide
25268          * Fires after this menu is hidden
25269          * @param {Roo.bootstrap.menu.Menu} this
25270          */
25271         hide : true,
25272         /**
25273          * @event click
25274          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25275          * @param {Roo.bootstrap.menu.Menu} this
25276          * @param {Roo.EventObject} e
25277          */
25278         click : true
25279     });
25280     
25281 };
25282
25283 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25284     
25285     submenu : false,
25286     html : '',
25287     weight : 'default',
25288     icon : false,
25289     pos : 'bottom',
25290     
25291     
25292     getChildContainer : function() {
25293         if(this.isSubMenu){
25294             return this.el;
25295         }
25296         
25297         return this.el.select('ul.dropdown-menu', true).first();  
25298     },
25299     
25300     getAutoCreate : function()
25301     {
25302         var text = [
25303             {
25304                 tag : 'span',
25305                 cls : 'roo-menu-text',
25306                 html : this.html
25307             }
25308         ];
25309         
25310         if(this.icon){
25311             text.unshift({
25312                 tag : 'i',
25313                 cls : 'fa ' + this.icon
25314             })
25315         }
25316         
25317         
25318         var cfg = {
25319             tag : 'div',
25320             cls : 'btn-group',
25321             cn : [
25322                 {
25323                     tag : 'button',
25324                     cls : 'dropdown-button btn btn-' + this.weight,
25325                     cn : text
25326                 },
25327                 {
25328                     tag : 'button',
25329                     cls : 'dropdown-toggle btn btn-' + this.weight,
25330                     cn : [
25331                         {
25332                             tag : 'span',
25333                             cls : 'caret'
25334                         }
25335                     ]
25336                 },
25337                 {
25338                     tag : 'ul',
25339                     cls : 'dropdown-menu'
25340                 }
25341             ]
25342             
25343         };
25344         
25345         if(this.pos == 'top'){
25346             cfg.cls += ' dropup';
25347         }
25348         
25349         if(this.isSubMenu){
25350             cfg = {
25351                 tag : 'ul',
25352                 cls : 'dropdown-menu'
25353             }
25354         }
25355         
25356         return cfg;
25357     },
25358     
25359     onRender : function(ct, position)
25360     {
25361         this.isSubMenu = ct.hasClass('dropdown-submenu');
25362         
25363         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25364     },
25365     
25366     initEvents : function() 
25367     {
25368         if(this.isSubMenu){
25369             return;
25370         }
25371         
25372         this.hidden = true;
25373         
25374         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25375         this.triggerEl.on('click', this.onTriggerPress, this);
25376         
25377         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25378         this.buttonEl.on('click', this.onClick, this);
25379         
25380     },
25381     
25382     list : function()
25383     {
25384         if(this.isSubMenu){
25385             return this.el;
25386         }
25387         
25388         return this.el.select('ul.dropdown-menu', true).first();
25389     },
25390     
25391     onClick : function(e)
25392     {
25393         this.fireEvent("click", this, e);
25394     },
25395     
25396     onTriggerPress  : function(e)
25397     {   
25398         if (this.isVisible()) {
25399             this.hide();
25400         } else {
25401             this.show();
25402         }
25403     },
25404     
25405     isVisible : function(){
25406         return !this.hidden;
25407     },
25408     
25409     show : function()
25410     {
25411         this.fireEvent("beforeshow", this);
25412         
25413         this.hidden = false;
25414         this.el.addClass('open');
25415         
25416         Roo.get(document).on("mouseup", this.onMouseUp, this);
25417         
25418         this.fireEvent("show", this);
25419         
25420         
25421     },
25422     
25423     hide : function()
25424     {
25425         this.fireEvent("beforehide", this);
25426         
25427         this.hidden = true;
25428         this.el.removeClass('open');
25429         
25430         Roo.get(document).un("mouseup", this.onMouseUp);
25431         
25432         this.fireEvent("hide", this);
25433     },
25434     
25435     onMouseUp : function()
25436     {
25437         this.hide();
25438     }
25439     
25440 });
25441
25442  
25443  /*
25444  * - LGPL
25445  *
25446  * menu item
25447  * 
25448  */
25449 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25450
25451 /**
25452  * @class Roo.bootstrap.menu.Item
25453  * @extends Roo.bootstrap.Component
25454  * Bootstrap MenuItem class
25455  * @cfg {Boolean} submenu (true | false) default false
25456  * @cfg {String} html text of the item
25457  * @cfg {String} href the link
25458  * @cfg {Boolean} disable (true | false) default false
25459  * @cfg {Boolean} preventDefault (true | false) default true
25460  * @cfg {String} icon Font awesome icon
25461  * @cfg {String} pos Submenu align to (left | right) default right 
25462  * 
25463  * 
25464  * @constructor
25465  * Create a new Item
25466  * @param {Object} config The config object
25467  */
25468
25469
25470 Roo.bootstrap.menu.Item = function(config){
25471     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25472     this.addEvents({
25473         /**
25474          * @event mouseover
25475          * Fires when the mouse is hovering over this menu
25476          * @param {Roo.bootstrap.menu.Item} this
25477          * @param {Roo.EventObject} e
25478          */
25479         mouseover : true,
25480         /**
25481          * @event mouseout
25482          * Fires when the mouse exits this menu
25483          * @param {Roo.bootstrap.menu.Item} this
25484          * @param {Roo.EventObject} e
25485          */
25486         mouseout : true,
25487         // raw events
25488         /**
25489          * @event click
25490          * The raw click event for the entire grid.
25491          * @param {Roo.EventObject} e
25492          */
25493         click : true
25494     });
25495 };
25496
25497 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25498     
25499     submenu : false,
25500     href : '',
25501     html : '',
25502     preventDefault: true,
25503     disable : false,
25504     icon : false,
25505     pos : 'right',
25506     
25507     getAutoCreate : function()
25508     {
25509         var text = [
25510             {
25511                 tag : 'span',
25512                 cls : 'roo-menu-item-text',
25513                 html : this.html
25514             }
25515         ];
25516         
25517         if(this.icon){
25518             text.unshift({
25519                 tag : 'i',
25520                 cls : 'fa ' + this.icon
25521             })
25522         }
25523         
25524         var cfg = {
25525             tag : 'li',
25526             cn : [
25527                 {
25528                     tag : 'a',
25529                     href : this.href || '#',
25530                     cn : text
25531                 }
25532             ]
25533         };
25534         
25535         if(this.disable){
25536             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25537         }
25538         
25539         if(this.submenu){
25540             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25541             
25542             if(this.pos == 'left'){
25543                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25544             }
25545         }
25546         
25547         return cfg;
25548     },
25549     
25550     initEvents : function() 
25551     {
25552         this.el.on('mouseover', this.onMouseOver, this);
25553         this.el.on('mouseout', this.onMouseOut, this);
25554         
25555         this.el.select('a', true).first().on('click', this.onClick, this);
25556         
25557     },
25558     
25559     onClick : function(e)
25560     {
25561         if(this.preventDefault){
25562             e.preventDefault();
25563         }
25564         
25565         this.fireEvent("click", this, e);
25566     },
25567     
25568     onMouseOver : function(e)
25569     {
25570         if(this.submenu && this.pos == 'left'){
25571             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25572         }
25573         
25574         this.fireEvent("mouseover", this, e);
25575     },
25576     
25577     onMouseOut : function(e)
25578     {
25579         this.fireEvent("mouseout", this, e);
25580     }
25581 });
25582
25583  
25584
25585  /*
25586  * - LGPL
25587  *
25588  * menu separator
25589  * 
25590  */
25591 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25592
25593 /**
25594  * @class Roo.bootstrap.menu.Separator
25595  * @extends Roo.bootstrap.Component
25596  * Bootstrap Separator class
25597  * 
25598  * @constructor
25599  * Create a new Separator
25600  * @param {Object} config The config object
25601  */
25602
25603
25604 Roo.bootstrap.menu.Separator = function(config){
25605     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25606 };
25607
25608 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25609     
25610     getAutoCreate : function(){
25611         var cfg = {
25612             tag : 'li',
25613             cls: 'divider'
25614         };
25615         
25616         return cfg;
25617     }
25618    
25619 });
25620
25621  
25622
25623  /*
25624  * - LGPL
25625  *
25626  * Tooltip
25627  * 
25628  */
25629
25630 /**
25631  * @class Roo.bootstrap.Tooltip
25632  * Bootstrap Tooltip class
25633  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25634  * to determine which dom element triggers the tooltip.
25635  * 
25636  * It needs to add support for additional attributes like tooltip-position
25637  * 
25638  * @constructor
25639  * Create a new Toolti
25640  * @param {Object} config The config object
25641  */
25642
25643 Roo.bootstrap.Tooltip = function(config){
25644     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25645     
25646     this.alignment = Roo.bootstrap.Tooltip.alignment;
25647     
25648     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25649         this.alignment = config.alignment;
25650     }
25651     
25652 };
25653
25654 Roo.apply(Roo.bootstrap.Tooltip, {
25655     /**
25656      * @function init initialize tooltip monitoring.
25657      * @static
25658      */
25659     currentEl : false,
25660     currentTip : false,
25661     currentRegion : false,
25662     
25663     //  init : delay?
25664     
25665     init : function()
25666     {
25667         Roo.get(document).on('mouseover', this.enter ,this);
25668         Roo.get(document).on('mouseout', this.leave, this);
25669          
25670         
25671         this.currentTip = new Roo.bootstrap.Tooltip();
25672     },
25673     
25674     enter : function(ev)
25675     {
25676         var dom = ev.getTarget();
25677         
25678         //Roo.log(['enter',dom]);
25679         var el = Roo.fly(dom);
25680         if (this.currentEl) {
25681             //Roo.log(dom);
25682             //Roo.log(this.currentEl);
25683             //Roo.log(this.currentEl.contains(dom));
25684             if (this.currentEl == el) {
25685                 return;
25686             }
25687             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25688                 return;
25689             }
25690
25691         }
25692         
25693         if (this.currentTip.el) {
25694             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25695         }    
25696         //Roo.log(ev);
25697         
25698         if(!el || el.dom == document){
25699             return;
25700         }
25701         
25702         var bindEl = el;
25703         
25704         // you can not look for children, as if el is the body.. then everythign is the child..
25705         if (!el.attr('tooltip')) { //
25706             if (!el.select("[tooltip]").elements.length) {
25707                 return;
25708             }
25709             // is the mouse over this child...?
25710             bindEl = el.select("[tooltip]").first();
25711             var xy = ev.getXY();
25712             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25713                 //Roo.log("not in region.");
25714                 return;
25715             }
25716             //Roo.log("child element over..");
25717             
25718         }
25719         this.currentEl = bindEl;
25720         this.currentTip.bind(bindEl);
25721         this.currentRegion = Roo.lib.Region.getRegion(dom);
25722         this.currentTip.enter();
25723         
25724     },
25725     leave : function(ev)
25726     {
25727         var dom = ev.getTarget();
25728         //Roo.log(['leave',dom]);
25729         if (!this.currentEl) {
25730             return;
25731         }
25732         
25733         
25734         if (dom != this.currentEl.dom) {
25735             return;
25736         }
25737         var xy = ev.getXY();
25738         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25739             return;
25740         }
25741         // only activate leave if mouse cursor is outside... bounding box..
25742         
25743         
25744         
25745         
25746         if (this.currentTip) {
25747             this.currentTip.leave();
25748         }
25749         //Roo.log('clear currentEl');
25750         this.currentEl = false;
25751         
25752         
25753     },
25754     alignment : {
25755         'left' : ['r-l', [-2,0], 'right'],
25756         'right' : ['l-r', [2,0], 'left'],
25757         'bottom' : ['t-b', [0,2], 'top'],
25758         'top' : [ 'b-t', [0,-2], 'bottom']
25759     }
25760     
25761 });
25762
25763
25764 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25765     
25766     
25767     bindEl : false,
25768     
25769     delay : null, // can be { show : 300 , hide: 500}
25770     
25771     timeout : null,
25772     
25773     hoverState : null, //???
25774     
25775     placement : 'bottom', 
25776     
25777     alignment : false,
25778     
25779     getAutoCreate : function(){
25780     
25781         var cfg = {
25782            cls : 'tooltip',
25783            role : 'tooltip',
25784            cn : [
25785                 {
25786                     cls : 'tooltip-arrow'
25787                 },
25788                 {
25789                     cls : 'tooltip-inner'
25790                 }
25791            ]
25792         };
25793         
25794         return cfg;
25795     },
25796     bind : function(el)
25797     {
25798         this.bindEl = el;
25799     },
25800       
25801     
25802     enter : function () {
25803        
25804         if (this.timeout != null) {
25805             clearTimeout(this.timeout);
25806         }
25807         
25808         this.hoverState = 'in';
25809          //Roo.log("enter - show");
25810         if (!this.delay || !this.delay.show) {
25811             this.show();
25812             return;
25813         }
25814         var _t = this;
25815         this.timeout = setTimeout(function () {
25816             if (_t.hoverState == 'in') {
25817                 _t.show();
25818             }
25819         }, this.delay.show);
25820     },
25821     leave : function()
25822     {
25823         clearTimeout(this.timeout);
25824     
25825         this.hoverState = 'out';
25826          if (!this.delay || !this.delay.hide) {
25827             this.hide();
25828             return;
25829         }
25830        
25831         var _t = this;
25832         this.timeout = setTimeout(function () {
25833             //Roo.log("leave - timeout");
25834             
25835             if (_t.hoverState == 'out') {
25836                 _t.hide();
25837                 Roo.bootstrap.Tooltip.currentEl = false;
25838             }
25839         }, delay);
25840     },
25841     
25842     show : function (msg)
25843     {
25844         if (!this.el) {
25845             this.render(document.body);
25846         }
25847         // set content.
25848         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25849         
25850         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25851         
25852         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25853         
25854         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25855         
25856         var placement = typeof this.placement == 'function' ?
25857             this.placement.call(this, this.el, on_el) :
25858             this.placement;
25859             
25860         var autoToken = /\s?auto?\s?/i;
25861         var autoPlace = autoToken.test(placement);
25862         if (autoPlace) {
25863             placement = placement.replace(autoToken, '') || 'top';
25864         }
25865         
25866         //this.el.detach()
25867         //this.el.setXY([0,0]);
25868         this.el.show();
25869         //this.el.dom.style.display='block';
25870         
25871         //this.el.appendTo(on_el);
25872         
25873         var p = this.getPosition();
25874         var box = this.el.getBox();
25875         
25876         if (autoPlace) {
25877             // fixme..
25878         }
25879         
25880         var align = this.alignment[placement];
25881         
25882         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25883         
25884         if(placement == 'top' || placement == 'bottom'){
25885             if(xy[0] < 0){
25886                 placement = 'right';
25887             }
25888             
25889             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25890                 placement = 'left';
25891             }
25892             
25893             var scroll = Roo.select('body', true).first().getScroll();
25894             
25895             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25896                 placement = 'top';
25897             }
25898             
25899         }
25900         
25901         this.el.alignTo(this.bindEl, align[0],align[1]);
25902         //var arrow = this.el.select('.arrow',true).first();
25903         //arrow.set(align[2], 
25904         
25905         this.el.addClass(placement);
25906         
25907         this.el.addClass('in fade');
25908         
25909         this.hoverState = null;
25910         
25911         if (this.el.hasClass('fade')) {
25912             // fade it?
25913         }
25914         
25915     },
25916     hide : function()
25917     {
25918          
25919         if (!this.el) {
25920             return;
25921         }
25922         //this.el.setXY([0,0]);
25923         this.el.removeClass('in');
25924         //this.el.hide();
25925         
25926     }
25927     
25928 });
25929  
25930
25931  /*
25932  * - LGPL
25933  *
25934  * Location Picker
25935  * 
25936  */
25937
25938 /**
25939  * @class Roo.bootstrap.LocationPicker
25940  * @extends Roo.bootstrap.Component
25941  * Bootstrap LocationPicker class
25942  * @cfg {Number} latitude Position when init default 0
25943  * @cfg {Number} longitude Position when init default 0
25944  * @cfg {Number} zoom default 15
25945  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25946  * @cfg {Boolean} mapTypeControl default false
25947  * @cfg {Boolean} disableDoubleClickZoom default false
25948  * @cfg {Boolean} scrollwheel default true
25949  * @cfg {Boolean} streetViewControl default false
25950  * @cfg {Number} radius default 0
25951  * @cfg {String} locationName
25952  * @cfg {Boolean} draggable default true
25953  * @cfg {Boolean} enableAutocomplete default false
25954  * @cfg {Boolean} enableReverseGeocode default true
25955  * @cfg {String} markerTitle
25956  * 
25957  * @constructor
25958  * Create a new LocationPicker
25959  * @param {Object} config The config object
25960  */
25961
25962
25963 Roo.bootstrap.LocationPicker = function(config){
25964     
25965     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25966     
25967     this.addEvents({
25968         /**
25969          * @event initial
25970          * Fires when the picker initialized.
25971          * @param {Roo.bootstrap.LocationPicker} this
25972          * @param {Google Location} location
25973          */
25974         initial : true,
25975         /**
25976          * @event positionchanged
25977          * Fires when the picker position changed.
25978          * @param {Roo.bootstrap.LocationPicker} this
25979          * @param {Google Location} location
25980          */
25981         positionchanged : true,
25982         /**
25983          * @event resize
25984          * Fires when the map resize.
25985          * @param {Roo.bootstrap.LocationPicker} this
25986          */
25987         resize : true,
25988         /**
25989          * @event show
25990          * Fires when the map show.
25991          * @param {Roo.bootstrap.LocationPicker} this
25992          */
25993         show : true,
25994         /**
25995          * @event hide
25996          * Fires when the map hide.
25997          * @param {Roo.bootstrap.LocationPicker} this
25998          */
25999         hide : true,
26000         /**
26001          * @event mapClick
26002          * Fires when click the map.
26003          * @param {Roo.bootstrap.LocationPicker} this
26004          * @param {Map event} e
26005          */
26006         mapClick : true,
26007         /**
26008          * @event mapRightClick
26009          * Fires when right click the map.
26010          * @param {Roo.bootstrap.LocationPicker} this
26011          * @param {Map event} e
26012          */
26013         mapRightClick : true,
26014         /**
26015          * @event markerClick
26016          * Fires when click the marker.
26017          * @param {Roo.bootstrap.LocationPicker} this
26018          * @param {Map event} e
26019          */
26020         markerClick : true,
26021         /**
26022          * @event markerRightClick
26023          * Fires when right click the marker.
26024          * @param {Roo.bootstrap.LocationPicker} this
26025          * @param {Map event} e
26026          */
26027         markerRightClick : true,
26028         /**
26029          * @event OverlayViewDraw
26030          * Fires when OverlayView Draw
26031          * @param {Roo.bootstrap.LocationPicker} this
26032          */
26033         OverlayViewDraw : true,
26034         /**
26035          * @event OverlayViewOnAdd
26036          * Fires when OverlayView Draw
26037          * @param {Roo.bootstrap.LocationPicker} this
26038          */
26039         OverlayViewOnAdd : true,
26040         /**
26041          * @event OverlayViewOnRemove
26042          * Fires when OverlayView Draw
26043          * @param {Roo.bootstrap.LocationPicker} this
26044          */
26045         OverlayViewOnRemove : true,
26046         /**
26047          * @event OverlayViewShow
26048          * Fires when OverlayView Draw
26049          * @param {Roo.bootstrap.LocationPicker} this
26050          * @param {Pixel} cpx
26051          */
26052         OverlayViewShow : true,
26053         /**
26054          * @event OverlayViewHide
26055          * Fires when OverlayView Draw
26056          * @param {Roo.bootstrap.LocationPicker} this
26057          */
26058         OverlayViewHide : true,
26059         /**
26060          * @event loadexception
26061          * Fires when load google lib failed.
26062          * @param {Roo.bootstrap.LocationPicker} this
26063          */
26064         loadexception : true
26065     });
26066         
26067 };
26068
26069 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26070     
26071     gMapContext: false,
26072     
26073     latitude: 0,
26074     longitude: 0,
26075     zoom: 15,
26076     mapTypeId: false,
26077     mapTypeControl: false,
26078     disableDoubleClickZoom: false,
26079     scrollwheel: true,
26080     streetViewControl: false,
26081     radius: 0,
26082     locationName: '',
26083     draggable: true,
26084     enableAutocomplete: false,
26085     enableReverseGeocode: true,
26086     markerTitle: '',
26087     
26088     getAutoCreate: function()
26089     {
26090
26091         var cfg = {
26092             tag: 'div',
26093             cls: 'roo-location-picker'
26094         };
26095         
26096         return cfg
26097     },
26098     
26099     initEvents: function(ct, position)
26100     {       
26101         if(!this.el.getWidth() || this.isApplied()){
26102             return;
26103         }
26104         
26105         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26106         
26107         this.initial();
26108     },
26109     
26110     initial: function()
26111     {
26112         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26113             this.fireEvent('loadexception', this);
26114             return;
26115         }
26116         
26117         if(!this.mapTypeId){
26118             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26119         }
26120         
26121         this.gMapContext = this.GMapContext();
26122         
26123         this.initOverlayView();
26124         
26125         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26126         
26127         var _this = this;
26128                 
26129         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26130             _this.setPosition(_this.gMapContext.marker.position);
26131         });
26132         
26133         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26134             _this.fireEvent('mapClick', this, event);
26135             
26136         });
26137
26138         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26139             _this.fireEvent('mapRightClick', this, event);
26140             
26141         });
26142         
26143         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26144             _this.fireEvent('markerClick', this, event);
26145             
26146         });
26147
26148         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26149             _this.fireEvent('markerRightClick', this, event);
26150             
26151         });
26152         
26153         this.setPosition(this.gMapContext.location);
26154         
26155         this.fireEvent('initial', this, this.gMapContext.location);
26156     },
26157     
26158     initOverlayView: function()
26159     {
26160         var _this = this;
26161         
26162         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26163             
26164             draw: function()
26165             {
26166                 _this.fireEvent('OverlayViewDraw', _this);
26167             },
26168             
26169             onAdd: function()
26170             {
26171                 _this.fireEvent('OverlayViewOnAdd', _this);
26172             },
26173             
26174             onRemove: function()
26175             {
26176                 _this.fireEvent('OverlayViewOnRemove', _this);
26177             },
26178             
26179             show: function(cpx)
26180             {
26181                 _this.fireEvent('OverlayViewShow', _this, cpx);
26182             },
26183             
26184             hide: function()
26185             {
26186                 _this.fireEvent('OverlayViewHide', _this);
26187             }
26188             
26189         });
26190     },
26191     
26192     fromLatLngToContainerPixel: function(event)
26193     {
26194         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26195     },
26196     
26197     isApplied: function() 
26198     {
26199         return this.getGmapContext() == false ? false : true;
26200     },
26201     
26202     getGmapContext: function() 
26203     {
26204         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26205     },
26206     
26207     GMapContext: function() 
26208     {
26209         var position = new google.maps.LatLng(this.latitude, this.longitude);
26210         
26211         var _map = new google.maps.Map(this.el.dom, {
26212             center: position,
26213             zoom: this.zoom,
26214             mapTypeId: this.mapTypeId,
26215             mapTypeControl: this.mapTypeControl,
26216             disableDoubleClickZoom: this.disableDoubleClickZoom,
26217             scrollwheel: this.scrollwheel,
26218             streetViewControl: this.streetViewControl,
26219             locationName: this.locationName,
26220             draggable: this.draggable,
26221             enableAutocomplete: this.enableAutocomplete,
26222             enableReverseGeocode: this.enableReverseGeocode
26223         });
26224         
26225         var _marker = new google.maps.Marker({
26226             position: position,
26227             map: _map,
26228             title: this.markerTitle,
26229             draggable: this.draggable
26230         });
26231         
26232         return {
26233             map: _map,
26234             marker: _marker,
26235             circle: null,
26236             location: position,
26237             radius: this.radius,
26238             locationName: this.locationName,
26239             addressComponents: {
26240                 formatted_address: null,
26241                 addressLine1: null,
26242                 addressLine2: null,
26243                 streetName: null,
26244                 streetNumber: null,
26245                 city: null,
26246                 district: null,
26247                 state: null,
26248                 stateOrProvince: null
26249             },
26250             settings: this,
26251             domContainer: this.el.dom,
26252             geodecoder: new google.maps.Geocoder()
26253         };
26254     },
26255     
26256     drawCircle: function(center, radius, options) 
26257     {
26258         if (this.gMapContext.circle != null) {
26259             this.gMapContext.circle.setMap(null);
26260         }
26261         if (radius > 0) {
26262             radius *= 1;
26263             options = Roo.apply({}, options, {
26264                 strokeColor: "#0000FF",
26265                 strokeOpacity: .35,
26266                 strokeWeight: 2,
26267                 fillColor: "#0000FF",
26268                 fillOpacity: .2
26269             });
26270             
26271             options.map = this.gMapContext.map;
26272             options.radius = radius;
26273             options.center = center;
26274             this.gMapContext.circle = new google.maps.Circle(options);
26275             return this.gMapContext.circle;
26276         }
26277         
26278         return null;
26279     },
26280     
26281     setPosition: function(location) 
26282     {
26283         this.gMapContext.location = location;
26284         this.gMapContext.marker.setPosition(location);
26285         this.gMapContext.map.panTo(location);
26286         this.drawCircle(location, this.gMapContext.radius, {});
26287         
26288         var _this = this;
26289         
26290         if (this.gMapContext.settings.enableReverseGeocode) {
26291             this.gMapContext.geodecoder.geocode({
26292                 latLng: this.gMapContext.location
26293             }, function(results, status) {
26294                 
26295                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26296                     _this.gMapContext.locationName = results[0].formatted_address;
26297                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26298                     
26299                     _this.fireEvent('positionchanged', this, location);
26300                 }
26301             });
26302             
26303             return;
26304         }
26305         
26306         this.fireEvent('positionchanged', this, location);
26307     },
26308     
26309     resize: function()
26310     {
26311         google.maps.event.trigger(this.gMapContext.map, "resize");
26312         
26313         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26314         
26315         this.fireEvent('resize', this);
26316     },
26317     
26318     setPositionByLatLng: function(latitude, longitude)
26319     {
26320         this.setPosition(new google.maps.LatLng(latitude, longitude));
26321     },
26322     
26323     getCurrentPosition: function() 
26324     {
26325         return {
26326             latitude: this.gMapContext.location.lat(),
26327             longitude: this.gMapContext.location.lng()
26328         };
26329     },
26330     
26331     getAddressName: function() 
26332     {
26333         return this.gMapContext.locationName;
26334     },
26335     
26336     getAddressComponents: function() 
26337     {
26338         return this.gMapContext.addressComponents;
26339     },
26340     
26341     address_component_from_google_geocode: function(address_components) 
26342     {
26343         var result = {};
26344         
26345         for (var i = 0; i < address_components.length; i++) {
26346             var component = address_components[i];
26347             if (component.types.indexOf("postal_code") >= 0) {
26348                 result.postalCode = component.short_name;
26349             } else if (component.types.indexOf("street_number") >= 0) {
26350                 result.streetNumber = component.short_name;
26351             } else if (component.types.indexOf("route") >= 0) {
26352                 result.streetName = component.short_name;
26353             } else if (component.types.indexOf("neighborhood") >= 0) {
26354                 result.city = component.short_name;
26355             } else if (component.types.indexOf("locality") >= 0) {
26356                 result.city = component.short_name;
26357             } else if (component.types.indexOf("sublocality") >= 0) {
26358                 result.district = component.short_name;
26359             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26360                 result.stateOrProvince = component.short_name;
26361             } else if (component.types.indexOf("country") >= 0) {
26362                 result.country = component.short_name;
26363             }
26364         }
26365         
26366         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26367         result.addressLine2 = "";
26368         return result;
26369     },
26370     
26371     setZoomLevel: function(zoom)
26372     {
26373         this.gMapContext.map.setZoom(zoom);
26374     },
26375     
26376     show: function()
26377     {
26378         if(!this.el){
26379             return;
26380         }
26381         
26382         this.el.show();
26383         
26384         this.resize();
26385         
26386         this.fireEvent('show', this);
26387     },
26388     
26389     hide: function()
26390     {
26391         if(!this.el){
26392             return;
26393         }
26394         
26395         this.el.hide();
26396         
26397         this.fireEvent('hide', this);
26398     }
26399     
26400 });
26401
26402 Roo.apply(Roo.bootstrap.LocationPicker, {
26403     
26404     OverlayView : function(map, options)
26405     {
26406         options = options || {};
26407         
26408         this.setMap(map);
26409     }
26410     
26411     
26412 });/*
26413  * - LGPL
26414  *
26415  * Alert
26416  * 
26417  */
26418
26419 /**
26420  * @class Roo.bootstrap.Alert
26421  * @extends Roo.bootstrap.Component
26422  * Bootstrap Alert class
26423  * @cfg {String} title The title of alert
26424  * @cfg {String} html The content of alert
26425  * @cfg {String} weight (  success | info | warning | danger )
26426  * @cfg {String} faicon font-awesomeicon
26427  * 
26428  * @constructor
26429  * Create a new alert
26430  * @param {Object} config The config object
26431  */
26432
26433
26434 Roo.bootstrap.Alert = function(config){
26435     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26436     
26437 };
26438
26439 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26440     
26441     title: '',
26442     html: '',
26443     weight: false,
26444     faicon: false,
26445     
26446     getAutoCreate : function()
26447     {
26448         
26449         var cfg = {
26450             tag : 'div',
26451             cls : 'alert',
26452             cn : [
26453                 {
26454                     tag : 'i',
26455                     cls : 'roo-alert-icon'
26456                     
26457                 },
26458                 {
26459                     tag : 'b',
26460                     cls : 'roo-alert-title',
26461                     html : this.title
26462                 },
26463                 {
26464                     tag : 'span',
26465                     cls : 'roo-alert-text',
26466                     html : this.html
26467                 }
26468             ]
26469         };
26470         
26471         if(this.faicon){
26472             cfg.cn[0].cls += ' fa ' + this.faicon;
26473         }
26474         
26475         if(this.weight){
26476             cfg.cls += ' alert-' + this.weight;
26477         }
26478         
26479         return cfg;
26480     },
26481     
26482     initEvents: function() 
26483     {
26484         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26485     },
26486     
26487     setTitle : function(str)
26488     {
26489         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26490     },
26491     
26492     setText : function(str)
26493     {
26494         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26495     },
26496     
26497     setWeight : function(weight)
26498     {
26499         if(this.weight){
26500             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26501         }
26502         
26503         this.weight = weight;
26504         
26505         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26506     },
26507     
26508     setIcon : function(icon)
26509     {
26510         if(this.faicon){
26511             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26512         }
26513         
26514         this.faicon = icon;
26515         
26516         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26517     },
26518     
26519     hide: function() 
26520     {
26521         this.el.hide();   
26522     },
26523     
26524     show: function() 
26525     {  
26526         this.el.show();   
26527     }
26528     
26529 });
26530
26531  
26532 /*
26533 * Licence: LGPL
26534 */
26535
26536 /**
26537  * @class Roo.bootstrap.UploadCropbox
26538  * @extends Roo.bootstrap.Component
26539  * Bootstrap UploadCropbox class
26540  * @cfg {String} emptyText show when image has been loaded
26541  * @cfg {String} rotateNotify show when image too small to rotate
26542  * @cfg {Number} errorTimeout default 3000
26543  * @cfg {Number} minWidth default 300
26544  * @cfg {Number} minHeight default 300
26545  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26546  * @cfg {Boolean} isDocument (true|false) default false
26547  * @cfg {String} url action url
26548  * @cfg {String} paramName default 'imageUpload'
26549  * @cfg {String} method default POST
26550  * @cfg {Boolean} loadMask (true|false) default true
26551  * @cfg {Boolean} loadingText default 'Loading...'
26552  * 
26553  * @constructor
26554  * Create a new UploadCropbox
26555  * @param {Object} config The config object
26556  */
26557
26558 Roo.bootstrap.UploadCropbox = function(config){
26559     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26560     
26561     this.addEvents({
26562         /**
26563          * @event beforeselectfile
26564          * Fire before select file
26565          * @param {Roo.bootstrap.UploadCropbox} this
26566          */
26567         "beforeselectfile" : true,
26568         /**
26569          * @event initial
26570          * Fire after initEvent
26571          * @param {Roo.bootstrap.UploadCropbox} this
26572          */
26573         "initial" : true,
26574         /**
26575          * @event crop
26576          * Fire after initEvent
26577          * @param {Roo.bootstrap.UploadCropbox} this
26578          * @param {String} data
26579          */
26580         "crop" : true,
26581         /**
26582          * @event prepare
26583          * Fire when preparing the file data
26584          * @param {Roo.bootstrap.UploadCropbox} this
26585          * @param {Object} file
26586          */
26587         "prepare" : true,
26588         /**
26589          * @event exception
26590          * Fire when get exception
26591          * @param {Roo.bootstrap.UploadCropbox} this
26592          * @param {XMLHttpRequest} xhr
26593          */
26594         "exception" : true,
26595         /**
26596          * @event beforeloadcanvas
26597          * Fire before load the canvas
26598          * @param {Roo.bootstrap.UploadCropbox} this
26599          * @param {String} src
26600          */
26601         "beforeloadcanvas" : true,
26602         /**
26603          * @event trash
26604          * Fire when trash image
26605          * @param {Roo.bootstrap.UploadCropbox} this
26606          */
26607         "trash" : true,
26608         /**
26609          * @event download
26610          * Fire when download the image
26611          * @param {Roo.bootstrap.UploadCropbox} this
26612          */
26613         "download" : true,
26614         /**
26615          * @event footerbuttonclick
26616          * Fire when footerbuttonclick
26617          * @param {Roo.bootstrap.UploadCropbox} this
26618          * @param {String} type
26619          */
26620         "footerbuttonclick" : true,
26621         /**
26622          * @event resize
26623          * Fire when resize
26624          * @param {Roo.bootstrap.UploadCropbox} this
26625          */
26626         "resize" : true,
26627         /**
26628          * @event rotate
26629          * Fire when rotate the image
26630          * @param {Roo.bootstrap.UploadCropbox} this
26631          * @param {String} pos
26632          */
26633         "rotate" : true,
26634         /**
26635          * @event inspect
26636          * Fire when inspect the file
26637          * @param {Roo.bootstrap.UploadCropbox} this
26638          * @param {Object} file
26639          */
26640         "inspect" : true,
26641         /**
26642          * @event upload
26643          * Fire when xhr upload the file
26644          * @param {Roo.bootstrap.UploadCropbox} this
26645          * @param {Object} data
26646          */
26647         "upload" : true,
26648         /**
26649          * @event arrange
26650          * Fire when arrange the file data
26651          * @param {Roo.bootstrap.UploadCropbox} this
26652          * @param {Object} formData
26653          */
26654         "arrange" : true
26655     });
26656     
26657     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26658 };
26659
26660 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26661     
26662     emptyText : 'Click to upload image',
26663     rotateNotify : 'Image is too small to rotate',
26664     errorTimeout : 3000,
26665     scale : 0,
26666     baseScale : 1,
26667     rotate : 0,
26668     dragable : false,
26669     pinching : false,
26670     mouseX : 0,
26671     mouseY : 0,
26672     cropData : false,
26673     minWidth : 300,
26674     minHeight : 300,
26675     file : false,
26676     exif : {},
26677     baseRotate : 1,
26678     cropType : 'image/jpeg',
26679     buttons : false,
26680     canvasLoaded : false,
26681     isDocument : false,
26682     method : 'POST',
26683     paramName : 'imageUpload',
26684     loadMask : true,
26685     loadingText : 'Loading...',
26686     maskEl : false,
26687     
26688     getAutoCreate : function()
26689     {
26690         var cfg = {
26691             tag : 'div',
26692             cls : 'roo-upload-cropbox',
26693             cn : [
26694                 {
26695                     tag : 'input',
26696                     cls : 'roo-upload-cropbox-selector',
26697                     type : 'file'
26698                 },
26699                 {
26700                     tag : 'div',
26701                     cls : 'roo-upload-cropbox-body',
26702                     style : 'cursor:pointer',
26703                     cn : [
26704                         {
26705                             tag : 'div',
26706                             cls : 'roo-upload-cropbox-preview'
26707                         },
26708                         {
26709                             tag : 'div',
26710                             cls : 'roo-upload-cropbox-thumb'
26711                         },
26712                         {
26713                             tag : 'div',
26714                             cls : 'roo-upload-cropbox-empty-notify',
26715                             html : this.emptyText
26716                         },
26717                         {
26718                             tag : 'div',
26719                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26720                             html : this.rotateNotify
26721                         }
26722                     ]
26723                 },
26724                 {
26725                     tag : 'div',
26726                     cls : 'roo-upload-cropbox-footer',
26727                     cn : {
26728                         tag : 'div',
26729                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26730                         cn : []
26731                     }
26732                 }
26733             ]
26734         };
26735         
26736         return cfg;
26737     },
26738     
26739     onRender : function(ct, position)
26740     {
26741         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26742         
26743         if (this.buttons.length) {
26744             
26745             Roo.each(this.buttons, function(bb) {
26746                 
26747                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26748                 
26749                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26750                 
26751             }, this);
26752         }
26753         
26754         if(this.loadMask){
26755             this.maskEl = this.el;
26756         }
26757     },
26758     
26759     initEvents : function()
26760     {
26761         this.urlAPI = (window.createObjectURL && window) || 
26762                                 (window.URL && URL.revokeObjectURL && URL) || 
26763                                 (window.webkitURL && webkitURL);
26764                         
26765         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26766         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26767         
26768         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26769         this.selectorEl.hide();
26770         
26771         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26772         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26773         
26774         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26775         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26776         this.thumbEl.hide();
26777         
26778         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26779         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26780         
26781         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26782         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26783         this.errorEl.hide();
26784         
26785         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26786         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26787         this.footerEl.hide();
26788         
26789         this.setThumbBoxSize();
26790         
26791         this.bind();
26792         
26793         this.resize();
26794         
26795         this.fireEvent('initial', this);
26796     },
26797
26798     bind : function()
26799     {
26800         var _this = this;
26801         
26802         window.addEventListener("resize", function() { _this.resize(); } );
26803         
26804         this.bodyEl.on('click', this.beforeSelectFile, this);
26805         
26806         if(Roo.isTouch){
26807             this.bodyEl.on('touchstart', this.onTouchStart, this);
26808             this.bodyEl.on('touchmove', this.onTouchMove, this);
26809             this.bodyEl.on('touchend', this.onTouchEnd, this);
26810         }
26811         
26812         if(!Roo.isTouch){
26813             this.bodyEl.on('mousedown', this.onMouseDown, this);
26814             this.bodyEl.on('mousemove', this.onMouseMove, this);
26815             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26816             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26817             Roo.get(document).on('mouseup', this.onMouseUp, this);
26818         }
26819         
26820         this.selectorEl.on('change', this.onFileSelected, this);
26821     },
26822     
26823     reset : function()
26824     {    
26825         this.scale = 0;
26826         this.baseScale = 1;
26827         this.rotate = 0;
26828         this.baseRotate = 1;
26829         this.dragable = false;
26830         this.pinching = false;
26831         this.mouseX = 0;
26832         this.mouseY = 0;
26833         this.cropData = false;
26834         this.notifyEl.dom.innerHTML = this.emptyText;
26835         
26836         this.selectorEl.dom.value = '';
26837         
26838     },
26839     
26840     resize : function()
26841     {
26842         if(this.fireEvent('resize', this) != false){
26843             this.setThumbBoxPosition();
26844             this.setCanvasPosition();
26845         }
26846     },
26847     
26848     onFooterButtonClick : function(e, el, o, type)
26849     {
26850         switch (type) {
26851             case 'rotate-left' :
26852                 this.onRotateLeft(e);
26853                 break;
26854             case 'rotate-right' :
26855                 this.onRotateRight(e);
26856                 break;
26857             case 'picture' :
26858                 this.beforeSelectFile(e);
26859                 break;
26860             case 'trash' :
26861                 this.trash(e);
26862                 break;
26863             case 'crop' :
26864                 this.crop(e);
26865                 break;
26866             case 'download' :
26867                 this.download(e);
26868                 break;
26869             default :
26870                 break;
26871         }
26872         
26873         this.fireEvent('footerbuttonclick', this, type);
26874     },
26875     
26876     beforeSelectFile : function(e)
26877     {
26878         e.preventDefault();
26879         
26880         if(this.fireEvent('beforeselectfile', this) != false){
26881             this.selectorEl.dom.click();
26882         }
26883     },
26884     
26885     onFileSelected : function(e)
26886     {
26887         e.preventDefault();
26888         
26889         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26890             return;
26891         }
26892         
26893         var file = this.selectorEl.dom.files[0];
26894         
26895         if(this.fireEvent('inspect', this, file) != false){
26896             this.prepare(file);
26897         }
26898         
26899     },
26900     
26901     trash : function(e)
26902     {
26903         this.fireEvent('trash', this);
26904     },
26905     
26906     download : function(e)
26907     {
26908         this.fireEvent('download', this);
26909     },
26910     
26911     loadCanvas : function(src)
26912     {   
26913         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26914             
26915             this.reset();
26916             
26917             this.imageEl = document.createElement('img');
26918             
26919             var _this = this;
26920             
26921             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26922             
26923             this.imageEl.src = src;
26924         }
26925     },
26926     
26927     onLoadCanvas : function()
26928     {   
26929         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26930         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26931         
26932         this.bodyEl.un('click', this.beforeSelectFile, this);
26933         
26934         this.notifyEl.hide();
26935         this.thumbEl.show();
26936         this.footerEl.show();
26937         
26938         this.baseRotateLevel();
26939         
26940         if(this.isDocument){
26941             this.setThumbBoxSize();
26942         }
26943         
26944         this.setThumbBoxPosition();
26945         
26946         this.baseScaleLevel();
26947         
26948         this.draw();
26949         
26950         this.resize();
26951         
26952         this.canvasLoaded = true;
26953         
26954         if(this.loadMask){
26955             this.maskEl.unmask();
26956         }
26957         
26958     },
26959     
26960     setCanvasPosition : function()
26961     {   
26962         if(!this.canvasEl){
26963             return;
26964         }
26965         
26966         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26967         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26968         
26969         this.previewEl.setLeft(pw);
26970         this.previewEl.setTop(ph);
26971         
26972     },
26973     
26974     onMouseDown : function(e)
26975     {   
26976         e.stopEvent();
26977         
26978         this.dragable = true;
26979         this.pinching = false;
26980         
26981         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26982             this.dragable = false;
26983             return;
26984         }
26985         
26986         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26987         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26988         
26989     },
26990     
26991     onMouseMove : function(e)
26992     {   
26993         e.stopEvent();
26994         
26995         if(!this.canvasLoaded){
26996             return;
26997         }
26998         
26999         if (!this.dragable){
27000             return;
27001         }
27002         
27003         var minX = Math.ceil(this.thumbEl.getLeft(true));
27004         var minY = Math.ceil(this.thumbEl.getTop(true));
27005         
27006         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27007         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27008         
27009         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27010         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27011         
27012         x = x - this.mouseX;
27013         y = y - this.mouseY;
27014         
27015         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27016         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27017         
27018         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27019         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27020         
27021         this.previewEl.setLeft(bgX);
27022         this.previewEl.setTop(bgY);
27023         
27024         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27025         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27026     },
27027     
27028     onMouseUp : function(e)
27029     {   
27030         e.stopEvent();
27031         
27032         this.dragable = false;
27033     },
27034     
27035     onMouseWheel : function(e)
27036     {   
27037         e.stopEvent();
27038         
27039         this.startScale = this.scale;
27040         
27041         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27042         
27043         if(!this.zoomable()){
27044             this.scale = this.startScale;
27045             return;
27046         }
27047         
27048         this.draw();
27049         
27050         return;
27051     },
27052     
27053     zoomable : function()
27054     {
27055         var minScale = this.thumbEl.getWidth() / this.minWidth;
27056         
27057         if(this.minWidth < this.minHeight){
27058             minScale = this.thumbEl.getHeight() / this.minHeight;
27059         }
27060         
27061         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27062         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27063         
27064         if(
27065                 this.isDocument &&
27066                 (this.rotate == 0 || this.rotate == 180) && 
27067                 (
27068                     width > this.imageEl.OriginWidth || 
27069                     height > this.imageEl.OriginHeight ||
27070                     (width < this.minWidth && height < this.minHeight)
27071                 )
27072         ){
27073             return false;
27074         }
27075         
27076         if(
27077                 this.isDocument &&
27078                 (this.rotate == 90 || this.rotate == 270) && 
27079                 (
27080                     width > this.imageEl.OriginWidth || 
27081                     height > this.imageEl.OriginHeight ||
27082                     (width < this.minHeight && height < this.minWidth)
27083                 )
27084         ){
27085             return false;
27086         }
27087         
27088         if(
27089                 !this.isDocument &&
27090                 (this.rotate == 0 || this.rotate == 180) && 
27091                 (
27092                     width < this.minWidth || 
27093                     width > this.imageEl.OriginWidth || 
27094                     height < this.minHeight || 
27095                     height > this.imageEl.OriginHeight
27096                 )
27097         ){
27098             return false;
27099         }
27100         
27101         if(
27102                 !this.isDocument &&
27103                 (this.rotate == 90 || this.rotate == 270) && 
27104                 (
27105                     width < this.minHeight || 
27106                     width > this.imageEl.OriginWidth || 
27107                     height < this.minWidth || 
27108                     height > this.imageEl.OriginHeight
27109                 )
27110         ){
27111             return false;
27112         }
27113         
27114         return true;
27115         
27116     },
27117     
27118     onRotateLeft : function(e)
27119     {   
27120         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27121             
27122             var minScale = this.thumbEl.getWidth() / this.minWidth;
27123             
27124             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27125             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27126             
27127             this.startScale = this.scale;
27128             
27129             while (this.getScaleLevel() < minScale){
27130             
27131                 this.scale = this.scale + 1;
27132                 
27133                 if(!this.zoomable()){
27134                     break;
27135                 }
27136                 
27137                 if(
27138                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27139                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27140                 ){
27141                     continue;
27142                 }
27143                 
27144                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27145
27146                 this.draw();
27147                 
27148                 return;
27149             }
27150             
27151             this.scale = this.startScale;
27152             
27153             this.onRotateFail();
27154             
27155             return false;
27156         }
27157         
27158         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27159
27160         if(this.isDocument){
27161             this.setThumbBoxSize();
27162             this.setThumbBoxPosition();
27163             this.setCanvasPosition();
27164         }
27165         
27166         this.draw();
27167         
27168         this.fireEvent('rotate', this, 'left');
27169         
27170     },
27171     
27172     onRotateRight : function(e)
27173     {
27174         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27175             
27176             var minScale = this.thumbEl.getWidth() / this.minWidth;
27177         
27178             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27179             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27180             
27181             this.startScale = this.scale;
27182             
27183             while (this.getScaleLevel() < minScale){
27184             
27185                 this.scale = this.scale + 1;
27186                 
27187                 if(!this.zoomable()){
27188                     break;
27189                 }
27190                 
27191                 if(
27192                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27193                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27194                 ){
27195                     continue;
27196                 }
27197                 
27198                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27199
27200                 this.draw();
27201                 
27202                 return;
27203             }
27204             
27205             this.scale = this.startScale;
27206             
27207             this.onRotateFail();
27208             
27209             return false;
27210         }
27211         
27212         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27213
27214         if(this.isDocument){
27215             this.setThumbBoxSize();
27216             this.setThumbBoxPosition();
27217             this.setCanvasPosition();
27218         }
27219         
27220         this.draw();
27221         
27222         this.fireEvent('rotate', this, 'right');
27223     },
27224     
27225     onRotateFail : function()
27226     {
27227         this.errorEl.show(true);
27228         
27229         var _this = this;
27230         
27231         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27232     },
27233     
27234     draw : function()
27235     {
27236         this.previewEl.dom.innerHTML = '';
27237         
27238         var canvasEl = document.createElement("canvas");
27239         
27240         var contextEl = canvasEl.getContext("2d");
27241         
27242         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27243         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27244         var center = this.imageEl.OriginWidth / 2;
27245         
27246         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27247             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27248             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27249             center = this.imageEl.OriginHeight / 2;
27250         }
27251         
27252         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27253         
27254         contextEl.translate(center, center);
27255         contextEl.rotate(this.rotate * Math.PI / 180);
27256
27257         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27258         
27259         this.canvasEl = document.createElement("canvas");
27260         
27261         this.contextEl = this.canvasEl.getContext("2d");
27262         
27263         switch (this.rotate) {
27264             case 0 :
27265                 
27266                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27267                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27268                 
27269                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27270                 
27271                 break;
27272             case 90 : 
27273                 
27274                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27275                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27276                 
27277                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27278                     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);
27279                     break;
27280                 }
27281                 
27282                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27283                 
27284                 break;
27285             case 180 :
27286                 
27287                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27288                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27289                 
27290                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27291                     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);
27292                     break;
27293                 }
27294                 
27295                 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);
27296                 
27297                 break;
27298             case 270 :
27299                 
27300                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27301                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27302         
27303                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27304                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27305                     break;
27306                 }
27307                 
27308                 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);
27309                 
27310                 break;
27311             default : 
27312                 break;
27313         }
27314         
27315         this.previewEl.appendChild(this.canvasEl);
27316         
27317         this.setCanvasPosition();
27318     },
27319     
27320     crop : function()
27321     {
27322         if(!this.canvasLoaded){
27323             return;
27324         }
27325         
27326         var imageCanvas = document.createElement("canvas");
27327         
27328         var imageContext = imageCanvas.getContext("2d");
27329         
27330         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27331         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27332         
27333         var center = imageCanvas.width / 2;
27334         
27335         imageContext.translate(center, center);
27336         
27337         imageContext.rotate(this.rotate * Math.PI / 180);
27338         
27339         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27340         
27341         var canvas = document.createElement("canvas");
27342         
27343         var context = canvas.getContext("2d");
27344                 
27345         canvas.width = this.minWidth;
27346         canvas.height = this.minHeight;
27347
27348         switch (this.rotate) {
27349             case 0 :
27350                 
27351                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27352                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27353                 
27354                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27355                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27356                 
27357                 var targetWidth = this.minWidth - 2 * x;
27358                 var targetHeight = this.minHeight - 2 * y;
27359                 
27360                 var scale = 1;
27361                 
27362                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27363                     scale = targetWidth / width;
27364                 }
27365                 
27366                 if(x > 0 && y == 0){
27367                     scale = targetHeight / height;
27368                 }
27369                 
27370                 if(x > 0 && y > 0){
27371                     scale = targetWidth / width;
27372                     
27373                     if(width < height){
27374                         scale = targetHeight / height;
27375                     }
27376                 }
27377                 
27378                 context.scale(scale, scale);
27379                 
27380                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27381                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27382
27383                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27384                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27385
27386                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27387                 
27388                 break;
27389             case 90 : 
27390                 
27391                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27392                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27393                 
27394                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27395                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27396                 
27397                 var targetWidth = this.minWidth - 2 * x;
27398                 var targetHeight = this.minHeight - 2 * y;
27399                 
27400                 var scale = 1;
27401                 
27402                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27403                     scale = targetWidth / width;
27404                 }
27405                 
27406                 if(x > 0 && y == 0){
27407                     scale = targetHeight / height;
27408                 }
27409                 
27410                 if(x > 0 && y > 0){
27411                     scale = targetWidth / width;
27412                     
27413                     if(width < height){
27414                         scale = targetHeight / height;
27415                     }
27416                 }
27417                 
27418                 context.scale(scale, scale);
27419                 
27420                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27421                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27422
27423                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27424                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27425                 
27426                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27427                 
27428                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27429                 
27430                 break;
27431             case 180 :
27432                 
27433                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27434                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27435                 
27436                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27437                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27438                 
27439                 var targetWidth = this.minWidth - 2 * x;
27440                 var targetHeight = this.minHeight - 2 * y;
27441                 
27442                 var scale = 1;
27443                 
27444                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27445                     scale = targetWidth / width;
27446                 }
27447                 
27448                 if(x > 0 && y == 0){
27449                     scale = targetHeight / height;
27450                 }
27451                 
27452                 if(x > 0 && y > 0){
27453                     scale = targetWidth / width;
27454                     
27455                     if(width < height){
27456                         scale = targetHeight / height;
27457                     }
27458                 }
27459                 
27460                 context.scale(scale, scale);
27461                 
27462                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27463                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27464
27465                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27466                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27467
27468                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27469                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27470                 
27471                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27472                 
27473                 break;
27474             case 270 :
27475                 
27476                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27477                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27478                 
27479                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27480                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27481                 
27482                 var targetWidth = this.minWidth - 2 * x;
27483                 var targetHeight = this.minHeight - 2 * y;
27484                 
27485                 var scale = 1;
27486                 
27487                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27488                     scale = targetWidth / width;
27489                 }
27490                 
27491                 if(x > 0 && y == 0){
27492                     scale = targetHeight / height;
27493                 }
27494                 
27495                 if(x > 0 && y > 0){
27496                     scale = targetWidth / width;
27497                     
27498                     if(width < height){
27499                         scale = targetHeight / height;
27500                     }
27501                 }
27502                 
27503                 context.scale(scale, scale);
27504                 
27505                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27506                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27507
27508                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27509                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27510                 
27511                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27512                 
27513                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27514                 
27515                 break;
27516             default : 
27517                 break;
27518         }
27519         
27520         this.cropData = canvas.toDataURL(this.cropType);
27521         
27522         if(this.fireEvent('crop', this, this.cropData) !== false){
27523             this.process(this.file, this.cropData);
27524         }
27525         
27526         return;
27527         
27528     },
27529     
27530     setThumbBoxSize : function()
27531     {
27532         var width, height;
27533         
27534         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27535             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27536             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27537             
27538             this.minWidth = width;
27539             this.minHeight = height;
27540             
27541             if(this.rotate == 90 || this.rotate == 270){
27542                 this.minWidth = height;
27543                 this.minHeight = width;
27544             }
27545         }
27546         
27547         height = 300;
27548         width = Math.ceil(this.minWidth * height / this.minHeight);
27549         
27550         if(this.minWidth > this.minHeight){
27551             width = 300;
27552             height = Math.ceil(this.minHeight * width / this.minWidth);
27553         }
27554         
27555         this.thumbEl.setStyle({
27556             width : width + 'px',
27557             height : height + 'px'
27558         });
27559
27560         return;
27561             
27562     },
27563     
27564     setThumbBoxPosition : function()
27565     {
27566         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27567         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27568         
27569         this.thumbEl.setLeft(x);
27570         this.thumbEl.setTop(y);
27571         
27572     },
27573     
27574     baseRotateLevel : function()
27575     {
27576         this.baseRotate = 1;
27577         
27578         if(
27579                 typeof(this.exif) != 'undefined' &&
27580                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27581                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27582         ){
27583             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27584         }
27585         
27586         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27587         
27588     },
27589     
27590     baseScaleLevel : function()
27591     {
27592         var width, height;
27593         
27594         if(this.isDocument){
27595             
27596             if(this.baseRotate == 6 || this.baseRotate == 8){
27597             
27598                 height = this.thumbEl.getHeight();
27599                 this.baseScale = height / this.imageEl.OriginWidth;
27600
27601                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27602                     width = this.thumbEl.getWidth();
27603                     this.baseScale = width / this.imageEl.OriginHeight;
27604                 }
27605
27606                 return;
27607             }
27608
27609             height = this.thumbEl.getHeight();
27610             this.baseScale = height / this.imageEl.OriginHeight;
27611
27612             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27613                 width = this.thumbEl.getWidth();
27614                 this.baseScale = width / this.imageEl.OriginWidth;
27615             }
27616
27617             return;
27618         }
27619         
27620         if(this.baseRotate == 6 || this.baseRotate == 8){
27621             
27622             width = this.thumbEl.getHeight();
27623             this.baseScale = width / this.imageEl.OriginHeight;
27624             
27625             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27626                 height = this.thumbEl.getWidth();
27627                 this.baseScale = height / this.imageEl.OriginHeight;
27628             }
27629             
27630             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27631                 height = this.thumbEl.getWidth();
27632                 this.baseScale = height / this.imageEl.OriginHeight;
27633                 
27634                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27635                     width = this.thumbEl.getHeight();
27636                     this.baseScale = width / this.imageEl.OriginWidth;
27637                 }
27638             }
27639             
27640             return;
27641         }
27642         
27643         width = this.thumbEl.getWidth();
27644         this.baseScale = width / this.imageEl.OriginWidth;
27645         
27646         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27647             height = this.thumbEl.getHeight();
27648             this.baseScale = height / this.imageEl.OriginHeight;
27649         }
27650         
27651         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27652             
27653             height = this.thumbEl.getHeight();
27654             this.baseScale = height / this.imageEl.OriginHeight;
27655             
27656             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27657                 width = this.thumbEl.getWidth();
27658                 this.baseScale = width / this.imageEl.OriginWidth;
27659             }
27660             
27661         }
27662         
27663         return;
27664     },
27665     
27666     getScaleLevel : function()
27667     {
27668         return this.baseScale * Math.pow(1.1, this.scale);
27669     },
27670     
27671     onTouchStart : function(e)
27672     {
27673         if(!this.canvasLoaded){
27674             this.beforeSelectFile(e);
27675             return;
27676         }
27677         
27678         var touches = e.browserEvent.touches;
27679         
27680         if(!touches){
27681             return;
27682         }
27683         
27684         if(touches.length == 1){
27685             this.onMouseDown(e);
27686             return;
27687         }
27688         
27689         if(touches.length != 2){
27690             return;
27691         }
27692         
27693         var coords = [];
27694         
27695         for(var i = 0, finger; finger = touches[i]; i++){
27696             coords.push(finger.pageX, finger.pageY);
27697         }
27698         
27699         var x = Math.pow(coords[0] - coords[2], 2);
27700         var y = Math.pow(coords[1] - coords[3], 2);
27701         
27702         this.startDistance = Math.sqrt(x + y);
27703         
27704         this.startScale = this.scale;
27705         
27706         this.pinching = true;
27707         this.dragable = false;
27708         
27709     },
27710     
27711     onTouchMove : function(e)
27712     {
27713         if(!this.pinching && !this.dragable){
27714             return;
27715         }
27716         
27717         var touches = e.browserEvent.touches;
27718         
27719         if(!touches){
27720             return;
27721         }
27722         
27723         if(this.dragable){
27724             this.onMouseMove(e);
27725             return;
27726         }
27727         
27728         var coords = [];
27729         
27730         for(var i = 0, finger; finger = touches[i]; i++){
27731             coords.push(finger.pageX, finger.pageY);
27732         }
27733         
27734         var x = Math.pow(coords[0] - coords[2], 2);
27735         var y = Math.pow(coords[1] - coords[3], 2);
27736         
27737         this.endDistance = Math.sqrt(x + y);
27738         
27739         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27740         
27741         if(!this.zoomable()){
27742             this.scale = this.startScale;
27743             return;
27744         }
27745         
27746         this.draw();
27747         
27748     },
27749     
27750     onTouchEnd : function(e)
27751     {
27752         this.pinching = false;
27753         this.dragable = false;
27754         
27755     },
27756     
27757     process : function(file, crop)
27758     {
27759         if(this.loadMask){
27760             this.maskEl.mask(this.loadingText);
27761         }
27762         
27763         this.xhr = new XMLHttpRequest();
27764         
27765         file.xhr = this.xhr;
27766
27767         this.xhr.open(this.method, this.url, true);
27768         
27769         var headers = {
27770             "Accept": "application/json",
27771             "Cache-Control": "no-cache",
27772             "X-Requested-With": "XMLHttpRequest"
27773         };
27774         
27775         for (var headerName in headers) {
27776             var headerValue = headers[headerName];
27777             if (headerValue) {
27778                 this.xhr.setRequestHeader(headerName, headerValue);
27779             }
27780         }
27781         
27782         var _this = this;
27783         
27784         this.xhr.onload = function()
27785         {
27786             _this.xhrOnLoad(_this.xhr);
27787         }
27788         
27789         this.xhr.onerror = function()
27790         {
27791             _this.xhrOnError(_this.xhr);
27792         }
27793         
27794         var formData = new FormData();
27795
27796         formData.append('returnHTML', 'NO');
27797         
27798         if(crop){
27799             formData.append('crop', crop);
27800         }
27801         
27802         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27803             formData.append(this.paramName, file, file.name);
27804         }
27805         
27806         if(typeof(file.filename) != 'undefined'){
27807             formData.append('filename', file.filename);
27808         }
27809         
27810         if(typeof(file.mimetype) != 'undefined'){
27811             formData.append('mimetype', file.mimetype);
27812         }
27813         
27814         if(this.fireEvent('arrange', this, formData) != false){
27815             this.xhr.send(formData);
27816         };
27817     },
27818     
27819     xhrOnLoad : function(xhr)
27820     {
27821         if(this.loadMask){
27822             this.maskEl.unmask();
27823         }
27824         
27825         if (xhr.readyState !== 4) {
27826             this.fireEvent('exception', this, xhr);
27827             return;
27828         }
27829
27830         var response = Roo.decode(xhr.responseText);
27831         
27832         if(!response.success){
27833             this.fireEvent('exception', this, xhr);
27834             return;
27835         }
27836         
27837         var response = Roo.decode(xhr.responseText);
27838         
27839         this.fireEvent('upload', this, response);
27840         
27841     },
27842     
27843     xhrOnError : function()
27844     {
27845         if(this.loadMask){
27846             this.maskEl.unmask();
27847         }
27848         
27849         Roo.log('xhr on error');
27850         
27851         var response = Roo.decode(xhr.responseText);
27852           
27853         Roo.log(response);
27854         
27855     },
27856     
27857     prepare : function(file)
27858     {   
27859         if(this.loadMask){
27860             this.maskEl.mask(this.loadingText);
27861         }
27862         
27863         this.file = false;
27864         this.exif = {};
27865         
27866         if(typeof(file) === 'string'){
27867             this.loadCanvas(file);
27868             return;
27869         }
27870         
27871         if(!file || !this.urlAPI){
27872             return;
27873         }
27874         
27875         this.file = file;
27876         this.cropType = file.type;
27877         
27878         var _this = this;
27879         
27880         if(this.fireEvent('prepare', this, this.file) != false){
27881             
27882             var reader = new FileReader();
27883             
27884             reader.onload = function (e) {
27885                 if (e.target.error) {
27886                     Roo.log(e.target.error);
27887                     return;
27888                 }
27889                 
27890                 var buffer = e.target.result,
27891                     dataView = new DataView(buffer),
27892                     offset = 2,
27893                     maxOffset = dataView.byteLength - 4,
27894                     markerBytes,
27895                     markerLength;
27896                 
27897                 if (dataView.getUint16(0) === 0xffd8) {
27898                     while (offset < maxOffset) {
27899                         markerBytes = dataView.getUint16(offset);
27900                         
27901                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27902                             markerLength = dataView.getUint16(offset + 2) + 2;
27903                             if (offset + markerLength > dataView.byteLength) {
27904                                 Roo.log('Invalid meta data: Invalid segment size.');
27905                                 break;
27906                             }
27907                             
27908                             if(markerBytes == 0xffe1){
27909                                 _this.parseExifData(
27910                                     dataView,
27911                                     offset,
27912                                     markerLength
27913                                 );
27914                             }
27915                             
27916                             offset += markerLength;
27917                             
27918                             continue;
27919                         }
27920                         
27921                         break;
27922                     }
27923                     
27924                 }
27925                 
27926                 var url = _this.urlAPI.createObjectURL(_this.file);
27927                 
27928                 _this.loadCanvas(url);
27929                 
27930                 return;
27931             }
27932             
27933             reader.readAsArrayBuffer(this.file);
27934             
27935         }
27936         
27937     },
27938     
27939     parseExifData : function(dataView, offset, length)
27940     {
27941         var tiffOffset = offset + 10,
27942             littleEndian,
27943             dirOffset;
27944     
27945         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27946             // No Exif data, might be XMP data instead
27947             return;
27948         }
27949         
27950         // Check for the ASCII code for "Exif" (0x45786966):
27951         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27952             // No Exif data, might be XMP data instead
27953             return;
27954         }
27955         if (tiffOffset + 8 > dataView.byteLength) {
27956             Roo.log('Invalid Exif data: Invalid segment size.');
27957             return;
27958         }
27959         // Check for the two null bytes:
27960         if (dataView.getUint16(offset + 8) !== 0x0000) {
27961             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27962             return;
27963         }
27964         // Check the byte alignment:
27965         switch (dataView.getUint16(tiffOffset)) {
27966         case 0x4949:
27967             littleEndian = true;
27968             break;
27969         case 0x4D4D:
27970             littleEndian = false;
27971             break;
27972         default:
27973             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27974             return;
27975         }
27976         // Check for the TIFF tag marker (0x002A):
27977         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27978             Roo.log('Invalid Exif data: Missing TIFF marker.');
27979             return;
27980         }
27981         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27982         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27983         
27984         this.parseExifTags(
27985             dataView,
27986             tiffOffset,
27987             tiffOffset + dirOffset,
27988             littleEndian
27989         );
27990     },
27991     
27992     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27993     {
27994         var tagsNumber,
27995             dirEndOffset,
27996             i;
27997         if (dirOffset + 6 > dataView.byteLength) {
27998             Roo.log('Invalid Exif data: Invalid directory offset.');
27999             return;
28000         }
28001         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28002         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28003         if (dirEndOffset + 4 > dataView.byteLength) {
28004             Roo.log('Invalid Exif data: Invalid directory size.');
28005             return;
28006         }
28007         for (i = 0; i < tagsNumber; i += 1) {
28008             this.parseExifTag(
28009                 dataView,
28010                 tiffOffset,
28011                 dirOffset + 2 + 12 * i, // tag offset
28012                 littleEndian
28013             );
28014         }
28015         // Return the offset to the next directory:
28016         return dataView.getUint32(dirEndOffset, littleEndian);
28017     },
28018     
28019     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28020     {
28021         var tag = dataView.getUint16(offset, littleEndian);
28022         
28023         this.exif[tag] = this.getExifValue(
28024             dataView,
28025             tiffOffset,
28026             offset,
28027             dataView.getUint16(offset + 2, littleEndian), // tag type
28028             dataView.getUint32(offset + 4, littleEndian), // tag length
28029             littleEndian
28030         );
28031     },
28032     
28033     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28034     {
28035         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28036             tagSize,
28037             dataOffset,
28038             values,
28039             i,
28040             str,
28041             c;
28042     
28043         if (!tagType) {
28044             Roo.log('Invalid Exif data: Invalid tag type.');
28045             return;
28046         }
28047         
28048         tagSize = tagType.size * length;
28049         // Determine if the value is contained in the dataOffset bytes,
28050         // or if the value at the dataOffset is a pointer to the actual data:
28051         dataOffset = tagSize > 4 ?
28052                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28053         if (dataOffset + tagSize > dataView.byteLength) {
28054             Roo.log('Invalid Exif data: Invalid data offset.');
28055             return;
28056         }
28057         if (length === 1) {
28058             return tagType.getValue(dataView, dataOffset, littleEndian);
28059         }
28060         values = [];
28061         for (i = 0; i < length; i += 1) {
28062             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28063         }
28064         
28065         if (tagType.ascii) {
28066             str = '';
28067             // Concatenate the chars:
28068             for (i = 0; i < values.length; i += 1) {
28069                 c = values[i];
28070                 // Ignore the terminating NULL byte(s):
28071                 if (c === '\u0000') {
28072                     break;
28073                 }
28074                 str += c;
28075             }
28076             return str;
28077         }
28078         return values;
28079     }
28080     
28081 });
28082
28083 Roo.apply(Roo.bootstrap.UploadCropbox, {
28084     tags : {
28085         'Orientation': 0x0112
28086     },
28087     
28088     Orientation: {
28089             1: 0, //'top-left',
28090 //            2: 'top-right',
28091             3: 180, //'bottom-right',
28092 //            4: 'bottom-left',
28093 //            5: 'left-top',
28094             6: 90, //'right-top',
28095 //            7: 'right-bottom',
28096             8: 270 //'left-bottom'
28097     },
28098     
28099     exifTagTypes : {
28100         // byte, 8-bit unsigned int:
28101         1: {
28102             getValue: function (dataView, dataOffset) {
28103                 return dataView.getUint8(dataOffset);
28104             },
28105             size: 1
28106         },
28107         // ascii, 8-bit byte:
28108         2: {
28109             getValue: function (dataView, dataOffset) {
28110                 return String.fromCharCode(dataView.getUint8(dataOffset));
28111             },
28112             size: 1,
28113             ascii: true
28114         },
28115         // short, 16 bit int:
28116         3: {
28117             getValue: function (dataView, dataOffset, littleEndian) {
28118                 return dataView.getUint16(dataOffset, littleEndian);
28119             },
28120             size: 2
28121         },
28122         // long, 32 bit int:
28123         4: {
28124             getValue: function (dataView, dataOffset, littleEndian) {
28125                 return dataView.getUint32(dataOffset, littleEndian);
28126             },
28127             size: 4
28128         },
28129         // rational = two long values, first is numerator, second is denominator:
28130         5: {
28131             getValue: function (dataView, dataOffset, littleEndian) {
28132                 return dataView.getUint32(dataOffset, littleEndian) /
28133                     dataView.getUint32(dataOffset + 4, littleEndian);
28134             },
28135             size: 8
28136         },
28137         // slong, 32 bit signed int:
28138         9: {
28139             getValue: function (dataView, dataOffset, littleEndian) {
28140                 return dataView.getInt32(dataOffset, littleEndian);
28141             },
28142             size: 4
28143         },
28144         // srational, two slongs, first is numerator, second is denominator:
28145         10: {
28146             getValue: function (dataView, dataOffset, littleEndian) {
28147                 return dataView.getInt32(dataOffset, littleEndian) /
28148                     dataView.getInt32(dataOffset + 4, littleEndian);
28149             },
28150             size: 8
28151         }
28152     },
28153     
28154     footer : {
28155         STANDARD : [
28156             {
28157                 tag : 'div',
28158                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28159                 action : 'rotate-left',
28160                 cn : [
28161                     {
28162                         tag : 'button',
28163                         cls : 'btn btn-default',
28164                         html : '<i class="fa fa-undo"></i>'
28165                     }
28166                 ]
28167             },
28168             {
28169                 tag : 'div',
28170                 cls : 'btn-group roo-upload-cropbox-picture',
28171                 action : 'picture',
28172                 cn : [
28173                     {
28174                         tag : 'button',
28175                         cls : 'btn btn-default',
28176                         html : '<i class="fa fa-picture-o"></i>'
28177                     }
28178                 ]
28179             },
28180             {
28181                 tag : 'div',
28182                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28183                 action : 'rotate-right',
28184                 cn : [
28185                     {
28186                         tag : 'button',
28187                         cls : 'btn btn-default',
28188                         html : '<i class="fa fa-repeat"></i>'
28189                     }
28190                 ]
28191             }
28192         ],
28193         DOCUMENT : [
28194             {
28195                 tag : 'div',
28196                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28197                 action : 'rotate-left',
28198                 cn : [
28199                     {
28200                         tag : 'button',
28201                         cls : 'btn btn-default',
28202                         html : '<i class="fa fa-undo"></i>'
28203                     }
28204                 ]
28205             },
28206             {
28207                 tag : 'div',
28208                 cls : 'btn-group roo-upload-cropbox-download',
28209                 action : 'download',
28210                 cn : [
28211                     {
28212                         tag : 'button',
28213                         cls : 'btn btn-default',
28214                         html : '<i class="fa fa-download"></i>'
28215                     }
28216                 ]
28217             },
28218             {
28219                 tag : 'div',
28220                 cls : 'btn-group roo-upload-cropbox-crop',
28221                 action : 'crop',
28222                 cn : [
28223                     {
28224                         tag : 'button',
28225                         cls : 'btn btn-default',
28226                         html : '<i class="fa fa-crop"></i>'
28227                     }
28228                 ]
28229             },
28230             {
28231                 tag : 'div',
28232                 cls : 'btn-group roo-upload-cropbox-trash',
28233                 action : 'trash',
28234                 cn : [
28235                     {
28236                         tag : 'button',
28237                         cls : 'btn btn-default',
28238                         html : '<i class="fa fa-trash"></i>'
28239                     }
28240                 ]
28241             },
28242             {
28243                 tag : 'div',
28244                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28245                 action : 'rotate-right',
28246                 cn : [
28247                     {
28248                         tag : 'button',
28249                         cls : 'btn btn-default',
28250                         html : '<i class="fa fa-repeat"></i>'
28251                     }
28252                 ]
28253             }
28254         ],
28255         ROTATOR : [
28256             {
28257                 tag : 'div',
28258                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28259                 action : 'rotate-left',
28260                 cn : [
28261                     {
28262                         tag : 'button',
28263                         cls : 'btn btn-default',
28264                         html : '<i class="fa fa-undo"></i>'
28265                     }
28266                 ]
28267             },
28268             {
28269                 tag : 'div',
28270                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28271                 action : 'rotate-right',
28272                 cn : [
28273                     {
28274                         tag : 'button',
28275                         cls : 'btn btn-default',
28276                         html : '<i class="fa fa-repeat"></i>'
28277                     }
28278                 ]
28279             }
28280         ]
28281     }
28282 });
28283
28284 /*
28285 * Licence: LGPL
28286 */
28287
28288 /**
28289  * @class Roo.bootstrap.DocumentManager
28290  * @extends Roo.bootstrap.Component
28291  * Bootstrap DocumentManager class
28292  * @cfg {String} paramName default 'imageUpload'
28293  * @cfg {String} toolTipName default 'filename'
28294  * @cfg {String} method default POST
28295  * @cfg {String} url action url
28296  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28297  * @cfg {Boolean} multiple multiple upload default true
28298  * @cfg {Number} thumbSize default 300
28299  * @cfg {String} fieldLabel
28300  * @cfg {Number} labelWidth default 4
28301  * @cfg {String} labelAlign (left|top) default left
28302  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28303 * @cfg {Number} labellg set the width of label (1-12)
28304  * @cfg {Number} labelmd set the width of label (1-12)
28305  * @cfg {Number} labelsm set the width of label (1-12)
28306  * @cfg {Number} labelxs set the width of label (1-12)
28307  * 
28308  * @constructor
28309  * Create a new DocumentManager
28310  * @param {Object} config The config object
28311  */
28312
28313 Roo.bootstrap.DocumentManager = function(config){
28314     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28315     
28316     this.files = [];
28317     this.delegates = [];
28318     
28319     this.addEvents({
28320         /**
28321          * @event initial
28322          * Fire when initial the DocumentManager
28323          * @param {Roo.bootstrap.DocumentManager} this
28324          */
28325         "initial" : true,
28326         /**
28327          * @event inspect
28328          * inspect selected file
28329          * @param {Roo.bootstrap.DocumentManager} this
28330          * @param {File} file
28331          */
28332         "inspect" : true,
28333         /**
28334          * @event exception
28335          * Fire when xhr load exception
28336          * @param {Roo.bootstrap.DocumentManager} this
28337          * @param {XMLHttpRequest} xhr
28338          */
28339         "exception" : true,
28340         /**
28341          * @event afterupload
28342          * Fire when xhr load exception
28343          * @param {Roo.bootstrap.DocumentManager} this
28344          * @param {XMLHttpRequest} xhr
28345          */
28346         "afterupload" : true,
28347         /**
28348          * @event prepare
28349          * prepare the form data
28350          * @param {Roo.bootstrap.DocumentManager} this
28351          * @param {Object} formData
28352          */
28353         "prepare" : true,
28354         /**
28355          * @event remove
28356          * Fire when remove the file
28357          * @param {Roo.bootstrap.DocumentManager} this
28358          * @param {Object} file
28359          */
28360         "remove" : true,
28361         /**
28362          * @event refresh
28363          * Fire after refresh the file
28364          * @param {Roo.bootstrap.DocumentManager} this
28365          */
28366         "refresh" : true,
28367         /**
28368          * @event click
28369          * Fire after click the image
28370          * @param {Roo.bootstrap.DocumentManager} this
28371          * @param {Object} file
28372          */
28373         "click" : true,
28374         /**
28375          * @event edit
28376          * Fire when upload a image and editable set to true
28377          * @param {Roo.bootstrap.DocumentManager} this
28378          * @param {Object} file
28379          */
28380         "edit" : true,
28381         /**
28382          * @event beforeselectfile
28383          * Fire before select file
28384          * @param {Roo.bootstrap.DocumentManager} this
28385          */
28386         "beforeselectfile" : true,
28387         /**
28388          * @event process
28389          * Fire before process file
28390          * @param {Roo.bootstrap.DocumentManager} this
28391          * @param {Object} file
28392          */
28393         "process" : true
28394         
28395     });
28396 };
28397
28398 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28399     
28400     boxes : 0,
28401     inputName : '',
28402     thumbSize : 300,
28403     multiple : true,
28404     files : false,
28405     method : 'POST',
28406     url : '',
28407     paramName : 'imageUpload',
28408     toolTipName : 'filename',
28409     fieldLabel : '',
28410     labelWidth : 4,
28411     labelAlign : 'left',
28412     editable : true,
28413     delegates : false,
28414     xhr : false, 
28415     
28416     labellg : 0,
28417     labelmd : 0,
28418     labelsm : 0,
28419     labelxs : 0,
28420     
28421     getAutoCreate : function()
28422     {   
28423         var managerWidget = {
28424             tag : 'div',
28425             cls : 'roo-document-manager',
28426             cn : [
28427                 {
28428                     tag : 'input',
28429                     cls : 'roo-document-manager-selector',
28430                     type : 'file'
28431                 },
28432                 {
28433                     tag : 'div',
28434                     cls : 'roo-document-manager-uploader',
28435                     cn : [
28436                         {
28437                             tag : 'div',
28438                             cls : 'roo-document-manager-upload-btn',
28439                             html : '<i class="fa fa-plus"></i>'
28440                         }
28441                     ]
28442                     
28443                 }
28444             ]
28445         };
28446         
28447         var content = [
28448             {
28449                 tag : 'div',
28450                 cls : 'column col-md-12',
28451                 cn : managerWidget
28452             }
28453         ];
28454         
28455         if(this.fieldLabel.length){
28456             
28457             content = [
28458                 {
28459                     tag : 'div',
28460                     cls : 'column col-md-12',
28461                     html : this.fieldLabel
28462                 },
28463                 {
28464                     tag : 'div',
28465                     cls : 'column col-md-12',
28466                     cn : managerWidget
28467                 }
28468             ];
28469
28470             if(this.labelAlign == 'left'){
28471                 content = [
28472                     {
28473                         tag : 'div',
28474                         cls : 'column',
28475                         html : this.fieldLabel
28476                     },
28477                     {
28478                         tag : 'div',
28479                         cls : 'column',
28480                         cn : managerWidget
28481                     }
28482                 ];
28483                 
28484                 if(this.labelWidth > 12){
28485                     content[0].style = "width: " + this.labelWidth + 'px';
28486                 }
28487
28488                 if(this.labelWidth < 13 && this.labelmd == 0){
28489                     this.labelmd = this.labelWidth;
28490                 }
28491
28492                 if(this.labellg > 0){
28493                     content[0].cls += ' col-lg-' + this.labellg;
28494                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28495                 }
28496
28497                 if(this.labelmd > 0){
28498                     content[0].cls += ' col-md-' + this.labelmd;
28499                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28500                 }
28501
28502                 if(this.labelsm > 0){
28503                     content[0].cls += ' col-sm-' + this.labelsm;
28504                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28505                 }
28506
28507                 if(this.labelxs > 0){
28508                     content[0].cls += ' col-xs-' + this.labelxs;
28509                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28510                 }
28511                 
28512             }
28513         }
28514         
28515         var cfg = {
28516             tag : 'div',
28517             cls : 'row clearfix',
28518             cn : content
28519         };
28520         
28521         return cfg;
28522         
28523     },
28524     
28525     initEvents : function()
28526     {
28527         this.managerEl = this.el.select('.roo-document-manager', true).first();
28528         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28529         
28530         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28531         this.selectorEl.hide();
28532         
28533         if(this.multiple){
28534             this.selectorEl.attr('multiple', 'multiple');
28535         }
28536         
28537         this.selectorEl.on('change', this.onFileSelected, this);
28538         
28539         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28540         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28541         
28542         this.uploader.on('click', this.onUploaderClick, this);
28543         
28544         this.renderProgressDialog();
28545         
28546         var _this = this;
28547         
28548         window.addEventListener("resize", function() { _this.refresh(); } );
28549         
28550         this.fireEvent('initial', this);
28551     },
28552     
28553     renderProgressDialog : function()
28554     {
28555         var _this = this;
28556         
28557         this.progressDialog = new Roo.bootstrap.Modal({
28558             cls : 'roo-document-manager-progress-dialog',
28559             allow_close : false,
28560             title : '',
28561             buttons : [
28562                 {
28563                     name  :'cancel',
28564                     weight : 'danger',
28565                     html : 'Cancel'
28566                 }
28567             ], 
28568             listeners : { 
28569                 btnclick : function() {
28570                     _this.uploadCancel();
28571                     this.hide();
28572                 }
28573             }
28574         });
28575          
28576         this.progressDialog.render(Roo.get(document.body));
28577          
28578         this.progress = new Roo.bootstrap.Progress({
28579             cls : 'roo-document-manager-progress',
28580             active : true,
28581             striped : true
28582         });
28583         
28584         this.progress.render(this.progressDialog.getChildContainer());
28585         
28586         this.progressBar = new Roo.bootstrap.ProgressBar({
28587             cls : 'roo-document-manager-progress-bar',
28588             aria_valuenow : 0,
28589             aria_valuemin : 0,
28590             aria_valuemax : 12,
28591             panel : 'success'
28592         });
28593         
28594         this.progressBar.render(this.progress.getChildContainer());
28595     },
28596     
28597     onUploaderClick : function(e)
28598     {
28599         e.preventDefault();
28600      
28601         if(this.fireEvent('beforeselectfile', this) != false){
28602             this.selectorEl.dom.click();
28603         }
28604         
28605     },
28606     
28607     onFileSelected : function(e)
28608     {
28609         e.preventDefault();
28610         
28611         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28612             return;
28613         }
28614         
28615         Roo.each(this.selectorEl.dom.files, function(file){
28616             if(this.fireEvent('inspect', this, file) != false){
28617                 this.files.push(file);
28618             }
28619         }, this);
28620         
28621         this.queue();
28622         
28623     },
28624     
28625     queue : function()
28626     {
28627         this.selectorEl.dom.value = '';
28628         
28629         if(!this.files.length){
28630             return;
28631         }
28632         
28633         if(this.boxes > 0 && this.files.length > this.boxes){
28634             this.files = this.files.slice(0, this.boxes);
28635         }
28636         
28637         this.uploader.show();
28638         
28639         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28640             this.uploader.hide();
28641         }
28642         
28643         var _this = this;
28644         
28645         var files = [];
28646         
28647         var docs = [];
28648         
28649         Roo.each(this.files, function(file){
28650             
28651             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28652                 var f = this.renderPreview(file);
28653                 files.push(f);
28654                 return;
28655             }
28656             
28657             if(file.type.indexOf('image') != -1){
28658                 this.delegates.push(
28659                     (function(){
28660                         _this.process(file);
28661                     }).createDelegate(this)
28662                 );
28663         
28664                 return;
28665             }
28666             
28667             docs.push(
28668                 (function(){
28669                     _this.process(file);
28670                 }).createDelegate(this)
28671             );
28672             
28673         }, this);
28674         
28675         this.files = files;
28676         
28677         this.delegates = this.delegates.concat(docs);
28678         
28679         if(!this.delegates.length){
28680             this.refresh();
28681             return;
28682         }
28683         
28684         this.progressBar.aria_valuemax = this.delegates.length;
28685         
28686         this.arrange();
28687         
28688         return;
28689     },
28690     
28691     arrange : function()
28692     {
28693         if(!this.delegates.length){
28694             this.progressDialog.hide();
28695             this.refresh();
28696             return;
28697         }
28698         
28699         var delegate = this.delegates.shift();
28700         
28701         this.progressDialog.show();
28702         
28703         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28704         
28705         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28706         
28707         delegate();
28708     },
28709     
28710     refresh : function()
28711     {
28712         this.uploader.show();
28713         
28714         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28715             this.uploader.hide();
28716         }
28717         
28718         Roo.isTouch ? this.closable(false) : this.closable(true);
28719         
28720         this.fireEvent('refresh', this);
28721     },
28722     
28723     onRemove : function(e, el, o)
28724     {
28725         e.preventDefault();
28726         
28727         this.fireEvent('remove', this, o);
28728         
28729     },
28730     
28731     remove : function(o)
28732     {
28733         var files = [];
28734         
28735         Roo.each(this.files, function(file){
28736             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28737                 files.push(file);
28738                 return;
28739             }
28740
28741             o.target.remove();
28742
28743         }, this);
28744         
28745         this.files = files;
28746         
28747         this.refresh();
28748     },
28749     
28750     clear : function()
28751     {
28752         Roo.each(this.files, function(file){
28753             if(!file.target){
28754                 return;
28755             }
28756             
28757             file.target.remove();
28758
28759         }, this);
28760         
28761         this.files = [];
28762         
28763         this.refresh();
28764     },
28765     
28766     onClick : function(e, el, o)
28767     {
28768         e.preventDefault();
28769         
28770         this.fireEvent('click', this, o);
28771         
28772     },
28773     
28774     closable : function(closable)
28775     {
28776         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28777             
28778             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28779             
28780             if(closable){
28781                 el.show();
28782                 return;
28783             }
28784             
28785             el.hide();
28786             
28787         }, this);
28788     },
28789     
28790     xhrOnLoad : function(xhr)
28791     {
28792         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28793             el.remove();
28794         }, this);
28795         
28796         if (xhr.readyState !== 4) {
28797             this.arrange();
28798             this.fireEvent('exception', this, xhr);
28799             return;
28800         }
28801
28802         var response = Roo.decode(xhr.responseText);
28803         
28804         if(!response.success){
28805             this.arrange();
28806             this.fireEvent('exception', this, xhr);
28807             return;
28808         }
28809         
28810         var file = this.renderPreview(response.data);
28811         
28812         this.files.push(file);
28813         
28814         this.arrange();
28815         
28816         this.fireEvent('afterupload', this, xhr);
28817         
28818     },
28819     
28820     xhrOnError : function(xhr)
28821     {
28822         Roo.log('xhr on error');
28823         
28824         var response = Roo.decode(xhr.responseText);
28825           
28826         Roo.log(response);
28827         
28828         this.arrange();
28829     },
28830     
28831     process : function(file)
28832     {
28833         if(this.fireEvent('process', this, file) !== false){
28834             if(this.editable && file.type.indexOf('image') != -1){
28835                 this.fireEvent('edit', this, file);
28836                 return;
28837             }
28838
28839             this.uploadStart(file, false);
28840
28841             return;
28842         }
28843         
28844     },
28845     
28846     uploadStart : function(file, crop)
28847     {
28848         this.xhr = new XMLHttpRequest();
28849         
28850         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28851             this.arrange();
28852             return;
28853         }
28854         
28855         file.xhr = this.xhr;
28856             
28857         this.managerEl.createChild({
28858             tag : 'div',
28859             cls : 'roo-document-manager-loading',
28860             cn : [
28861                 {
28862                     tag : 'div',
28863                     tooltip : file.name,
28864                     cls : 'roo-document-manager-thumb',
28865                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28866                 }
28867             ]
28868
28869         });
28870
28871         this.xhr.open(this.method, this.url, true);
28872         
28873         var headers = {
28874             "Accept": "application/json",
28875             "Cache-Control": "no-cache",
28876             "X-Requested-With": "XMLHttpRequest"
28877         };
28878         
28879         for (var headerName in headers) {
28880             var headerValue = headers[headerName];
28881             if (headerValue) {
28882                 this.xhr.setRequestHeader(headerName, headerValue);
28883             }
28884         }
28885         
28886         var _this = this;
28887         
28888         this.xhr.onload = function()
28889         {
28890             _this.xhrOnLoad(_this.xhr);
28891         }
28892         
28893         this.xhr.onerror = function()
28894         {
28895             _this.xhrOnError(_this.xhr);
28896         }
28897         
28898         var formData = new FormData();
28899
28900         formData.append('returnHTML', 'NO');
28901         
28902         if(crop){
28903             formData.append('crop', crop);
28904         }
28905         
28906         formData.append(this.paramName, file, file.name);
28907         
28908         var options = {
28909             file : file, 
28910             manually : false
28911         };
28912         
28913         if(this.fireEvent('prepare', this, formData, options) != false){
28914             
28915             if(options.manually){
28916                 return;
28917             }
28918             
28919             this.xhr.send(formData);
28920             return;
28921         };
28922         
28923         this.uploadCancel();
28924     },
28925     
28926     uploadCancel : function()
28927     {
28928         if (this.xhr) {
28929             this.xhr.abort();
28930         }
28931         
28932         this.delegates = [];
28933         
28934         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28935             el.remove();
28936         }, this);
28937         
28938         this.arrange();
28939     },
28940     
28941     renderPreview : function(file)
28942     {
28943         if(typeof(file.target) != 'undefined' && file.target){
28944             return file;
28945         }
28946         
28947         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28948         
28949         var previewEl = this.managerEl.createChild({
28950             tag : 'div',
28951             cls : 'roo-document-manager-preview',
28952             cn : [
28953                 {
28954                     tag : 'div',
28955                     tooltip : file[this.toolTipName],
28956                     cls : 'roo-document-manager-thumb',
28957                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28958                 },
28959                 {
28960                     tag : 'button',
28961                     cls : 'close',
28962                     html : '<i class="fa fa-times-circle"></i>'
28963                 }
28964             ]
28965         });
28966
28967         var close = previewEl.select('button.close', true).first();
28968
28969         close.on('click', this.onRemove, this, file);
28970
28971         file.target = previewEl;
28972
28973         var image = previewEl.select('img', true).first();
28974         
28975         var _this = this;
28976         
28977         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28978         
28979         image.on('click', this.onClick, this, file);
28980         
28981         return file;
28982         
28983     },
28984     
28985     onPreviewLoad : function(file, image)
28986     {
28987         if(typeof(file.target) == 'undefined' || !file.target){
28988             return;
28989         }
28990         
28991         var width = image.dom.naturalWidth || image.dom.width;
28992         var height = image.dom.naturalHeight || image.dom.height;
28993         
28994         if(width > height){
28995             file.target.addClass('wide');
28996             return;
28997         }
28998         
28999         file.target.addClass('tall');
29000         return;
29001         
29002     },
29003     
29004     uploadFromSource : function(file, crop)
29005     {
29006         this.xhr = new XMLHttpRequest();
29007         
29008         this.managerEl.createChild({
29009             tag : 'div',
29010             cls : 'roo-document-manager-loading',
29011             cn : [
29012                 {
29013                     tag : 'div',
29014                     tooltip : file.name,
29015                     cls : 'roo-document-manager-thumb',
29016                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29017                 }
29018             ]
29019
29020         });
29021
29022         this.xhr.open(this.method, this.url, true);
29023         
29024         var headers = {
29025             "Accept": "application/json",
29026             "Cache-Control": "no-cache",
29027             "X-Requested-With": "XMLHttpRequest"
29028         };
29029         
29030         for (var headerName in headers) {
29031             var headerValue = headers[headerName];
29032             if (headerValue) {
29033                 this.xhr.setRequestHeader(headerName, headerValue);
29034             }
29035         }
29036         
29037         var _this = this;
29038         
29039         this.xhr.onload = function()
29040         {
29041             _this.xhrOnLoad(_this.xhr);
29042         }
29043         
29044         this.xhr.onerror = function()
29045         {
29046             _this.xhrOnError(_this.xhr);
29047         }
29048         
29049         var formData = new FormData();
29050
29051         formData.append('returnHTML', 'NO');
29052         
29053         formData.append('crop', crop);
29054         
29055         if(typeof(file.filename) != 'undefined'){
29056             formData.append('filename', file.filename);
29057         }
29058         
29059         if(typeof(file.mimetype) != 'undefined'){
29060             formData.append('mimetype', file.mimetype);
29061         }
29062         
29063         Roo.log(formData);
29064         
29065         if(this.fireEvent('prepare', this, formData) != false){
29066             this.xhr.send(formData);
29067         };
29068     }
29069 });
29070
29071 /*
29072 * Licence: LGPL
29073 */
29074
29075 /**
29076  * @class Roo.bootstrap.DocumentViewer
29077  * @extends Roo.bootstrap.Component
29078  * Bootstrap DocumentViewer class
29079  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29080  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29081  * 
29082  * @constructor
29083  * Create a new DocumentViewer
29084  * @param {Object} config The config object
29085  */
29086
29087 Roo.bootstrap.DocumentViewer = function(config){
29088     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29089     
29090     this.addEvents({
29091         /**
29092          * @event initial
29093          * Fire after initEvent
29094          * @param {Roo.bootstrap.DocumentViewer} this
29095          */
29096         "initial" : true,
29097         /**
29098          * @event click
29099          * Fire after click
29100          * @param {Roo.bootstrap.DocumentViewer} this
29101          */
29102         "click" : true,
29103         /**
29104          * @event download
29105          * Fire after download button
29106          * @param {Roo.bootstrap.DocumentViewer} this
29107          */
29108         "download" : true,
29109         /**
29110          * @event trash
29111          * Fire after trash button
29112          * @param {Roo.bootstrap.DocumentViewer} this
29113          */
29114         "trash" : true
29115         
29116     });
29117 };
29118
29119 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29120     
29121     showDownload : true,
29122     
29123     showTrash : true,
29124     
29125     getAutoCreate : function()
29126     {
29127         var cfg = {
29128             tag : 'div',
29129             cls : 'roo-document-viewer',
29130             cn : [
29131                 {
29132                     tag : 'div',
29133                     cls : 'roo-document-viewer-body',
29134                     cn : [
29135                         {
29136                             tag : 'div',
29137                             cls : 'roo-document-viewer-thumb',
29138                             cn : [
29139                                 {
29140                                     tag : 'img',
29141                                     cls : 'roo-document-viewer-image'
29142                                 }
29143                             ]
29144                         }
29145                     ]
29146                 },
29147                 {
29148                     tag : 'div',
29149                     cls : 'roo-document-viewer-footer',
29150                     cn : {
29151                         tag : 'div',
29152                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29153                         cn : [
29154                             {
29155                                 tag : 'div',
29156                                 cls : 'btn-group roo-document-viewer-download',
29157                                 cn : [
29158                                     {
29159                                         tag : 'button',
29160                                         cls : 'btn btn-default',
29161                                         html : '<i class="fa fa-download"></i>'
29162                                     }
29163                                 ]
29164                             },
29165                             {
29166                                 tag : 'div',
29167                                 cls : 'btn-group roo-document-viewer-trash',
29168                                 cn : [
29169                                     {
29170                                         tag : 'button',
29171                                         cls : 'btn btn-default',
29172                                         html : '<i class="fa fa-trash"></i>'
29173                                     }
29174                                 ]
29175                             }
29176                         ]
29177                     }
29178                 }
29179             ]
29180         };
29181         
29182         return cfg;
29183     },
29184     
29185     initEvents : function()
29186     {
29187         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29188         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29189         
29190         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29191         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29192         
29193         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29194         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29195         
29196         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29197         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29198         
29199         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29200         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29201         
29202         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29203         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29204         
29205         this.bodyEl.on('click', this.onClick, this);
29206         this.downloadBtn.on('click', this.onDownload, this);
29207         this.trashBtn.on('click', this.onTrash, this);
29208         
29209         this.downloadBtn.hide();
29210         this.trashBtn.hide();
29211         
29212         if(this.showDownload){
29213             this.downloadBtn.show();
29214         }
29215         
29216         if(this.showTrash){
29217             this.trashBtn.show();
29218         }
29219         
29220         if(!this.showDownload && !this.showTrash) {
29221             this.footerEl.hide();
29222         }
29223         
29224     },
29225     
29226     initial : function()
29227     {
29228         this.fireEvent('initial', this);
29229         
29230     },
29231     
29232     onClick : function(e)
29233     {
29234         e.preventDefault();
29235         
29236         this.fireEvent('click', this);
29237     },
29238     
29239     onDownload : function(e)
29240     {
29241         e.preventDefault();
29242         
29243         this.fireEvent('download', this);
29244     },
29245     
29246     onTrash : function(e)
29247     {
29248         e.preventDefault();
29249         
29250         this.fireEvent('trash', this);
29251     }
29252     
29253 });
29254 /*
29255  * - LGPL
29256  *
29257  * nav progress bar
29258  * 
29259  */
29260
29261 /**
29262  * @class Roo.bootstrap.NavProgressBar
29263  * @extends Roo.bootstrap.Component
29264  * Bootstrap NavProgressBar class
29265  * 
29266  * @constructor
29267  * Create a new nav progress bar
29268  * @param {Object} config The config object
29269  */
29270
29271 Roo.bootstrap.NavProgressBar = function(config){
29272     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29273
29274     this.bullets = this.bullets || [];
29275    
29276 //    Roo.bootstrap.NavProgressBar.register(this);
29277      this.addEvents({
29278         /**
29279              * @event changed
29280              * Fires when the active item changes
29281              * @param {Roo.bootstrap.NavProgressBar} this
29282              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29283              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29284          */
29285         'changed': true
29286      });
29287     
29288 };
29289
29290 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29291     
29292     bullets : [],
29293     barItems : [],
29294     
29295     getAutoCreate : function()
29296     {
29297         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29298         
29299         cfg = {
29300             tag : 'div',
29301             cls : 'roo-navigation-bar-group',
29302             cn : [
29303                 {
29304                     tag : 'div',
29305                     cls : 'roo-navigation-top-bar'
29306                 },
29307                 {
29308                     tag : 'div',
29309                     cls : 'roo-navigation-bullets-bar',
29310                     cn : [
29311                         {
29312                             tag : 'ul',
29313                             cls : 'roo-navigation-bar'
29314                         }
29315                     ]
29316                 },
29317                 
29318                 {
29319                     tag : 'div',
29320                     cls : 'roo-navigation-bottom-bar'
29321                 }
29322             ]
29323             
29324         };
29325         
29326         return cfg;
29327         
29328     },
29329     
29330     initEvents: function() 
29331     {
29332         
29333     },
29334     
29335     onRender : function(ct, position) 
29336     {
29337         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29338         
29339         if(this.bullets.length){
29340             Roo.each(this.bullets, function(b){
29341                this.addItem(b);
29342             }, this);
29343         }
29344         
29345         this.format();
29346         
29347     },
29348     
29349     addItem : function(cfg)
29350     {
29351         var item = new Roo.bootstrap.NavProgressItem(cfg);
29352         
29353         item.parentId = this.id;
29354         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29355         
29356         if(cfg.html){
29357             var top = new Roo.bootstrap.Element({
29358                 tag : 'div',
29359                 cls : 'roo-navigation-bar-text'
29360             });
29361             
29362             var bottom = new Roo.bootstrap.Element({
29363                 tag : 'div',
29364                 cls : 'roo-navigation-bar-text'
29365             });
29366             
29367             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29368             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29369             
29370             var topText = new Roo.bootstrap.Element({
29371                 tag : 'span',
29372                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29373             });
29374             
29375             var bottomText = new Roo.bootstrap.Element({
29376                 tag : 'span',
29377                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29378             });
29379             
29380             topText.onRender(top.el, null);
29381             bottomText.onRender(bottom.el, null);
29382             
29383             item.topEl = top;
29384             item.bottomEl = bottom;
29385         }
29386         
29387         this.barItems.push(item);
29388         
29389         return item;
29390     },
29391     
29392     getActive : function()
29393     {
29394         var active = false;
29395         
29396         Roo.each(this.barItems, function(v){
29397             
29398             if (!v.isActive()) {
29399                 return;
29400             }
29401             
29402             active = v;
29403             return false;
29404             
29405         });
29406         
29407         return active;
29408     },
29409     
29410     setActiveItem : function(item)
29411     {
29412         var prev = false;
29413         
29414         Roo.each(this.barItems, function(v){
29415             if (v.rid == item.rid) {
29416                 return ;
29417             }
29418             
29419             if (v.isActive()) {
29420                 v.setActive(false);
29421                 prev = v;
29422             }
29423         });
29424
29425         item.setActive(true);
29426         
29427         this.fireEvent('changed', this, item, prev);
29428     },
29429     
29430     getBarItem: function(rid)
29431     {
29432         var ret = false;
29433         
29434         Roo.each(this.barItems, function(e) {
29435             if (e.rid != rid) {
29436                 return;
29437             }
29438             
29439             ret =  e;
29440             return false;
29441         });
29442         
29443         return ret;
29444     },
29445     
29446     indexOfItem : function(item)
29447     {
29448         var index = false;
29449         
29450         Roo.each(this.barItems, function(v, i){
29451             
29452             if (v.rid != item.rid) {
29453                 return;
29454             }
29455             
29456             index = i;
29457             return false
29458         });
29459         
29460         return index;
29461     },
29462     
29463     setActiveNext : function()
29464     {
29465         var i = this.indexOfItem(this.getActive());
29466         
29467         if (i > this.barItems.length) {
29468             return;
29469         }
29470         
29471         this.setActiveItem(this.barItems[i+1]);
29472     },
29473     
29474     setActivePrev : function()
29475     {
29476         var i = this.indexOfItem(this.getActive());
29477         
29478         if (i  < 1) {
29479             return;
29480         }
29481         
29482         this.setActiveItem(this.barItems[i-1]);
29483     },
29484     
29485     format : function()
29486     {
29487         if(!this.barItems.length){
29488             return;
29489         }
29490      
29491         var width = 100 / this.barItems.length;
29492         
29493         Roo.each(this.barItems, function(i){
29494             i.el.setStyle('width', width + '%');
29495             i.topEl.el.setStyle('width', width + '%');
29496             i.bottomEl.el.setStyle('width', width + '%');
29497         }, this);
29498         
29499     }
29500     
29501 });
29502 /*
29503  * - LGPL
29504  *
29505  * Nav Progress Item
29506  * 
29507  */
29508
29509 /**
29510  * @class Roo.bootstrap.NavProgressItem
29511  * @extends Roo.bootstrap.Component
29512  * Bootstrap NavProgressItem class
29513  * @cfg {String} rid the reference id
29514  * @cfg {Boolean} active (true|false) Is item active default false
29515  * @cfg {Boolean} disabled (true|false) Is item active default false
29516  * @cfg {String} html
29517  * @cfg {String} position (top|bottom) text position default bottom
29518  * @cfg {String} icon show icon instead of number
29519  * 
29520  * @constructor
29521  * Create a new NavProgressItem
29522  * @param {Object} config The config object
29523  */
29524 Roo.bootstrap.NavProgressItem = function(config){
29525     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29526     this.addEvents({
29527         // raw events
29528         /**
29529          * @event click
29530          * The raw click event for the entire grid.
29531          * @param {Roo.bootstrap.NavProgressItem} this
29532          * @param {Roo.EventObject} e
29533          */
29534         "click" : true
29535     });
29536    
29537 };
29538
29539 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29540     
29541     rid : '',
29542     active : false,
29543     disabled : false,
29544     html : '',
29545     position : 'bottom',
29546     icon : false,
29547     
29548     getAutoCreate : function()
29549     {
29550         var iconCls = 'roo-navigation-bar-item-icon';
29551         
29552         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29553         
29554         var cfg = {
29555             tag: 'li',
29556             cls: 'roo-navigation-bar-item',
29557             cn : [
29558                 {
29559                     tag : 'i',
29560                     cls : iconCls
29561                 }
29562             ]
29563         };
29564         
29565         if(this.active){
29566             cfg.cls += ' active';
29567         }
29568         if(this.disabled){
29569             cfg.cls += ' disabled';
29570         }
29571         
29572         return cfg;
29573     },
29574     
29575     disable : function()
29576     {
29577         this.setDisabled(true);
29578     },
29579     
29580     enable : function()
29581     {
29582         this.setDisabled(false);
29583     },
29584     
29585     initEvents: function() 
29586     {
29587         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29588         
29589         this.iconEl.on('click', this.onClick, this);
29590     },
29591     
29592     onClick : function(e)
29593     {
29594         e.preventDefault();
29595         
29596         if(this.disabled){
29597             return;
29598         }
29599         
29600         if(this.fireEvent('click', this, e) === false){
29601             return;
29602         };
29603         
29604         this.parent().setActiveItem(this);
29605     },
29606     
29607     isActive: function () 
29608     {
29609         return this.active;
29610     },
29611     
29612     setActive : function(state)
29613     {
29614         if(this.active == state){
29615             return;
29616         }
29617         
29618         this.active = state;
29619         
29620         if (state) {
29621             this.el.addClass('active');
29622             return;
29623         }
29624         
29625         this.el.removeClass('active');
29626         
29627         return;
29628     },
29629     
29630     setDisabled : function(state)
29631     {
29632         if(this.disabled == state){
29633             return;
29634         }
29635         
29636         this.disabled = state;
29637         
29638         if (state) {
29639             this.el.addClass('disabled');
29640             return;
29641         }
29642         
29643         this.el.removeClass('disabled');
29644     },
29645     
29646     tooltipEl : function()
29647     {
29648         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29649     }
29650 });
29651  
29652
29653  /*
29654  * - LGPL
29655  *
29656  * FieldLabel
29657  * 
29658  */
29659
29660 /**
29661  * @class Roo.bootstrap.FieldLabel
29662  * @extends Roo.bootstrap.Component
29663  * Bootstrap FieldLabel class
29664  * @cfg {String} html contents of the element
29665  * @cfg {String} tag tag of the element default label
29666  * @cfg {String} cls class of the element
29667  * @cfg {String} target label target 
29668  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29669  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29670  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29671  * @cfg {String} iconTooltip default "This field is required"
29672  * 
29673  * @constructor
29674  * Create a new FieldLabel
29675  * @param {Object} config The config object
29676  */
29677
29678 Roo.bootstrap.FieldLabel = function(config){
29679     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29680     
29681     this.addEvents({
29682             /**
29683              * @event invalid
29684              * Fires after the field has been marked as invalid.
29685              * @param {Roo.form.FieldLabel} this
29686              * @param {String} msg The validation message
29687              */
29688             invalid : true,
29689             /**
29690              * @event valid
29691              * Fires after the field has been validated with no errors.
29692              * @param {Roo.form.FieldLabel} this
29693              */
29694             valid : true
29695         });
29696 };
29697
29698 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29699     
29700     tag: 'label',
29701     cls: '',
29702     html: '',
29703     target: '',
29704     allowBlank : true,
29705     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29706     validClass : 'text-success fa fa-lg fa-check',
29707     iconTooltip : 'This field is required',
29708     
29709     getAutoCreate : function(){
29710         
29711         var cfg = {
29712             tag : this.tag,
29713             cls : 'roo-bootstrap-field-label ' + this.cls,
29714             for : this.target,
29715             cn : [
29716                 {
29717                     tag : 'i',
29718                     cls : '',
29719                     tooltip : this.iconTooltip
29720                 },
29721                 {
29722                     tag : 'span',
29723                     html : this.html
29724                 }
29725             ] 
29726         };
29727         
29728         return cfg;
29729     },
29730     
29731     initEvents: function() 
29732     {
29733         Roo.bootstrap.Element.superclass.initEvents.call(this);
29734         
29735         this.iconEl = this.el.select('i', true).first();
29736         
29737         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29738         
29739         Roo.bootstrap.FieldLabel.register(this);
29740     },
29741     
29742     /**
29743      * Mark this field as valid
29744      */
29745     markValid : function()
29746     {
29747         this.iconEl.show();
29748         
29749         this.iconEl.removeClass(this.invalidClass);
29750         
29751         this.iconEl.addClass(this.validClass);
29752         
29753         this.fireEvent('valid', this);
29754     },
29755     
29756     /**
29757      * Mark this field as invalid
29758      * @param {String} msg The validation message
29759      */
29760     markInvalid : function(msg)
29761     {
29762         this.iconEl.show();
29763         
29764         this.iconEl.removeClass(this.validClass);
29765         
29766         this.iconEl.addClass(this.invalidClass);
29767         
29768         this.fireEvent('invalid', this, msg);
29769     }
29770     
29771    
29772 });
29773
29774 Roo.apply(Roo.bootstrap.FieldLabel, {
29775     
29776     groups: {},
29777     
29778      /**
29779     * register a FieldLabel Group
29780     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29781     */
29782     register : function(label)
29783     {
29784         if(this.groups.hasOwnProperty(label.target)){
29785             return;
29786         }
29787      
29788         this.groups[label.target] = label;
29789         
29790     },
29791     /**
29792     * fetch a FieldLabel Group based on the target
29793     * @param {string} target
29794     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29795     */
29796     get: function(target) {
29797         if (typeof(this.groups[target]) == 'undefined') {
29798             return false;
29799         }
29800         
29801         return this.groups[target] ;
29802     }
29803 });
29804
29805  
29806
29807  /*
29808  * - LGPL
29809  *
29810  * page DateSplitField.
29811  * 
29812  */
29813
29814
29815 /**
29816  * @class Roo.bootstrap.DateSplitField
29817  * @extends Roo.bootstrap.Component
29818  * Bootstrap DateSplitField class
29819  * @cfg {string} fieldLabel - the label associated
29820  * @cfg {Number} labelWidth set the width of label (0-12)
29821  * @cfg {String} labelAlign (top|left)
29822  * @cfg {Boolean} dayAllowBlank (true|false) default false
29823  * @cfg {Boolean} monthAllowBlank (true|false) default false
29824  * @cfg {Boolean} yearAllowBlank (true|false) default false
29825  * @cfg {string} dayPlaceholder 
29826  * @cfg {string} monthPlaceholder
29827  * @cfg {string} yearPlaceholder
29828  * @cfg {string} dayFormat default 'd'
29829  * @cfg {string} monthFormat default 'm'
29830  * @cfg {string} yearFormat default 'Y'
29831  * @cfg {Number} labellg set the width of label (1-12)
29832  * @cfg {Number} labelmd set the width of label (1-12)
29833  * @cfg {Number} labelsm set the width of label (1-12)
29834  * @cfg {Number} labelxs set the width of label (1-12)
29835
29836  *     
29837  * @constructor
29838  * Create a new DateSplitField
29839  * @param {Object} config The config object
29840  */
29841
29842 Roo.bootstrap.DateSplitField = function(config){
29843     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29844     
29845     this.addEvents({
29846         // raw events
29847          /**
29848          * @event years
29849          * getting the data of years
29850          * @param {Roo.bootstrap.DateSplitField} this
29851          * @param {Object} years
29852          */
29853         "years" : true,
29854         /**
29855          * @event days
29856          * getting the data of days
29857          * @param {Roo.bootstrap.DateSplitField} this
29858          * @param {Object} days
29859          */
29860         "days" : true,
29861         /**
29862          * @event invalid
29863          * Fires after the field has been marked as invalid.
29864          * @param {Roo.form.Field} this
29865          * @param {String} msg The validation message
29866          */
29867         invalid : true,
29868        /**
29869          * @event valid
29870          * Fires after the field has been validated with no errors.
29871          * @param {Roo.form.Field} this
29872          */
29873         valid : true
29874     });
29875 };
29876
29877 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29878     
29879     fieldLabel : '',
29880     labelAlign : 'top',
29881     labelWidth : 3,
29882     dayAllowBlank : false,
29883     monthAllowBlank : false,
29884     yearAllowBlank : false,
29885     dayPlaceholder : '',
29886     monthPlaceholder : '',
29887     yearPlaceholder : '',
29888     dayFormat : 'd',
29889     monthFormat : 'm',
29890     yearFormat : 'Y',
29891     isFormField : true,
29892     labellg : 0,
29893     labelmd : 0,
29894     labelsm : 0,
29895     labelxs : 0,
29896     
29897     getAutoCreate : function()
29898     {
29899         var cfg = {
29900             tag : 'div',
29901             cls : 'row roo-date-split-field-group',
29902             cn : [
29903                 {
29904                     tag : 'input',
29905                     type : 'hidden',
29906                     cls : 'form-hidden-field roo-date-split-field-group-value',
29907                     name : this.name
29908                 }
29909             ]
29910         };
29911         
29912         var labelCls = 'col-md-12';
29913         var contentCls = 'col-md-4';
29914         
29915         if(this.fieldLabel){
29916             
29917             var label = {
29918                 tag : 'div',
29919                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29920                 cn : [
29921                     {
29922                         tag : 'label',
29923                         html : this.fieldLabel
29924                     }
29925                 ]
29926             };
29927             
29928             if(this.labelAlign == 'left'){
29929             
29930                 if(this.labelWidth > 12){
29931                     label.style = "width: " + this.labelWidth + 'px';
29932                 }
29933
29934                 if(this.labelWidth < 13 && this.labelmd == 0){
29935                     this.labelmd = this.labelWidth;
29936                 }
29937
29938                 if(this.labellg > 0){
29939                     labelCls = ' col-lg-' + this.labellg;
29940                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29941                 }
29942
29943                 if(this.labelmd > 0){
29944                     labelCls = ' col-md-' + this.labelmd;
29945                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29946                 }
29947
29948                 if(this.labelsm > 0){
29949                     labelCls = ' col-sm-' + this.labelsm;
29950                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29951                 }
29952
29953                 if(this.labelxs > 0){
29954                     labelCls = ' col-xs-' + this.labelxs;
29955                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29956                 }
29957             }
29958             
29959             label.cls += ' ' + labelCls;
29960             
29961             cfg.cn.push(label);
29962         }
29963         
29964         Roo.each(['day', 'month', 'year'], function(t){
29965             cfg.cn.push({
29966                 tag : 'div',
29967                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29968             });
29969         }, this);
29970         
29971         return cfg;
29972     },
29973     
29974     inputEl: function ()
29975     {
29976         return this.el.select('.roo-date-split-field-group-value', true).first();
29977     },
29978     
29979     onRender : function(ct, position) 
29980     {
29981         var _this = this;
29982         
29983         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29984         
29985         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29986         
29987         this.dayField = new Roo.bootstrap.ComboBox({
29988             allowBlank : this.dayAllowBlank,
29989             alwaysQuery : true,
29990             displayField : 'value',
29991             editable : false,
29992             fieldLabel : '',
29993             forceSelection : true,
29994             mode : 'local',
29995             placeholder : this.dayPlaceholder,
29996             selectOnFocus : true,
29997             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29998             triggerAction : 'all',
29999             typeAhead : true,
30000             valueField : 'value',
30001             store : new Roo.data.SimpleStore({
30002                 data : (function() {    
30003                     var days = [];
30004                     _this.fireEvent('days', _this, days);
30005                     return days;
30006                 })(),
30007                 fields : [ 'value' ]
30008             }),
30009             listeners : {
30010                 select : function (_self, record, index)
30011                 {
30012                     _this.setValue(_this.getValue());
30013                 }
30014             }
30015         });
30016
30017         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30018         
30019         this.monthField = new Roo.bootstrap.MonthField({
30020             after : '<i class=\"fa fa-calendar\"></i>',
30021             allowBlank : this.monthAllowBlank,
30022             placeholder : this.monthPlaceholder,
30023             readOnly : true,
30024             listeners : {
30025                 render : function (_self)
30026                 {
30027                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30028                         e.preventDefault();
30029                         _self.focus();
30030                     });
30031                 },
30032                 select : function (_self, oldvalue, newvalue)
30033                 {
30034                     _this.setValue(_this.getValue());
30035                 }
30036             }
30037         });
30038         
30039         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30040         
30041         this.yearField = new Roo.bootstrap.ComboBox({
30042             allowBlank : this.yearAllowBlank,
30043             alwaysQuery : true,
30044             displayField : 'value',
30045             editable : false,
30046             fieldLabel : '',
30047             forceSelection : true,
30048             mode : 'local',
30049             placeholder : this.yearPlaceholder,
30050             selectOnFocus : true,
30051             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30052             triggerAction : 'all',
30053             typeAhead : true,
30054             valueField : 'value',
30055             store : new Roo.data.SimpleStore({
30056                 data : (function() {
30057                     var years = [];
30058                     _this.fireEvent('years', _this, years);
30059                     return years;
30060                 })(),
30061                 fields : [ 'value' ]
30062             }),
30063             listeners : {
30064                 select : function (_self, record, index)
30065                 {
30066                     _this.setValue(_this.getValue());
30067                 }
30068             }
30069         });
30070
30071         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30072     },
30073     
30074     setValue : function(v, format)
30075     {
30076         this.inputEl.dom.value = v;
30077         
30078         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30079         
30080         var d = Date.parseDate(v, f);
30081         
30082         if(!d){
30083             this.validate();
30084             return;
30085         }
30086         
30087         this.setDay(d.format(this.dayFormat));
30088         this.setMonth(d.format(this.monthFormat));
30089         this.setYear(d.format(this.yearFormat));
30090         
30091         this.validate();
30092         
30093         return;
30094     },
30095     
30096     setDay : function(v)
30097     {
30098         this.dayField.setValue(v);
30099         this.inputEl.dom.value = this.getValue();
30100         this.validate();
30101         return;
30102     },
30103     
30104     setMonth : function(v)
30105     {
30106         this.monthField.setValue(v, true);
30107         this.inputEl.dom.value = this.getValue();
30108         this.validate();
30109         return;
30110     },
30111     
30112     setYear : function(v)
30113     {
30114         this.yearField.setValue(v);
30115         this.inputEl.dom.value = this.getValue();
30116         this.validate();
30117         return;
30118     },
30119     
30120     getDay : function()
30121     {
30122         return this.dayField.getValue();
30123     },
30124     
30125     getMonth : function()
30126     {
30127         return this.monthField.getValue();
30128     },
30129     
30130     getYear : function()
30131     {
30132         return this.yearField.getValue();
30133     },
30134     
30135     getValue : function()
30136     {
30137         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30138         
30139         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30140         
30141         return date;
30142     },
30143     
30144     reset : function()
30145     {
30146         this.setDay('');
30147         this.setMonth('');
30148         this.setYear('');
30149         this.inputEl.dom.value = '';
30150         this.validate();
30151         return;
30152     },
30153     
30154     validate : function()
30155     {
30156         var d = this.dayField.validate();
30157         var m = this.monthField.validate();
30158         var y = this.yearField.validate();
30159         
30160         var valid = true;
30161         
30162         if(
30163                 (!this.dayAllowBlank && !d) ||
30164                 (!this.monthAllowBlank && !m) ||
30165                 (!this.yearAllowBlank && !y)
30166         ){
30167             valid = false;
30168         }
30169         
30170         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30171             return valid;
30172         }
30173         
30174         if(valid){
30175             this.markValid();
30176             return valid;
30177         }
30178         
30179         this.markInvalid();
30180         
30181         return valid;
30182     },
30183     
30184     markValid : function()
30185     {
30186         
30187         var label = this.el.select('label', true).first();
30188         var icon = this.el.select('i.fa-star', true).first();
30189
30190         if(label && icon){
30191             icon.remove();
30192         }
30193         
30194         this.fireEvent('valid', this);
30195     },
30196     
30197      /**
30198      * Mark this field as invalid
30199      * @param {String} msg The validation message
30200      */
30201     markInvalid : function(msg)
30202     {
30203         
30204         var label = this.el.select('label', true).first();
30205         var icon = this.el.select('i.fa-star', true).first();
30206
30207         if(label && !icon){
30208             this.el.select('.roo-date-split-field-label', true).createChild({
30209                 tag : 'i',
30210                 cls : 'text-danger fa fa-lg fa-star',
30211                 tooltip : 'This field is required',
30212                 style : 'margin-right:5px;'
30213             }, label, true);
30214         }
30215         
30216         this.fireEvent('invalid', this, msg);
30217     },
30218     
30219     clearInvalid : function()
30220     {
30221         var label = this.el.select('label', true).first();
30222         var icon = this.el.select('i.fa-star', true).first();
30223
30224         if(label && icon){
30225             icon.remove();
30226         }
30227         
30228         this.fireEvent('valid', this);
30229     },
30230     
30231     getName: function()
30232     {
30233         return this.name;
30234     }
30235     
30236 });
30237
30238  /**
30239  *
30240  * This is based on 
30241  * http://masonry.desandro.com
30242  *
30243  * The idea is to render all the bricks based on vertical width...
30244  *
30245  * The original code extends 'outlayer' - we might need to use that....
30246  * 
30247  */
30248
30249
30250 /**
30251  * @class Roo.bootstrap.LayoutMasonry
30252  * @extends Roo.bootstrap.Component
30253  * Bootstrap Layout Masonry class
30254  * 
30255  * @constructor
30256  * Create a new Element
30257  * @param {Object} config The config object
30258  */
30259
30260 Roo.bootstrap.LayoutMasonry = function(config){
30261     
30262     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30263     
30264     this.bricks = [];
30265     
30266     Roo.bootstrap.LayoutMasonry.register(this);
30267     
30268     this.addEvents({
30269         // raw events
30270         /**
30271          * @event layout
30272          * Fire after layout the items
30273          * @param {Roo.bootstrap.LayoutMasonry} this
30274          * @param {Roo.EventObject} e
30275          */
30276         "layout" : true
30277     });
30278     
30279 };
30280
30281 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30282     
30283     /**
30284      * @cfg {Boolean} isLayoutInstant = no animation?
30285      */   
30286     isLayoutInstant : false, // needed?
30287    
30288     /**
30289      * @cfg {Number} boxWidth  width of the columns
30290      */   
30291     boxWidth : 450,
30292     
30293       /**
30294      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30295      */   
30296     boxHeight : 0,
30297     
30298     /**
30299      * @cfg {Number} padWidth padding below box..
30300      */   
30301     padWidth : 10, 
30302     
30303     /**
30304      * @cfg {Number} gutter gutter width..
30305      */   
30306     gutter : 10,
30307     
30308      /**
30309      * @cfg {Number} maxCols maximum number of columns
30310      */   
30311     
30312     maxCols: 0,
30313     
30314     /**
30315      * @cfg {Boolean} isAutoInitial defalut true
30316      */   
30317     isAutoInitial : true, 
30318     
30319     containerWidth: 0,
30320     
30321     /**
30322      * @cfg {Boolean} isHorizontal defalut false
30323      */   
30324     isHorizontal : false, 
30325
30326     currentSize : null,
30327     
30328     tag: 'div',
30329     
30330     cls: '',
30331     
30332     bricks: null, //CompositeElement
30333     
30334     cols : 1,
30335     
30336     _isLayoutInited : false,
30337     
30338 //    isAlternative : false, // only use for vertical layout...
30339     
30340     /**
30341      * @cfg {Number} alternativePadWidth padding below box..
30342      */   
30343     alternativePadWidth : 50,
30344     
30345     selectedBrick : [],
30346     
30347     getAutoCreate : function(){
30348         
30349         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30350         
30351         var cfg = {
30352             tag: this.tag,
30353             cls: 'blog-masonary-wrapper ' + this.cls,
30354             cn : {
30355                 cls : 'mas-boxes masonary'
30356             }
30357         };
30358         
30359         return cfg;
30360     },
30361     
30362     getChildContainer: function( )
30363     {
30364         if (this.boxesEl) {
30365             return this.boxesEl;
30366         }
30367         
30368         this.boxesEl = this.el.select('.mas-boxes').first();
30369         
30370         return this.boxesEl;
30371     },
30372     
30373     
30374     initEvents : function()
30375     {
30376         var _this = this;
30377         
30378         if(this.isAutoInitial){
30379             Roo.log('hook children rendered');
30380             this.on('childrenrendered', function() {
30381                 Roo.log('children rendered');
30382                 _this.initial();
30383             } ,this);
30384         }
30385     },
30386     
30387     initial : function()
30388     {
30389         this.selectedBrick = [];
30390         
30391         this.currentSize = this.el.getBox(true);
30392         
30393         Roo.EventManager.onWindowResize(this.resize, this); 
30394
30395         if(!this.isAutoInitial){
30396             this.layout();
30397             return;
30398         }
30399         
30400         this.layout();
30401         
30402         return;
30403         //this.layout.defer(500,this);
30404         
30405     },
30406     
30407     resize : function()
30408     {
30409         var cs = this.el.getBox(true);
30410         
30411         if (
30412                 this.currentSize.width == cs.width && 
30413                 this.currentSize.x == cs.x && 
30414                 this.currentSize.height == cs.height && 
30415                 this.currentSize.y == cs.y 
30416         ) {
30417             Roo.log("no change in with or X or Y");
30418             return;
30419         }
30420         
30421         this.currentSize = cs;
30422         
30423         this.layout();
30424         
30425     },
30426     
30427     layout : function()
30428     {   
30429         this._resetLayout();
30430         
30431         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30432         
30433         this.layoutItems( isInstant );
30434       
30435         this._isLayoutInited = true;
30436         
30437         this.fireEvent('layout', this);
30438         
30439     },
30440     
30441     _resetLayout : function()
30442     {
30443         if(this.isHorizontal){
30444             this.horizontalMeasureColumns();
30445             return;
30446         }
30447         
30448         this.verticalMeasureColumns();
30449         
30450     },
30451     
30452     verticalMeasureColumns : function()
30453     {
30454         this.getContainerWidth();
30455         
30456 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30457 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30458 //            return;
30459 //        }
30460         
30461         var boxWidth = this.boxWidth + this.padWidth;
30462         
30463         if(this.containerWidth < this.boxWidth){
30464             boxWidth = this.containerWidth
30465         }
30466         
30467         var containerWidth = this.containerWidth;
30468         
30469         var cols = Math.floor(containerWidth / boxWidth);
30470         
30471         this.cols = Math.max( cols, 1 );
30472         
30473         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30474         
30475         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30476         
30477         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30478         
30479         this.colWidth = boxWidth + avail - this.padWidth;
30480         
30481         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30482         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30483     },
30484     
30485     horizontalMeasureColumns : function()
30486     {
30487         this.getContainerWidth();
30488         
30489         var boxWidth = this.boxWidth;
30490         
30491         if(this.containerWidth < boxWidth){
30492             boxWidth = this.containerWidth;
30493         }
30494         
30495         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30496         
30497         this.el.setHeight(boxWidth);
30498         
30499     },
30500     
30501     getContainerWidth : function()
30502     {
30503         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30504     },
30505     
30506     layoutItems : function( isInstant )
30507     {
30508         Roo.log(this.bricks);
30509         
30510         var items = Roo.apply([], this.bricks);
30511         
30512         if(this.isHorizontal){
30513             this._horizontalLayoutItems( items , isInstant );
30514             return;
30515         }
30516         
30517 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30518 //            this._verticalAlternativeLayoutItems( items , isInstant );
30519 //            return;
30520 //        }
30521         
30522         this._verticalLayoutItems( items , isInstant );
30523         
30524     },
30525     
30526     _verticalLayoutItems : function ( items , isInstant)
30527     {
30528         if ( !items || !items.length ) {
30529             return;
30530         }
30531         
30532         var standard = [
30533             ['xs', 'xs', 'xs', 'tall'],
30534             ['xs', 'xs', 'tall'],
30535             ['xs', 'xs', 'sm'],
30536             ['xs', 'xs', 'xs'],
30537             ['xs', 'tall'],
30538             ['xs', 'sm'],
30539             ['xs', 'xs'],
30540             ['xs'],
30541             
30542             ['sm', 'xs', 'xs'],
30543             ['sm', 'xs'],
30544             ['sm'],
30545             
30546             ['tall', 'xs', 'xs', 'xs'],
30547             ['tall', 'xs', 'xs'],
30548             ['tall', 'xs'],
30549             ['tall']
30550             
30551         ];
30552         
30553         var queue = [];
30554         
30555         var boxes = [];
30556         
30557         var box = [];
30558         
30559         Roo.each(items, function(item, k){
30560             
30561             switch (item.size) {
30562                 // these layouts take up a full box,
30563                 case 'md' :
30564                 case 'md-left' :
30565                 case 'md-right' :
30566                 case 'wide' :
30567                     
30568                     if(box.length){
30569                         boxes.push(box);
30570                         box = [];
30571                     }
30572                     
30573                     boxes.push([item]);
30574                     
30575                     break;
30576                     
30577                 case 'xs' :
30578                 case 'sm' :
30579                 case 'tall' :
30580                     
30581                     box.push(item);
30582                     
30583                     break;
30584                 default :
30585                     break;
30586                     
30587             }
30588             
30589         }, this);
30590         
30591         if(box.length){
30592             boxes.push(box);
30593             box = [];
30594         }
30595         
30596         var filterPattern = function(box, length)
30597         {
30598             if(!box.length){
30599                 return;
30600             }
30601             
30602             var match = false;
30603             
30604             var pattern = box.slice(0, length);
30605             
30606             var format = [];
30607             
30608             Roo.each(pattern, function(i){
30609                 format.push(i.size);
30610             }, this);
30611             
30612             Roo.each(standard, function(s){
30613                 
30614                 if(String(s) != String(format)){
30615                     return;
30616                 }
30617                 
30618                 match = true;
30619                 return false;
30620                 
30621             }, this);
30622             
30623             if(!match && length == 1){
30624                 return;
30625             }
30626             
30627             if(!match){
30628                 filterPattern(box, length - 1);
30629                 return;
30630             }
30631                 
30632             queue.push(pattern);
30633
30634             box = box.slice(length, box.length);
30635
30636             filterPattern(box, 4);
30637
30638             return;
30639             
30640         }
30641         
30642         Roo.each(boxes, function(box, k){
30643             
30644             if(!box.length){
30645                 return;
30646             }
30647             
30648             if(box.length == 1){
30649                 queue.push(box);
30650                 return;
30651             }
30652             
30653             filterPattern(box, 4);
30654             
30655         }, this);
30656         
30657         this._processVerticalLayoutQueue( queue, isInstant );
30658         
30659     },
30660     
30661 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30662 //    {
30663 //        if ( !items || !items.length ) {
30664 //            return;
30665 //        }
30666 //
30667 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30668 //        
30669 //    },
30670     
30671     _horizontalLayoutItems : function ( items , isInstant)
30672     {
30673         if ( !items || !items.length || items.length < 3) {
30674             return;
30675         }
30676         
30677         items.reverse();
30678         
30679         var eItems = items.slice(0, 3);
30680         
30681         items = items.slice(3, items.length);
30682         
30683         var standard = [
30684             ['xs', 'xs', 'xs', 'wide'],
30685             ['xs', 'xs', 'wide'],
30686             ['xs', 'xs', 'sm'],
30687             ['xs', 'xs', 'xs'],
30688             ['xs', 'wide'],
30689             ['xs', 'sm'],
30690             ['xs', 'xs'],
30691             ['xs'],
30692             
30693             ['sm', 'xs', 'xs'],
30694             ['sm', 'xs'],
30695             ['sm'],
30696             
30697             ['wide', 'xs', 'xs', 'xs'],
30698             ['wide', 'xs', 'xs'],
30699             ['wide', 'xs'],
30700             ['wide'],
30701             
30702             ['wide-thin']
30703         ];
30704         
30705         var queue = [];
30706         
30707         var boxes = [];
30708         
30709         var box = [];
30710         
30711         Roo.each(items, function(item, k){
30712             
30713             switch (item.size) {
30714                 case 'md' :
30715                 case 'md-left' :
30716                 case 'md-right' :
30717                 case 'tall' :
30718                     
30719                     if(box.length){
30720                         boxes.push(box);
30721                         box = [];
30722                     }
30723                     
30724                     boxes.push([item]);
30725                     
30726                     break;
30727                     
30728                 case 'xs' :
30729                 case 'sm' :
30730                 case 'wide' :
30731                 case 'wide-thin' :
30732                     
30733                     box.push(item);
30734                     
30735                     break;
30736                 default :
30737                     break;
30738                     
30739             }
30740             
30741         }, this);
30742         
30743         if(box.length){
30744             boxes.push(box);
30745             box = [];
30746         }
30747         
30748         var filterPattern = function(box, length)
30749         {
30750             if(!box.length){
30751                 return;
30752             }
30753             
30754             var match = false;
30755             
30756             var pattern = box.slice(0, length);
30757             
30758             var format = [];
30759             
30760             Roo.each(pattern, function(i){
30761                 format.push(i.size);
30762             }, this);
30763             
30764             Roo.each(standard, function(s){
30765                 
30766                 if(String(s) != String(format)){
30767                     return;
30768                 }
30769                 
30770                 match = true;
30771                 return false;
30772                 
30773             }, this);
30774             
30775             if(!match && length == 1){
30776                 return;
30777             }
30778             
30779             if(!match){
30780                 filterPattern(box, length - 1);
30781                 return;
30782             }
30783                 
30784             queue.push(pattern);
30785
30786             box = box.slice(length, box.length);
30787
30788             filterPattern(box, 4);
30789
30790             return;
30791             
30792         }
30793         
30794         Roo.each(boxes, function(box, k){
30795             
30796             if(!box.length){
30797                 return;
30798             }
30799             
30800             if(box.length == 1){
30801                 queue.push(box);
30802                 return;
30803             }
30804             
30805             filterPattern(box, 4);
30806             
30807         }, this);
30808         
30809         
30810         var prune = [];
30811         
30812         var pos = this.el.getBox(true);
30813         
30814         var minX = pos.x;
30815         
30816         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30817         
30818         var hit_end = false;
30819         
30820         Roo.each(queue, function(box){
30821             
30822             if(hit_end){
30823                 
30824                 Roo.each(box, function(b){
30825                 
30826                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30827                     b.el.hide();
30828
30829                 }, this);
30830
30831                 return;
30832             }
30833             
30834             var mx = 0;
30835             
30836             Roo.each(box, function(b){
30837                 
30838                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30839                 b.el.show();
30840
30841                 mx = Math.max(mx, b.x);
30842                 
30843             }, this);
30844             
30845             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30846             
30847             if(maxX < minX){
30848                 
30849                 Roo.each(box, function(b){
30850                 
30851                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30852                     b.el.hide();
30853                     
30854                 }, this);
30855                 
30856                 hit_end = true;
30857                 
30858                 return;
30859             }
30860             
30861             prune.push(box);
30862             
30863         }, this);
30864         
30865         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30866     },
30867     
30868     /** Sets position of item in DOM
30869     * @param {Element} item
30870     * @param {Number} x - horizontal position
30871     * @param {Number} y - vertical position
30872     * @param {Boolean} isInstant - disables transitions
30873     */
30874     _processVerticalLayoutQueue : function( queue, isInstant )
30875     {
30876         var pos = this.el.getBox(true);
30877         var x = pos.x;
30878         var y = pos.y;
30879         var maxY = [];
30880         
30881         for (var i = 0; i < this.cols; i++){
30882             maxY[i] = pos.y;
30883         }
30884         
30885         Roo.each(queue, function(box, k){
30886             
30887             var col = k % this.cols;
30888             
30889             Roo.each(box, function(b,kk){
30890                 
30891                 b.el.position('absolute');
30892                 
30893                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30894                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30895                 
30896                 if(b.size == 'md-left' || b.size == 'md-right'){
30897                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30898                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30899                 }
30900                 
30901                 b.el.setWidth(width);
30902                 b.el.setHeight(height);
30903                 // iframe?
30904                 b.el.select('iframe',true).setSize(width,height);
30905                 
30906             }, this);
30907             
30908             for (var i = 0; i < this.cols; i++){
30909                 
30910                 if(maxY[i] < maxY[col]){
30911                     col = i;
30912                     continue;
30913                 }
30914                 
30915                 col = Math.min(col, i);
30916                 
30917             }
30918             
30919             x = pos.x + col * (this.colWidth + this.padWidth);
30920             
30921             y = maxY[col];
30922             
30923             var positions = [];
30924             
30925             switch (box.length){
30926                 case 1 :
30927                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30928                     break;
30929                 case 2 :
30930                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30931                     break;
30932                 case 3 :
30933                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30934                     break;
30935                 case 4 :
30936                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30937                     break;
30938                 default :
30939                     break;
30940             }
30941             
30942             Roo.each(box, function(b,kk){
30943                 
30944                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30945                 
30946                 var sz = b.el.getSize();
30947                 
30948                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30949                 
30950             }, this);
30951             
30952         }, this);
30953         
30954         var mY = 0;
30955         
30956         for (var i = 0; i < this.cols; i++){
30957             mY = Math.max(mY, maxY[i]);
30958         }
30959         
30960         this.el.setHeight(mY - pos.y);
30961         
30962     },
30963     
30964 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30965 //    {
30966 //        var pos = this.el.getBox(true);
30967 //        var x = pos.x;
30968 //        var y = pos.y;
30969 //        var maxX = pos.right;
30970 //        
30971 //        var maxHeight = 0;
30972 //        
30973 //        Roo.each(items, function(item, k){
30974 //            
30975 //            var c = k % 2;
30976 //            
30977 //            item.el.position('absolute');
30978 //                
30979 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30980 //
30981 //            item.el.setWidth(width);
30982 //
30983 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30984 //
30985 //            item.el.setHeight(height);
30986 //            
30987 //            if(c == 0){
30988 //                item.el.setXY([x, y], isInstant ? false : true);
30989 //            } else {
30990 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30991 //            }
30992 //            
30993 //            y = y + height + this.alternativePadWidth;
30994 //            
30995 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30996 //            
30997 //        }, this);
30998 //        
30999 //        this.el.setHeight(maxHeight);
31000 //        
31001 //    },
31002     
31003     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31004     {
31005         var pos = this.el.getBox(true);
31006         
31007         var minX = pos.x;
31008         var minY = pos.y;
31009         
31010         var maxX = pos.right;
31011         
31012         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31013         
31014         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31015         
31016         Roo.each(queue, function(box, k){
31017             
31018             Roo.each(box, function(b, kk){
31019                 
31020                 b.el.position('absolute');
31021                 
31022                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31023                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31024                 
31025                 if(b.size == 'md-left' || b.size == 'md-right'){
31026                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31027                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31028                 }
31029                 
31030                 b.el.setWidth(width);
31031                 b.el.setHeight(height);
31032                 
31033             }, this);
31034             
31035             if(!box.length){
31036                 return;
31037             }
31038             
31039             var positions = [];
31040             
31041             switch (box.length){
31042                 case 1 :
31043                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31044                     break;
31045                 case 2 :
31046                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31047                     break;
31048                 case 3 :
31049                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31050                     break;
31051                 case 4 :
31052                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31053                     break;
31054                 default :
31055                     break;
31056             }
31057             
31058             Roo.each(box, function(b,kk){
31059                 
31060                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31061                 
31062                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31063                 
31064             }, this);
31065             
31066         }, this);
31067         
31068     },
31069     
31070     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31071     {
31072         Roo.each(eItems, function(b,k){
31073             
31074             b.size = (k == 0) ? 'sm' : 'xs';
31075             b.x = (k == 0) ? 2 : 1;
31076             b.y = (k == 0) ? 2 : 1;
31077             
31078             b.el.position('absolute');
31079             
31080             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31081                 
31082             b.el.setWidth(width);
31083             
31084             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31085             
31086             b.el.setHeight(height);
31087             
31088         }, this);
31089
31090         var positions = [];
31091         
31092         positions.push({
31093             x : maxX - this.unitWidth * 2 - this.gutter,
31094             y : minY
31095         });
31096         
31097         positions.push({
31098             x : maxX - this.unitWidth,
31099             y : minY + (this.unitWidth + this.gutter) * 2
31100         });
31101         
31102         positions.push({
31103             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31104             y : minY
31105         });
31106         
31107         Roo.each(eItems, function(b,k){
31108             
31109             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31110
31111         }, this);
31112         
31113     },
31114     
31115     getVerticalOneBoxColPositions : function(x, y, box)
31116     {
31117         var pos = [];
31118         
31119         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31120         
31121         if(box[0].size == 'md-left'){
31122             rand = 0;
31123         }
31124         
31125         if(box[0].size == 'md-right'){
31126             rand = 1;
31127         }
31128         
31129         pos.push({
31130             x : x + (this.unitWidth + this.gutter) * rand,
31131             y : y
31132         });
31133         
31134         return pos;
31135     },
31136     
31137     getVerticalTwoBoxColPositions : function(x, y, box)
31138     {
31139         var pos = [];
31140         
31141         if(box[0].size == 'xs'){
31142             
31143             pos.push({
31144                 x : x,
31145                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31146             });
31147
31148             pos.push({
31149                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31150                 y : y
31151             });
31152             
31153             return pos;
31154             
31155         }
31156         
31157         pos.push({
31158             x : x,
31159             y : y
31160         });
31161
31162         pos.push({
31163             x : x + (this.unitWidth + this.gutter) * 2,
31164             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31165         });
31166         
31167         return pos;
31168         
31169     },
31170     
31171     getVerticalThreeBoxColPositions : function(x, y, box)
31172     {
31173         var pos = [];
31174         
31175         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31176             
31177             pos.push({
31178                 x : x,
31179                 y : y
31180             });
31181
31182             pos.push({
31183                 x : x + (this.unitWidth + this.gutter) * 1,
31184                 y : y
31185             });
31186             
31187             pos.push({
31188                 x : x + (this.unitWidth + this.gutter) * 2,
31189                 y : y
31190             });
31191             
31192             return pos;
31193             
31194         }
31195         
31196         if(box[0].size == 'xs' && box[1].size == 'xs'){
31197             
31198             pos.push({
31199                 x : x,
31200                 y : y
31201             });
31202
31203             pos.push({
31204                 x : x,
31205                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31206             });
31207             
31208             pos.push({
31209                 x : x + (this.unitWidth + this.gutter) * 1,
31210                 y : y
31211             });
31212             
31213             return pos;
31214             
31215         }
31216         
31217         pos.push({
31218             x : x,
31219             y : y
31220         });
31221
31222         pos.push({
31223             x : x + (this.unitWidth + this.gutter) * 2,
31224             y : y
31225         });
31226
31227         pos.push({
31228             x : x + (this.unitWidth + this.gutter) * 2,
31229             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31230         });
31231             
31232         return pos;
31233         
31234     },
31235     
31236     getVerticalFourBoxColPositions : function(x, y, box)
31237     {
31238         var pos = [];
31239         
31240         if(box[0].size == 'xs'){
31241             
31242             pos.push({
31243                 x : x,
31244                 y : y
31245             });
31246
31247             pos.push({
31248                 x : x,
31249                 y : y + (this.unitHeight + this.gutter) * 1
31250             });
31251             
31252             pos.push({
31253                 x : x,
31254                 y : y + (this.unitHeight + this.gutter) * 2
31255             });
31256             
31257             pos.push({
31258                 x : x + (this.unitWidth + this.gutter) * 1,
31259                 y : y
31260             });
31261             
31262             return pos;
31263             
31264         }
31265         
31266         pos.push({
31267             x : x,
31268             y : y
31269         });
31270
31271         pos.push({
31272             x : x + (this.unitWidth + this.gutter) * 2,
31273             y : y
31274         });
31275
31276         pos.push({
31277             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31278             y : y + (this.unitHeight + this.gutter) * 1
31279         });
31280
31281         pos.push({
31282             x : x + (this.unitWidth + this.gutter) * 2,
31283             y : y + (this.unitWidth + this.gutter) * 2
31284         });
31285
31286         return pos;
31287         
31288     },
31289     
31290     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31291     {
31292         var pos = [];
31293         
31294         if(box[0].size == 'md-left'){
31295             pos.push({
31296                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31297                 y : minY
31298             });
31299             
31300             return pos;
31301         }
31302         
31303         if(box[0].size == 'md-right'){
31304             pos.push({
31305                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31306                 y : minY + (this.unitWidth + this.gutter) * 1
31307             });
31308             
31309             return pos;
31310         }
31311         
31312         var rand = Math.floor(Math.random() * (4 - box[0].y));
31313         
31314         pos.push({
31315             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31316             y : minY + (this.unitWidth + this.gutter) * rand
31317         });
31318         
31319         return pos;
31320         
31321     },
31322     
31323     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31324     {
31325         var pos = [];
31326         
31327         if(box[0].size == 'xs'){
31328             
31329             pos.push({
31330                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31331                 y : minY
31332             });
31333
31334             pos.push({
31335                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31336                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31337             });
31338             
31339             return pos;
31340             
31341         }
31342         
31343         pos.push({
31344             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31345             y : minY
31346         });
31347
31348         pos.push({
31349             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31350             y : minY + (this.unitWidth + this.gutter) * 2
31351         });
31352         
31353         return pos;
31354         
31355     },
31356     
31357     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31358     {
31359         var pos = [];
31360         
31361         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31362             
31363             pos.push({
31364                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31365                 y : minY
31366             });
31367
31368             pos.push({
31369                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31370                 y : minY + (this.unitWidth + this.gutter) * 1
31371             });
31372             
31373             pos.push({
31374                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31375                 y : minY + (this.unitWidth + this.gutter) * 2
31376             });
31377             
31378             return pos;
31379             
31380         }
31381         
31382         if(box[0].size == 'xs' && box[1].size == 'xs'){
31383             
31384             pos.push({
31385                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31386                 y : minY
31387             });
31388
31389             pos.push({
31390                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31391                 y : minY
31392             });
31393             
31394             pos.push({
31395                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31396                 y : minY + (this.unitWidth + this.gutter) * 1
31397             });
31398             
31399             return pos;
31400             
31401         }
31402         
31403         pos.push({
31404             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31405             y : minY
31406         });
31407
31408         pos.push({
31409             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31410             y : minY + (this.unitWidth + this.gutter) * 2
31411         });
31412
31413         pos.push({
31414             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31415             y : minY + (this.unitWidth + this.gutter) * 2
31416         });
31417             
31418         return pos;
31419         
31420     },
31421     
31422     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31423     {
31424         var pos = [];
31425         
31426         if(box[0].size == 'xs'){
31427             
31428             pos.push({
31429                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31430                 y : minY
31431             });
31432
31433             pos.push({
31434                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31435                 y : minY
31436             });
31437             
31438             pos.push({
31439                 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),
31440                 y : minY
31441             });
31442             
31443             pos.push({
31444                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31445                 y : minY + (this.unitWidth + this.gutter) * 1
31446             });
31447             
31448             return pos;
31449             
31450         }
31451         
31452         pos.push({
31453             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31454             y : minY
31455         });
31456         
31457         pos.push({
31458             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31459             y : minY + (this.unitWidth + this.gutter) * 2
31460         });
31461         
31462         pos.push({
31463             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31464             y : minY + (this.unitWidth + this.gutter) * 2
31465         });
31466         
31467         pos.push({
31468             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),
31469             y : minY + (this.unitWidth + this.gutter) * 2
31470         });
31471
31472         return pos;
31473         
31474     },
31475     
31476     /**
31477     * remove a Masonry Brick
31478     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31479     */
31480     removeBrick : function(brick_id)
31481     {
31482         if (!brick_id) {
31483             return;
31484         }
31485         
31486         for (var i = 0; i<this.bricks.length; i++) {
31487             if (this.bricks[i].id == brick_id) {
31488                 this.bricks.splice(i,1);
31489                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31490                 this.initial();
31491             }
31492         }
31493     },
31494     
31495     /**
31496     * adds a Masonry Brick
31497     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31498     */
31499     addBrick : function(cfg)
31500     {
31501         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31502         //this.register(cn);
31503         cn.parentId = this.id;
31504         cn.onRender(this.el, null);
31505         return cn;
31506     },
31507     
31508     /**
31509     * register a Masonry Brick
31510     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31511     */
31512     
31513     register : function(brick)
31514     {
31515         this.bricks.push(brick);
31516         brick.masonryId = this.id;
31517     },
31518     
31519     /**
31520     * clear all the Masonry Brick
31521     */
31522     clearAll : function()
31523     {
31524         this.bricks = [];
31525         //this.getChildContainer().dom.innerHTML = "";
31526         this.el.dom.innerHTML = '';
31527     },
31528     
31529     getSelected : function()
31530     {
31531         if (!this.selectedBrick) {
31532             return false;
31533         }
31534         
31535         return this.selectedBrick;
31536     }
31537 });
31538
31539 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31540     
31541     groups: {},
31542      /**
31543     * register a Masonry Layout
31544     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31545     */
31546     
31547     register : function(layout)
31548     {
31549         this.groups[layout.id] = layout;
31550     },
31551     /**
31552     * fetch a  Masonry Layout based on the masonry layout ID
31553     * @param {string} the masonry layout to add
31554     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31555     */
31556     
31557     get: function(layout_id) {
31558         if (typeof(this.groups[layout_id]) == 'undefined') {
31559             return false;
31560         }
31561         return this.groups[layout_id] ;
31562     }
31563     
31564     
31565     
31566 });
31567
31568  
31569
31570  /**
31571  *
31572  * This is based on 
31573  * http://masonry.desandro.com
31574  *
31575  * The idea is to render all the bricks based on vertical width...
31576  *
31577  * The original code extends 'outlayer' - we might need to use that....
31578  * 
31579  */
31580
31581
31582 /**
31583  * @class Roo.bootstrap.LayoutMasonryAuto
31584  * @extends Roo.bootstrap.Component
31585  * Bootstrap Layout Masonry class
31586  * 
31587  * @constructor
31588  * Create a new Element
31589  * @param {Object} config The config object
31590  */
31591
31592 Roo.bootstrap.LayoutMasonryAuto = function(config){
31593     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31594 };
31595
31596 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31597     
31598       /**
31599      * @cfg {Boolean} isFitWidth  - resize the width..
31600      */   
31601     isFitWidth : false,  // options..
31602     /**
31603      * @cfg {Boolean} isOriginLeft = left align?
31604      */   
31605     isOriginLeft : true,
31606     /**
31607      * @cfg {Boolean} isOriginTop = top align?
31608      */   
31609     isOriginTop : false,
31610     /**
31611      * @cfg {Boolean} isLayoutInstant = no animation?
31612      */   
31613     isLayoutInstant : false, // needed?
31614     /**
31615      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31616      */   
31617     isResizingContainer : true,
31618     /**
31619      * @cfg {Number} columnWidth  width of the columns 
31620      */   
31621     
31622     columnWidth : 0,
31623     
31624     /**
31625      * @cfg {Number} maxCols maximum number of columns
31626      */   
31627     
31628     maxCols: 0,
31629     /**
31630      * @cfg {Number} padHeight padding below box..
31631      */   
31632     
31633     padHeight : 10, 
31634     
31635     /**
31636      * @cfg {Boolean} isAutoInitial defalut true
31637      */   
31638     
31639     isAutoInitial : true, 
31640     
31641     // private?
31642     gutter : 0,
31643     
31644     containerWidth: 0,
31645     initialColumnWidth : 0,
31646     currentSize : null,
31647     
31648     colYs : null, // array.
31649     maxY : 0,
31650     padWidth: 10,
31651     
31652     
31653     tag: 'div',
31654     cls: '',
31655     bricks: null, //CompositeElement
31656     cols : 0, // array?
31657     // element : null, // wrapped now this.el
31658     _isLayoutInited : null, 
31659     
31660     
31661     getAutoCreate : function(){
31662         
31663         var cfg = {
31664             tag: this.tag,
31665             cls: 'blog-masonary-wrapper ' + this.cls,
31666             cn : {
31667                 cls : 'mas-boxes masonary'
31668             }
31669         };
31670         
31671         return cfg;
31672     },
31673     
31674     getChildContainer: function( )
31675     {
31676         if (this.boxesEl) {
31677             return this.boxesEl;
31678         }
31679         
31680         this.boxesEl = this.el.select('.mas-boxes').first();
31681         
31682         return this.boxesEl;
31683     },
31684     
31685     
31686     initEvents : function()
31687     {
31688         var _this = this;
31689         
31690         if(this.isAutoInitial){
31691             Roo.log('hook children rendered');
31692             this.on('childrenrendered', function() {
31693                 Roo.log('children rendered');
31694                 _this.initial();
31695             } ,this);
31696         }
31697         
31698     },
31699     
31700     initial : function()
31701     {
31702         this.reloadItems();
31703
31704         this.currentSize = this.el.getBox(true);
31705
31706         /// was window resize... - let's see if this works..
31707         Roo.EventManager.onWindowResize(this.resize, this); 
31708
31709         if(!this.isAutoInitial){
31710             this.layout();
31711             return;
31712         }
31713         
31714         this.layout.defer(500,this);
31715     },
31716     
31717     reloadItems: function()
31718     {
31719         this.bricks = this.el.select('.masonry-brick', true);
31720         
31721         this.bricks.each(function(b) {
31722             //Roo.log(b.getSize());
31723             if (!b.attr('originalwidth')) {
31724                 b.attr('originalwidth',  b.getSize().width);
31725             }
31726             
31727         });
31728         
31729         Roo.log(this.bricks.elements.length);
31730     },
31731     
31732     resize : function()
31733     {
31734         Roo.log('resize');
31735         var cs = this.el.getBox(true);
31736         
31737         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31738             Roo.log("no change in with or X");
31739             return;
31740         }
31741         this.currentSize = cs;
31742         this.layout();
31743     },
31744     
31745     layout : function()
31746     {
31747          Roo.log('layout');
31748         this._resetLayout();
31749         //this._manageStamps();
31750       
31751         // don't animate first layout
31752         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31753         this.layoutItems( isInstant );
31754       
31755         // flag for initalized
31756         this._isLayoutInited = true;
31757     },
31758     
31759     layoutItems : function( isInstant )
31760     {
31761         //var items = this._getItemsForLayout( this.items );
31762         // original code supports filtering layout items.. we just ignore it..
31763         
31764         this._layoutItems( this.bricks , isInstant );
31765       
31766         this._postLayout();
31767     },
31768     _layoutItems : function ( items , isInstant)
31769     {
31770        //this.fireEvent( 'layout', this, items );
31771     
31772
31773         if ( !items || !items.elements.length ) {
31774           // no items, emit event with empty array
31775             return;
31776         }
31777
31778         var queue = [];
31779         items.each(function(item) {
31780             Roo.log("layout item");
31781             Roo.log(item);
31782             // get x/y object from method
31783             var position = this._getItemLayoutPosition( item );
31784             // enqueue
31785             position.item = item;
31786             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31787             queue.push( position );
31788         }, this);
31789       
31790         this._processLayoutQueue( queue );
31791     },
31792     /** Sets position of item in DOM
31793     * @param {Element} item
31794     * @param {Number} x - horizontal position
31795     * @param {Number} y - vertical position
31796     * @param {Boolean} isInstant - disables transitions
31797     */
31798     _processLayoutQueue : function( queue )
31799     {
31800         for ( var i=0, len = queue.length; i < len; i++ ) {
31801             var obj = queue[i];
31802             obj.item.position('absolute');
31803             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31804         }
31805     },
31806       
31807     
31808     /**
31809     * Any logic you want to do after each layout,
31810     * i.e. size the container
31811     */
31812     _postLayout : function()
31813     {
31814         this.resizeContainer();
31815     },
31816     
31817     resizeContainer : function()
31818     {
31819         if ( !this.isResizingContainer ) {
31820             return;
31821         }
31822         var size = this._getContainerSize();
31823         if ( size ) {
31824             this.el.setSize(size.width,size.height);
31825             this.boxesEl.setSize(size.width,size.height);
31826         }
31827     },
31828     
31829     
31830     
31831     _resetLayout : function()
31832     {
31833         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31834         this.colWidth = this.el.getWidth();
31835         //this.gutter = this.el.getWidth(); 
31836         
31837         this.measureColumns();
31838
31839         // reset column Y
31840         var i = this.cols;
31841         this.colYs = [];
31842         while (i--) {
31843             this.colYs.push( 0 );
31844         }
31845     
31846         this.maxY = 0;
31847     },
31848
31849     measureColumns : function()
31850     {
31851         this.getContainerWidth();
31852       // if columnWidth is 0, default to outerWidth of first item
31853         if ( !this.columnWidth ) {
31854             var firstItem = this.bricks.first();
31855             Roo.log(firstItem);
31856             this.columnWidth  = this.containerWidth;
31857             if (firstItem && firstItem.attr('originalwidth') ) {
31858                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31859             }
31860             // columnWidth fall back to item of first element
31861             Roo.log("set column width?");
31862                         this.initialColumnWidth = this.columnWidth  ;
31863
31864             // if first elem has no width, default to size of container
31865             
31866         }
31867         
31868         
31869         if (this.initialColumnWidth) {
31870             this.columnWidth = this.initialColumnWidth;
31871         }
31872         
31873         
31874             
31875         // column width is fixed at the top - however if container width get's smaller we should
31876         // reduce it...
31877         
31878         // this bit calcs how man columns..
31879             
31880         var columnWidth = this.columnWidth += this.gutter;
31881       
31882         // calculate columns
31883         var containerWidth = this.containerWidth + this.gutter;
31884         
31885         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31886         // fix rounding errors, typically with gutters
31887         var excess = columnWidth - containerWidth % columnWidth;
31888         
31889         
31890         // if overshoot is less than a pixel, round up, otherwise floor it
31891         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31892         cols = Math[ mathMethod ]( cols );
31893         this.cols = Math.max( cols, 1 );
31894         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31895         
31896          // padding positioning..
31897         var totalColWidth = this.cols * this.columnWidth;
31898         var padavail = this.containerWidth - totalColWidth;
31899         // so for 2 columns - we need 3 'pads'
31900         
31901         var padNeeded = (1+this.cols) * this.padWidth;
31902         
31903         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31904         
31905         this.columnWidth += padExtra
31906         //this.padWidth = Math.floor(padavail /  ( this.cols));
31907         
31908         // adjust colum width so that padding is fixed??
31909         
31910         // we have 3 columns ... total = width * 3
31911         // we have X left over... that should be used by 
31912         
31913         //if (this.expandC) {
31914             
31915         //}
31916         
31917         
31918         
31919     },
31920     
31921     getContainerWidth : function()
31922     {
31923        /* // container is parent if fit width
31924         var container = this.isFitWidth ? this.element.parentNode : this.element;
31925         // check that this.size and size are there
31926         // IE8 triggers resize on body size change, so they might not be
31927         
31928         var size = getSize( container );  //FIXME
31929         this.containerWidth = size && size.innerWidth; //FIXME
31930         */
31931          
31932         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31933         
31934     },
31935     
31936     _getItemLayoutPosition : function( item )  // what is item?
31937     {
31938         // we resize the item to our columnWidth..
31939       
31940         item.setWidth(this.columnWidth);
31941         item.autoBoxAdjust  = false;
31942         
31943         var sz = item.getSize();
31944  
31945         // how many columns does this brick span
31946         var remainder = this.containerWidth % this.columnWidth;
31947         
31948         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31949         // round if off by 1 pixel, otherwise use ceil
31950         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31951         colSpan = Math.min( colSpan, this.cols );
31952         
31953         // normally this should be '1' as we dont' currently allow multi width columns..
31954         
31955         var colGroup = this._getColGroup( colSpan );
31956         // get the minimum Y value from the columns
31957         var minimumY = Math.min.apply( Math, colGroup );
31958         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31959         
31960         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31961          
31962         // position the brick
31963         var position = {
31964             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31965             y: this.currentSize.y + minimumY + this.padHeight
31966         };
31967         
31968         Roo.log(position);
31969         // apply setHeight to necessary columns
31970         var setHeight = minimumY + sz.height + this.padHeight;
31971         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31972         
31973         var setSpan = this.cols + 1 - colGroup.length;
31974         for ( var i = 0; i < setSpan; i++ ) {
31975           this.colYs[ shortColIndex + i ] = setHeight ;
31976         }
31977       
31978         return position;
31979     },
31980     
31981     /**
31982      * @param {Number} colSpan - number of columns the element spans
31983      * @returns {Array} colGroup
31984      */
31985     _getColGroup : function( colSpan )
31986     {
31987         if ( colSpan < 2 ) {
31988           // if brick spans only one column, use all the column Ys
31989           return this.colYs;
31990         }
31991       
31992         var colGroup = [];
31993         // how many different places could this brick fit horizontally
31994         var groupCount = this.cols + 1 - colSpan;
31995         // for each group potential horizontal position
31996         for ( var i = 0; i < groupCount; i++ ) {
31997           // make an array of colY values for that one group
31998           var groupColYs = this.colYs.slice( i, i + colSpan );
31999           // and get the max value of the array
32000           colGroup[i] = Math.max.apply( Math, groupColYs );
32001         }
32002         return colGroup;
32003     },
32004     /*
32005     _manageStamp : function( stamp )
32006     {
32007         var stampSize =  stamp.getSize();
32008         var offset = stamp.getBox();
32009         // get the columns that this stamp affects
32010         var firstX = this.isOriginLeft ? offset.x : offset.right;
32011         var lastX = firstX + stampSize.width;
32012         var firstCol = Math.floor( firstX / this.columnWidth );
32013         firstCol = Math.max( 0, firstCol );
32014         
32015         var lastCol = Math.floor( lastX / this.columnWidth );
32016         // lastCol should not go over if multiple of columnWidth #425
32017         lastCol -= lastX % this.columnWidth ? 0 : 1;
32018         lastCol = Math.min( this.cols - 1, lastCol );
32019         
32020         // set colYs to bottom of the stamp
32021         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32022             stampSize.height;
32023             
32024         for ( var i = firstCol; i <= lastCol; i++ ) {
32025           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32026         }
32027     },
32028     */
32029     
32030     _getContainerSize : function()
32031     {
32032         this.maxY = Math.max.apply( Math, this.colYs );
32033         var size = {
32034             height: this.maxY
32035         };
32036       
32037         if ( this.isFitWidth ) {
32038             size.width = this._getContainerFitWidth();
32039         }
32040       
32041         return size;
32042     },
32043     
32044     _getContainerFitWidth : function()
32045     {
32046         var unusedCols = 0;
32047         // count unused columns
32048         var i = this.cols;
32049         while ( --i ) {
32050           if ( this.colYs[i] !== 0 ) {
32051             break;
32052           }
32053           unusedCols++;
32054         }
32055         // fit container to columns that have been used
32056         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32057     },
32058     
32059     needsResizeLayout : function()
32060     {
32061         var previousWidth = this.containerWidth;
32062         this.getContainerWidth();
32063         return previousWidth !== this.containerWidth;
32064     }
32065  
32066 });
32067
32068  
32069
32070  /*
32071  * - LGPL
32072  *
32073  * element
32074  * 
32075  */
32076
32077 /**
32078  * @class Roo.bootstrap.MasonryBrick
32079  * @extends Roo.bootstrap.Component
32080  * Bootstrap MasonryBrick class
32081  * 
32082  * @constructor
32083  * Create a new MasonryBrick
32084  * @param {Object} config The config object
32085  */
32086
32087 Roo.bootstrap.MasonryBrick = function(config){
32088     
32089     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32090     
32091     Roo.bootstrap.MasonryBrick.register(this);
32092     
32093     this.addEvents({
32094         // raw events
32095         /**
32096          * @event click
32097          * When a MasonryBrick is clcik
32098          * @param {Roo.bootstrap.MasonryBrick} this
32099          * @param {Roo.EventObject} e
32100          */
32101         "click" : true
32102     });
32103 };
32104
32105 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32106     
32107     /**
32108      * @cfg {String} title
32109      */   
32110     title : '',
32111     /**
32112      * @cfg {String} html
32113      */   
32114     html : '',
32115     /**
32116      * @cfg {String} bgimage
32117      */   
32118     bgimage : '',
32119     /**
32120      * @cfg {String} videourl
32121      */   
32122     videourl : '',
32123     /**
32124      * @cfg {String} cls
32125      */   
32126     cls : '',
32127     /**
32128      * @cfg {String} href
32129      */   
32130     href : '',
32131     /**
32132      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32133      */   
32134     size : 'xs',
32135     
32136     /**
32137      * @cfg {String} placetitle (center|bottom)
32138      */   
32139     placetitle : '',
32140     
32141     /**
32142      * @cfg {Boolean} isFitContainer defalut true
32143      */   
32144     isFitContainer : true, 
32145     
32146     /**
32147      * @cfg {Boolean} preventDefault defalut false
32148      */   
32149     preventDefault : false, 
32150     
32151     /**
32152      * @cfg {Boolean} inverse defalut false
32153      */   
32154     maskInverse : false, 
32155     
32156     getAutoCreate : function()
32157     {
32158         if(!this.isFitContainer){
32159             return this.getSplitAutoCreate();
32160         }
32161         
32162         var cls = 'masonry-brick masonry-brick-full';
32163         
32164         if(this.href.length){
32165             cls += ' masonry-brick-link';
32166         }
32167         
32168         if(this.bgimage.length){
32169             cls += ' masonry-brick-image';
32170         }
32171         
32172         if(this.maskInverse){
32173             cls += ' mask-inverse';
32174         }
32175         
32176         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32177             cls += ' enable-mask';
32178         }
32179         
32180         if(this.size){
32181             cls += ' masonry-' + this.size + '-brick';
32182         }
32183         
32184         if(this.placetitle.length){
32185             
32186             switch (this.placetitle) {
32187                 case 'center' :
32188                     cls += ' masonry-center-title';
32189                     break;
32190                 case 'bottom' :
32191                     cls += ' masonry-bottom-title';
32192                     break;
32193                 default:
32194                     break;
32195             }
32196             
32197         } else {
32198             if(!this.html.length && !this.bgimage.length){
32199                 cls += ' masonry-center-title';
32200             }
32201
32202             if(!this.html.length && this.bgimage.length){
32203                 cls += ' masonry-bottom-title';
32204             }
32205         }
32206         
32207         if(this.cls){
32208             cls += ' ' + this.cls;
32209         }
32210         
32211         var cfg = {
32212             tag: (this.href.length) ? 'a' : 'div',
32213             cls: cls,
32214             cn: [
32215                 {
32216                     tag: 'div',
32217                     cls: 'masonry-brick-mask'
32218                 },
32219                 {
32220                     tag: 'div',
32221                     cls: 'masonry-brick-paragraph',
32222                     cn: []
32223                 }
32224             ]
32225         };
32226         
32227         if(this.href.length){
32228             cfg.href = this.href;
32229         }
32230         
32231         var cn = cfg.cn[1].cn;
32232         
32233         if(this.title.length){
32234             cn.push({
32235                 tag: 'h4',
32236                 cls: 'masonry-brick-title',
32237                 html: this.title
32238             });
32239         }
32240         
32241         if(this.html.length){
32242             cn.push({
32243                 tag: 'p',
32244                 cls: 'masonry-brick-text',
32245                 html: this.html
32246             });
32247         }
32248         
32249         if (!this.title.length && !this.html.length) {
32250             cfg.cn[1].cls += ' hide';
32251         }
32252         
32253         if(this.bgimage.length){
32254             cfg.cn.push({
32255                 tag: 'img',
32256                 cls: 'masonry-brick-image-view',
32257                 src: this.bgimage
32258             });
32259         }
32260         
32261         if(this.videourl.length){
32262             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32263             // youtube support only?
32264             cfg.cn.push({
32265                 tag: 'iframe',
32266                 cls: 'masonry-brick-image-view',
32267                 src: vurl,
32268                 frameborder : 0,
32269                 allowfullscreen : true
32270             });
32271         }
32272         
32273         return cfg;
32274         
32275     },
32276     
32277     getSplitAutoCreate : function()
32278     {
32279         var cls = 'masonry-brick masonry-brick-split';
32280         
32281         if(this.href.length){
32282             cls += ' masonry-brick-link';
32283         }
32284         
32285         if(this.bgimage.length){
32286             cls += ' masonry-brick-image';
32287         }
32288         
32289         if(this.size){
32290             cls += ' masonry-' + this.size + '-brick';
32291         }
32292         
32293         switch (this.placetitle) {
32294             case 'center' :
32295                 cls += ' masonry-center-title';
32296                 break;
32297             case 'bottom' :
32298                 cls += ' masonry-bottom-title';
32299                 break;
32300             default:
32301                 if(!this.bgimage.length){
32302                     cls += ' masonry-center-title';
32303                 }
32304
32305                 if(this.bgimage.length){
32306                     cls += ' masonry-bottom-title';
32307                 }
32308                 break;
32309         }
32310         
32311         if(this.cls){
32312             cls += ' ' + this.cls;
32313         }
32314         
32315         var cfg = {
32316             tag: (this.href.length) ? 'a' : 'div',
32317             cls: cls,
32318             cn: [
32319                 {
32320                     tag: 'div',
32321                     cls: 'masonry-brick-split-head',
32322                     cn: [
32323                         {
32324                             tag: 'div',
32325                             cls: 'masonry-brick-paragraph',
32326                             cn: []
32327                         }
32328                     ]
32329                 },
32330                 {
32331                     tag: 'div',
32332                     cls: 'masonry-brick-split-body',
32333                     cn: []
32334                 }
32335             ]
32336         };
32337         
32338         if(this.href.length){
32339             cfg.href = this.href;
32340         }
32341         
32342         if(this.title.length){
32343             cfg.cn[0].cn[0].cn.push({
32344                 tag: 'h4',
32345                 cls: 'masonry-brick-title',
32346                 html: this.title
32347             });
32348         }
32349         
32350         if(this.html.length){
32351             cfg.cn[1].cn.push({
32352                 tag: 'p',
32353                 cls: 'masonry-brick-text',
32354                 html: this.html
32355             });
32356         }
32357
32358         if(this.bgimage.length){
32359             cfg.cn[0].cn.push({
32360                 tag: 'img',
32361                 cls: 'masonry-brick-image-view',
32362                 src: this.bgimage
32363             });
32364         }
32365         
32366         if(this.videourl.length){
32367             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32368             // youtube support only?
32369             cfg.cn[0].cn.cn.push({
32370                 tag: 'iframe',
32371                 cls: 'masonry-brick-image-view',
32372                 src: vurl,
32373                 frameborder : 0,
32374                 allowfullscreen : true
32375             });
32376         }
32377         
32378         return cfg;
32379     },
32380     
32381     initEvents: function() 
32382     {
32383         switch (this.size) {
32384             case 'xs' :
32385                 this.x = 1;
32386                 this.y = 1;
32387                 break;
32388             case 'sm' :
32389                 this.x = 2;
32390                 this.y = 2;
32391                 break;
32392             case 'md' :
32393             case 'md-left' :
32394             case 'md-right' :
32395                 this.x = 3;
32396                 this.y = 3;
32397                 break;
32398             case 'tall' :
32399                 this.x = 2;
32400                 this.y = 3;
32401                 break;
32402             case 'wide' :
32403                 this.x = 3;
32404                 this.y = 2;
32405                 break;
32406             case 'wide-thin' :
32407                 this.x = 3;
32408                 this.y = 1;
32409                 break;
32410                         
32411             default :
32412                 break;
32413         }
32414         
32415         if(Roo.isTouch){
32416             this.el.on('touchstart', this.onTouchStart, this);
32417             this.el.on('touchmove', this.onTouchMove, this);
32418             this.el.on('touchend', this.onTouchEnd, this);
32419             this.el.on('contextmenu', this.onContextMenu, this);
32420         } else {
32421             this.el.on('mouseenter'  ,this.enter, this);
32422             this.el.on('mouseleave', this.leave, this);
32423             this.el.on('click', this.onClick, this);
32424         }
32425         
32426         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32427             this.parent().bricks.push(this);   
32428         }
32429         
32430     },
32431     
32432     onClick: function(e, el)
32433     {
32434         var time = this.endTimer - this.startTimer;
32435         // Roo.log(e.preventDefault());
32436         if(Roo.isTouch){
32437             if(time > 1000){
32438                 e.preventDefault();
32439                 return;
32440             }
32441         }
32442         
32443         if(!this.preventDefault){
32444             return;
32445         }
32446         
32447         e.preventDefault();
32448         
32449         if (this.activcClass != '') {
32450             this.selectBrick();
32451         }
32452         
32453         this.fireEvent('click', this);
32454     },
32455     
32456     enter: function(e, el)
32457     {
32458         e.preventDefault();
32459         
32460         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32461             return;
32462         }
32463         
32464         if(this.bgimage.length && this.html.length){
32465             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32466         }
32467     },
32468     
32469     leave: function(e, el)
32470     {
32471         e.preventDefault();
32472         
32473         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32474             return;
32475         }
32476         
32477         if(this.bgimage.length && this.html.length){
32478             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32479         }
32480     },
32481     
32482     onTouchStart: function(e, el)
32483     {
32484 //        e.preventDefault();
32485         
32486         this.touchmoved = false;
32487         
32488         if(!this.isFitContainer){
32489             return;
32490         }
32491         
32492         if(!this.bgimage.length || !this.html.length){
32493             return;
32494         }
32495         
32496         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32497         
32498         this.timer = new Date().getTime();
32499         
32500     },
32501     
32502     onTouchMove: function(e, el)
32503     {
32504         this.touchmoved = true;
32505     },
32506     
32507     onContextMenu : function(e,el)
32508     {
32509         e.preventDefault();
32510         e.stopPropagation();
32511         return false;
32512     },
32513     
32514     onTouchEnd: function(e, el)
32515     {
32516 //        e.preventDefault();
32517         
32518         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32519         
32520             this.leave(e,el);
32521             
32522             return;
32523         }
32524         
32525         if(!this.bgimage.length || !this.html.length){
32526             
32527             if(this.href.length){
32528                 window.location.href = this.href;
32529             }
32530             
32531             return;
32532         }
32533         
32534         if(!this.isFitContainer){
32535             return;
32536         }
32537         
32538         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32539         
32540         window.location.href = this.href;
32541     },
32542     
32543     //selection on single brick only
32544     selectBrick : function() {
32545         
32546         if (!this.parentId) {
32547             return;
32548         }
32549         
32550         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32551         var index = m.selectedBrick.indexOf(this.id);
32552         
32553         if ( index > -1) {
32554             m.selectedBrick.splice(index,1);
32555             this.el.removeClass(this.activeClass);
32556             return;
32557         }
32558         
32559         for(var i = 0; i < m.selectedBrick.length; i++) {
32560             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32561             b.el.removeClass(b.activeClass);
32562         }
32563         
32564         m.selectedBrick = [];
32565         
32566         m.selectedBrick.push(this.id);
32567         this.el.addClass(this.activeClass);
32568         return;
32569     }
32570     
32571 });
32572
32573 Roo.apply(Roo.bootstrap.MasonryBrick, {
32574     
32575     //groups: {},
32576     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32577      /**
32578     * register a Masonry Brick
32579     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32580     */
32581     
32582     register : function(brick)
32583     {
32584         //this.groups[brick.id] = brick;
32585         this.groups.add(brick.id, brick);
32586     },
32587     /**
32588     * fetch a  masonry brick based on the masonry brick ID
32589     * @param {string} the masonry brick to add
32590     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32591     */
32592     
32593     get: function(brick_id) 
32594     {
32595         // if (typeof(this.groups[brick_id]) == 'undefined') {
32596         //     return false;
32597         // }
32598         // return this.groups[brick_id] ;
32599         
32600         if(this.groups.key(brick_id)) {
32601             return this.groups.key(brick_id);
32602         }
32603         
32604         return false;
32605     }
32606     
32607     
32608     
32609 });
32610
32611  /*
32612  * - LGPL
32613  *
32614  * element
32615  * 
32616  */
32617
32618 /**
32619  * @class Roo.bootstrap.Brick
32620  * @extends Roo.bootstrap.Component
32621  * Bootstrap Brick class
32622  * 
32623  * @constructor
32624  * Create a new Brick
32625  * @param {Object} config The config object
32626  */
32627
32628 Roo.bootstrap.Brick = function(config){
32629     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32630     
32631     this.addEvents({
32632         // raw events
32633         /**
32634          * @event click
32635          * When a Brick is click
32636          * @param {Roo.bootstrap.Brick} this
32637          * @param {Roo.EventObject} e
32638          */
32639         "click" : true
32640     });
32641 };
32642
32643 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32644     
32645     /**
32646      * @cfg {String} title
32647      */   
32648     title : '',
32649     /**
32650      * @cfg {String} html
32651      */   
32652     html : '',
32653     /**
32654      * @cfg {String} bgimage
32655      */   
32656     bgimage : '',
32657     /**
32658      * @cfg {String} cls
32659      */   
32660     cls : '',
32661     /**
32662      * @cfg {String} href
32663      */   
32664     href : '',
32665     /**
32666      * @cfg {String} video
32667      */   
32668     video : '',
32669     /**
32670      * @cfg {Boolean} square
32671      */   
32672     square : true,
32673     
32674     getAutoCreate : function()
32675     {
32676         var cls = 'roo-brick';
32677         
32678         if(this.href.length){
32679             cls += ' roo-brick-link';
32680         }
32681         
32682         if(this.bgimage.length){
32683             cls += ' roo-brick-image';
32684         }
32685         
32686         if(!this.html.length && !this.bgimage.length){
32687             cls += ' roo-brick-center-title';
32688         }
32689         
32690         if(!this.html.length && this.bgimage.length){
32691             cls += ' roo-brick-bottom-title';
32692         }
32693         
32694         if(this.cls){
32695             cls += ' ' + this.cls;
32696         }
32697         
32698         var cfg = {
32699             tag: (this.href.length) ? 'a' : 'div',
32700             cls: cls,
32701             cn: [
32702                 {
32703                     tag: 'div',
32704                     cls: 'roo-brick-paragraph',
32705                     cn: []
32706                 }
32707             ]
32708         };
32709         
32710         if(this.href.length){
32711             cfg.href = this.href;
32712         }
32713         
32714         var cn = cfg.cn[0].cn;
32715         
32716         if(this.title.length){
32717             cn.push({
32718                 tag: 'h4',
32719                 cls: 'roo-brick-title',
32720                 html: this.title
32721             });
32722         }
32723         
32724         if(this.html.length){
32725             cn.push({
32726                 tag: 'p',
32727                 cls: 'roo-brick-text',
32728                 html: this.html
32729             });
32730         } else {
32731             cn.cls += ' hide';
32732         }
32733         
32734         if(this.bgimage.length){
32735             cfg.cn.push({
32736                 tag: 'img',
32737                 cls: 'roo-brick-image-view',
32738                 src: this.bgimage
32739             });
32740         }
32741         
32742         return cfg;
32743     },
32744     
32745     initEvents: function() 
32746     {
32747         if(this.title.length || this.html.length){
32748             this.el.on('mouseenter'  ,this.enter, this);
32749             this.el.on('mouseleave', this.leave, this);
32750         }
32751         
32752         Roo.EventManager.onWindowResize(this.resize, this); 
32753         
32754         if(this.bgimage.length){
32755             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32756             this.imageEl.on('load', this.onImageLoad, this);
32757             return;
32758         }
32759         
32760         this.resize();
32761     },
32762     
32763     onImageLoad : function()
32764     {
32765         this.resize();
32766     },
32767     
32768     resize : function()
32769     {
32770         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32771         
32772         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32773         
32774         if(this.bgimage.length){
32775             var image = this.el.select('.roo-brick-image-view', true).first();
32776             
32777             image.setWidth(paragraph.getWidth());
32778             
32779             if(this.square){
32780                 image.setHeight(paragraph.getWidth());
32781             }
32782             
32783             this.el.setHeight(image.getHeight());
32784             paragraph.setHeight(image.getHeight());
32785             
32786         }
32787         
32788     },
32789     
32790     enter: function(e, el)
32791     {
32792         e.preventDefault();
32793         
32794         if(this.bgimage.length){
32795             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32796             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32797         }
32798     },
32799     
32800     leave: function(e, el)
32801     {
32802         e.preventDefault();
32803         
32804         if(this.bgimage.length){
32805             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32806             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32807         }
32808     }
32809     
32810 });
32811
32812  
32813
32814  /*
32815  * - LGPL
32816  *
32817  * Input
32818  * 
32819  */
32820
32821 /**
32822  * @class Roo.bootstrap.NumberField
32823  * @extends Roo.bootstrap.Input
32824  * Bootstrap NumberField class
32825  * 
32826  * 
32827  * 
32828  * 
32829  * @constructor
32830  * Create a new NumberField
32831  * @param {Object} config The config object
32832  */
32833
32834 Roo.bootstrap.NumberField = function(config){
32835     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32836 };
32837
32838 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32839     
32840     /**
32841      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32842      */
32843     allowDecimals : true,
32844     /**
32845      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32846      */
32847     decimalSeparator : ".",
32848     /**
32849      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32850      */
32851     decimalPrecision : 2,
32852     /**
32853      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32854      */
32855     allowNegative : true,
32856     /**
32857      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32858      */
32859     minValue : Number.NEGATIVE_INFINITY,
32860     /**
32861      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32862      */
32863     maxValue : Number.MAX_VALUE,
32864     /**
32865      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32866      */
32867     minText : "The minimum value for this field is {0}",
32868     /**
32869      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32870      */
32871     maxText : "The maximum value for this field is {0}",
32872     /**
32873      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32874      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32875      */
32876     nanText : "{0} is not a valid number",
32877     /**
32878      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32879      */
32880     castInt : true,
32881
32882     // private
32883     initEvents : function()
32884     {   
32885         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32886         
32887         var allowed = "0123456789";
32888         
32889         if(this.allowDecimals){
32890             allowed += this.decimalSeparator;
32891         }
32892         
32893         if(this.allowNegative){
32894             allowed += "-";
32895         }
32896         
32897         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32898         
32899         var keyPress = function(e){
32900             
32901             var k = e.getKey();
32902             
32903             var c = e.getCharCode();
32904             
32905             if(
32906                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32907                     allowed.indexOf(String.fromCharCode(c)) === -1
32908             ){
32909                 e.stopEvent();
32910                 return;
32911             }
32912             
32913             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32914                 return;
32915             }
32916             
32917             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32918                 e.stopEvent();
32919             }
32920         };
32921         
32922         this.el.on("keypress", keyPress, this);
32923     },
32924     
32925     validateValue : function(value)
32926     {
32927         
32928         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32929             return false;
32930         }
32931         
32932         var num = this.parseValue(value);
32933         
32934         if(isNaN(num)){
32935             this.markInvalid(String.format(this.nanText, value));
32936             return false;
32937         }
32938         
32939         if(num < this.minValue){
32940             this.markInvalid(String.format(this.minText, this.minValue));
32941             return false;
32942         }
32943         
32944         if(num > this.maxValue){
32945             this.markInvalid(String.format(this.maxText, this.maxValue));
32946             return false;
32947         }
32948         
32949         return true;
32950     },
32951
32952     getValue : function()
32953     {
32954         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32955     },
32956
32957     parseValue : function(value)
32958     {
32959         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32960         return isNaN(value) ? '' : value;
32961     },
32962
32963     fixPrecision : function(value)
32964     {
32965         var nan = isNaN(value);
32966         
32967         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32968             return nan ? '' : value;
32969         }
32970         return parseFloat(value).toFixed(this.decimalPrecision);
32971     },
32972
32973     setValue : function(v)
32974     {
32975         v = this.fixPrecision(v);
32976         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32977     },
32978
32979     decimalPrecisionFcn : function(v)
32980     {
32981         return Math.floor(v);
32982     },
32983
32984     beforeBlur : function()
32985     {
32986         if(!this.castInt){
32987             return;
32988         }
32989         
32990         var v = this.parseValue(this.getRawValue());
32991         if(v){
32992             this.setValue(v);
32993         }
32994     }
32995     
32996 });
32997
32998  
32999
33000 /*
33001 * Licence: LGPL
33002 */
33003
33004 /**
33005  * @class Roo.bootstrap.DocumentSlider
33006  * @extends Roo.bootstrap.Component
33007  * Bootstrap DocumentSlider class
33008  * 
33009  * @constructor
33010  * Create a new DocumentViewer
33011  * @param {Object} config The config object
33012  */
33013
33014 Roo.bootstrap.DocumentSlider = function(config){
33015     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33016     
33017     this.files = [];
33018     
33019     this.addEvents({
33020         /**
33021          * @event initial
33022          * Fire after initEvent
33023          * @param {Roo.bootstrap.DocumentSlider} this
33024          */
33025         "initial" : true,
33026         /**
33027          * @event update
33028          * Fire after update
33029          * @param {Roo.bootstrap.DocumentSlider} this
33030          */
33031         "update" : true,
33032         /**
33033          * @event click
33034          * Fire after click
33035          * @param {Roo.bootstrap.DocumentSlider} this
33036          */
33037         "click" : true
33038     });
33039 };
33040
33041 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33042     
33043     files : false,
33044     
33045     indicator : 0,
33046     
33047     getAutoCreate : function()
33048     {
33049         var cfg = {
33050             tag : 'div',
33051             cls : 'roo-document-slider',
33052             cn : [
33053                 {
33054                     tag : 'div',
33055                     cls : 'roo-document-slider-header',
33056                     cn : [
33057                         {
33058                             tag : 'div',
33059                             cls : 'roo-document-slider-header-title'
33060                         }
33061                     ]
33062                 },
33063                 {
33064                     tag : 'div',
33065                     cls : 'roo-document-slider-body',
33066                     cn : [
33067                         {
33068                             tag : 'div',
33069                             cls : 'roo-document-slider-prev',
33070                             cn : [
33071                                 {
33072                                     tag : 'i',
33073                                     cls : 'fa fa-chevron-left'
33074                                 }
33075                             ]
33076                         },
33077                         {
33078                             tag : 'div',
33079                             cls : 'roo-document-slider-thumb',
33080                             cn : [
33081                                 {
33082                                     tag : 'img',
33083                                     cls : 'roo-document-slider-image'
33084                                 }
33085                             ]
33086                         },
33087                         {
33088                             tag : 'div',
33089                             cls : 'roo-document-slider-next',
33090                             cn : [
33091                                 {
33092                                     tag : 'i',
33093                                     cls : 'fa fa-chevron-right'
33094                                 }
33095                             ]
33096                         }
33097                     ]
33098                 }
33099             ]
33100         };
33101         
33102         return cfg;
33103     },
33104     
33105     initEvents : function()
33106     {
33107         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33108         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33109         
33110         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33111         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33112         
33113         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33114         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33115         
33116         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33117         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33118         
33119         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33120         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33121         
33122         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33123         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33124         
33125         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33126         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33127         
33128         this.thumbEl.on('click', this.onClick, this);
33129         
33130         this.prevIndicator.on('click', this.prev, this);
33131         
33132         this.nextIndicator.on('click', this.next, this);
33133         
33134     },
33135     
33136     initial : function()
33137     {
33138         if(this.files.length){
33139             this.indicator = 1;
33140             this.update()
33141         }
33142         
33143         this.fireEvent('initial', this);
33144     },
33145     
33146     update : function()
33147     {
33148         this.imageEl.attr('src', this.files[this.indicator - 1]);
33149         
33150         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33151         
33152         this.prevIndicator.show();
33153         
33154         if(this.indicator == 1){
33155             this.prevIndicator.hide();
33156         }
33157         
33158         this.nextIndicator.show();
33159         
33160         if(this.indicator == this.files.length){
33161             this.nextIndicator.hide();
33162         }
33163         
33164         this.thumbEl.scrollTo('top');
33165         
33166         this.fireEvent('update', this);
33167     },
33168     
33169     onClick : function(e)
33170     {
33171         e.preventDefault();
33172         
33173         this.fireEvent('click', this);
33174     },
33175     
33176     prev : function(e)
33177     {
33178         e.preventDefault();
33179         
33180         this.indicator = Math.max(1, this.indicator - 1);
33181         
33182         this.update();
33183     },
33184     
33185     next : function(e)
33186     {
33187         e.preventDefault();
33188         
33189         this.indicator = Math.min(this.files.length, this.indicator + 1);
33190         
33191         this.update();
33192     }
33193 });
33194 /*
33195  * - LGPL
33196  *
33197  * RadioSet
33198  *
33199  *
33200  */
33201
33202 /**
33203  * @class Roo.bootstrap.RadioSet
33204  * @extends Roo.bootstrap.Input
33205  * Bootstrap RadioSet class
33206  * @cfg {String} indicatorpos (left|right) default left
33207  * @cfg {Boolean} inline (true|false) inline the element (default true)
33208  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33209  * @constructor
33210  * Create a new RadioSet
33211  * @param {Object} config The config object
33212  */
33213
33214 Roo.bootstrap.RadioSet = function(config){
33215     
33216     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33217     
33218     this.radioes = [];
33219     
33220     Roo.bootstrap.RadioSet.register(this);
33221     
33222     this.addEvents({
33223         /**
33224         * @event check
33225         * Fires when the element is checked or unchecked.
33226         * @param {Roo.bootstrap.RadioSet} this This radio
33227         * @param {Roo.bootstrap.Radio} item The checked item
33228         */
33229        check : true
33230     });
33231     
33232 };
33233
33234 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33235
33236     radioes : false,
33237     
33238     inline : true,
33239     
33240     weight : '',
33241     
33242     indicatorpos : 'left',
33243     
33244     getAutoCreate : function()
33245     {
33246         var label = {
33247             tag : 'label',
33248             cls : 'roo-radio-set-label',
33249             cn : [
33250                 {
33251                     tag : 'span',
33252                     html : this.fieldLabel
33253                 }
33254             ]
33255         };
33256         
33257         if(this.indicatorpos == 'left'){
33258             label.cn.unshift({
33259                 tag : 'i',
33260                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33261                 tooltip : 'This field is required'
33262             });
33263         } else {
33264             label.cn.push({
33265                 tag : 'i',
33266                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33267                 tooltip : 'This field is required'
33268             });
33269         }
33270         
33271         var items = {
33272             tag : 'div',
33273             cls : 'roo-radio-set-items'
33274         };
33275         
33276         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33277         
33278         if (align === 'left' && this.fieldLabel.length) {
33279             
33280             items = {
33281                 cls : "roo-radio-set-right", 
33282                 cn: [
33283                     items
33284                 ]
33285             };
33286             
33287             if(this.labelWidth > 12){
33288                 label.style = "width: " + this.labelWidth + 'px';
33289             }
33290             
33291             if(this.labelWidth < 13 && this.labelmd == 0){
33292                 this.labelmd = this.labelWidth;
33293             }
33294             
33295             if(this.labellg > 0){
33296                 label.cls += ' col-lg-' + this.labellg;
33297                 items.cls += ' col-lg-' + (12 - this.labellg);
33298             }
33299             
33300             if(this.labelmd > 0){
33301                 label.cls += ' col-md-' + this.labelmd;
33302                 items.cls += ' col-md-' + (12 - this.labelmd);
33303             }
33304             
33305             if(this.labelsm > 0){
33306                 label.cls += ' col-sm-' + this.labelsm;
33307                 items.cls += ' col-sm-' + (12 - this.labelsm);
33308             }
33309             
33310             if(this.labelxs > 0){
33311                 label.cls += ' col-xs-' + this.labelxs;
33312                 items.cls += ' col-xs-' + (12 - this.labelxs);
33313             }
33314         }
33315         
33316         var cfg = {
33317             tag : 'div',
33318             cls : 'roo-radio-set',
33319             cn : [
33320                 {
33321                     tag : 'input',
33322                     cls : 'roo-radio-set-input',
33323                     type : 'hidden',
33324                     name : this.name,
33325                     value : this.value ? this.value :  ''
33326                 },
33327                 label,
33328                 items
33329             ]
33330         };
33331         
33332         if(this.weight.length){
33333             cfg.cls += ' roo-radio-' + this.weight;
33334         }
33335         
33336         if(this.inline) {
33337             cfg.cls += ' roo-radio-set-inline';
33338         }
33339         
33340         var settings=this;
33341         ['xs','sm','md','lg'].map(function(size){
33342             if (settings[size]) {
33343                 cfg.cls += ' col-' + size + '-' + settings[size];
33344             }
33345         });
33346         
33347         return cfg;
33348         
33349     },
33350
33351     initEvents : function()
33352     {
33353         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33354         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33355         
33356         if(!this.fieldLabel.length){
33357             this.labelEl.hide();
33358         }
33359         
33360         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33361         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33362         
33363         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33364         this.indicatorEl().hide();
33365         
33366         this.originalValue = this.getValue();
33367         
33368     },
33369     
33370     inputEl: function ()
33371     {
33372         return this.el.select('.roo-radio-set-input', true).first();
33373     },
33374     
33375     getChildContainer : function()
33376     {
33377         return this.itemsEl;
33378     },
33379     
33380     register : function(item)
33381     {
33382         this.radioes.push(item);
33383         
33384     },
33385     
33386     validate : function()
33387     {   
33388         var valid = false;
33389         
33390         Roo.each(this.radioes, function(i){
33391             if(!i.checked){
33392                 return;
33393             }
33394             
33395             valid = true;
33396             return false;
33397         });
33398         
33399         if(this.allowBlank) {
33400             return true;
33401         }
33402         
33403         if(this.disabled || valid){
33404             this.markValid();
33405             return true;
33406         }
33407         
33408         this.markInvalid();
33409         return false;
33410         
33411     },
33412     
33413     markValid : function()
33414     {
33415         if(this.labelEl.isVisible(true)){
33416             this.indicatorEl().hide();
33417         }
33418         
33419         this.el.removeClass([this.invalidClass, this.validClass]);
33420         this.el.addClass(this.validClass);
33421         
33422         this.fireEvent('valid', this);
33423     },
33424     
33425     markInvalid : function(msg)
33426     {
33427         if(this.allowBlank || this.disabled){
33428             return;
33429         }
33430         
33431         if(this.labelEl.isVisible(true)){
33432             this.indicatorEl().show();
33433         }
33434         
33435         this.el.removeClass([this.invalidClass, this.validClass]);
33436         this.el.addClass(this.invalidClass);
33437         
33438         this.fireEvent('invalid', this, msg);
33439         
33440     },
33441     
33442     setValue : function(v, suppressEvent)
33443     {   
33444         this.value = v;
33445         if(this.rendered){
33446             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33447         }
33448         
33449         Roo.each(this.radioes, function(i){
33450             
33451             i.checked = false;
33452             i.el.removeClass('checked');
33453             
33454             if(i.value === v || i.value.toString() === v.toString()){
33455                 i.checked = true;
33456                 i.el.addClass('checked');
33457                 
33458                 if(suppressEvent !== true){
33459                     this.fireEvent('check', this, i);
33460                 }
33461             }
33462             
33463         }, this);
33464         
33465         this.validate();
33466     },
33467     
33468     clearInvalid : function(){
33469         
33470         if(!this.el || this.preventMark){
33471             return;
33472         }
33473         
33474         this.el.removeClass([this.invalidClass]);
33475         
33476         this.fireEvent('valid', this);
33477     }
33478     
33479 });
33480
33481 Roo.apply(Roo.bootstrap.RadioSet, {
33482     
33483     groups: {},
33484     
33485     register : function(set)
33486     {
33487         this.groups[set.name] = set;
33488     },
33489     
33490     get: function(name) 
33491     {
33492         if (typeof(this.groups[name]) == 'undefined') {
33493             return false;
33494         }
33495         
33496         return this.groups[name] ;
33497     }
33498     
33499 });
33500 /*
33501  * Based on:
33502  * Ext JS Library 1.1.1
33503  * Copyright(c) 2006-2007, Ext JS, LLC.
33504  *
33505  * Originally Released Under LGPL - original licence link has changed is not relivant.
33506  *
33507  * Fork - LGPL
33508  * <script type="text/javascript">
33509  */
33510
33511
33512 /**
33513  * @class Roo.bootstrap.SplitBar
33514  * @extends Roo.util.Observable
33515  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33516  * <br><br>
33517  * Usage:
33518  * <pre><code>
33519 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33520                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33521 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33522 split.minSize = 100;
33523 split.maxSize = 600;
33524 split.animate = true;
33525 split.on('moved', splitterMoved);
33526 </code></pre>
33527  * @constructor
33528  * Create a new SplitBar
33529  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33530  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33531  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33532  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33533                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33534                         position of the SplitBar).
33535  */
33536 Roo.bootstrap.SplitBar = function(cfg){
33537     
33538     /** @private */
33539     
33540     //{
33541     //  dragElement : elm
33542     //  resizingElement: el,
33543         // optional..
33544     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33545     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33546         // existingProxy ???
33547     //}
33548     
33549     this.el = Roo.get(cfg.dragElement, true);
33550     this.el.dom.unselectable = "on";
33551     /** @private */
33552     this.resizingEl = Roo.get(cfg.resizingElement, true);
33553
33554     /**
33555      * @private
33556      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33557      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33558      * @type Number
33559      */
33560     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33561     
33562     /**
33563      * The minimum size of the resizing element. (Defaults to 0)
33564      * @type Number
33565      */
33566     this.minSize = 0;
33567     
33568     /**
33569      * The maximum size of the resizing element. (Defaults to 2000)
33570      * @type Number
33571      */
33572     this.maxSize = 2000;
33573     
33574     /**
33575      * Whether to animate the transition to the new size
33576      * @type Boolean
33577      */
33578     this.animate = false;
33579     
33580     /**
33581      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33582      * @type Boolean
33583      */
33584     this.useShim = false;
33585     
33586     /** @private */
33587     this.shim = null;
33588     
33589     if(!cfg.existingProxy){
33590         /** @private */
33591         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33592     }else{
33593         this.proxy = Roo.get(cfg.existingProxy).dom;
33594     }
33595     /** @private */
33596     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33597     
33598     /** @private */
33599     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33600     
33601     /** @private */
33602     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33603     
33604     /** @private */
33605     this.dragSpecs = {};
33606     
33607     /**
33608      * @private The adapter to use to positon and resize elements
33609      */
33610     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33611     this.adapter.init(this);
33612     
33613     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33614         /** @private */
33615         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33616         this.el.addClass("roo-splitbar-h");
33617     }else{
33618         /** @private */
33619         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33620         this.el.addClass("roo-splitbar-v");
33621     }
33622     
33623     this.addEvents({
33624         /**
33625          * @event resize
33626          * Fires when the splitter is moved (alias for {@link #event-moved})
33627          * @param {Roo.bootstrap.SplitBar} this
33628          * @param {Number} newSize the new width or height
33629          */
33630         "resize" : true,
33631         /**
33632          * @event moved
33633          * Fires when the splitter is moved
33634          * @param {Roo.bootstrap.SplitBar} this
33635          * @param {Number} newSize the new width or height
33636          */
33637         "moved" : true,
33638         /**
33639          * @event beforeresize
33640          * Fires before the splitter is dragged
33641          * @param {Roo.bootstrap.SplitBar} this
33642          */
33643         "beforeresize" : true,
33644
33645         "beforeapply" : true
33646     });
33647
33648     Roo.util.Observable.call(this);
33649 };
33650
33651 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33652     onStartProxyDrag : function(x, y){
33653         this.fireEvent("beforeresize", this);
33654         if(!this.overlay){
33655             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33656             o.unselectable();
33657             o.enableDisplayMode("block");
33658             // all splitbars share the same overlay
33659             Roo.bootstrap.SplitBar.prototype.overlay = o;
33660         }
33661         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33662         this.overlay.show();
33663         Roo.get(this.proxy).setDisplayed("block");
33664         var size = this.adapter.getElementSize(this);
33665         this.activeMinSize = this.getMinimumSize();;
33666         this.activeMaxSize = this.getMaximumSize();;
33667         var c1 = size - this.activeMinSize;
33668         var c2 = Math.max(this.activeMaxSize - size, 0);
33669         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33670             this.dd.resetConstraints();
33671             this.dd.setXConstraint(
33672                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33673                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33674             );
33675             this.dd.setYConstraint(0, 0);
33676         }else{
33677             this.dd.resetConstraints();
33678             this.dd.setXConstraint(0, 0);
33679             this.dd.setYConstraint(
33680                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33681                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33682             );
33683          }
33684         this.dragSpecs.startSize = size;
33685         this.dragSpecs.startPoint = [x, y];
33686         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33687     },
33688     
33689     /** 
33690      * @private Called after the drag operation by the DDProxy
33691      */
33692     onEndProxyDrag : function(e){
33693         Roo.get(this.proxy).setDisplayed(false);
33694         var endPoint = Roo.lib.Event.getXY(e);
33695         if(this.overlay){
33696             this.overlay.hide();
33697         }
33698         var newSize;
33699         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33700             newSize = this.dragSpecs.startSize + 
33701                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33702                     endPoint[0] - this.dragSpecs.startPoint[0] :
33703                     this.dragSpecs.startPoint[0] - endPoint[0]
33704                 );
33705         }else{
33706             newSize = this.dragSpecs.startSize + 
33707                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33708                     endPoint[1] - this.dragSpecs.startPoint[1] :
33709                     this.dragSpecs.startPoint[1] - endPoint[1]
33710                 );
33711         }
33712         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33713         if(newSize != this.dragSpecs.startSize){
33714             if(this.fireEvent('beforeapply', this, newSize) !== false){
33715                 this.adapter.setElementSize(this, newSize);
33716                 this.fireEvent("moved", this, newSize);
33717                 this.fireEvent("resize", this, newSize);
33718             }
33719         }
33720     },
33721     
33722     /**
33723      * Get the adapter this SplitBar uses
33724      * @return The adapter object
33725      */
33726     getAdapter : function(){
33727         return this.adapter;
33728     },
33729     
33730     /**
33731      * Set the adapter this SplitBar uses
33732      * @param {Object} adapter A SplitBar adapter object
33733      */
33734     setAdapter : function(adapter){
33735         this.adapter = adapter;
33736         this.adapter.init(this);
33737     },
33738     
33739     /**
33740      * Gets the minimum size for the resizing element
33741      * @return {Number} The minimum size
33742      */
33743     getMinimumSize : function(){
33744         return this.minSize;
33745     },
33746     
33747     /**
33748      * Sets the minimum size for the resizing element
33749      * @param {Number} minSize The minimum size
33750      */
33751     setMinimumSize : function(minSize){
33752         this.minSize = minSize;
33753     },
33754     
33755     /**
33756      * Gets the maximum size for the resizing element
33757      * @return {Number} The maximum size
33758      */
33759     getMaximumSize : function(){
33760         return this.maxSize;
33761     },
33762     
33763     /**
33764      * Sets the maximum size for the resizing element
33765      * @param {Number} maxSize The maximum size
33766      */
33767     setMaximumSize : function(maxSize){
33768         this.maxSize = maxSize;
33769     },
33770     
33771     /**
33772      * Sets the initialize size for the resizing element
33773      * @param {Number} size The initial size
33774      */
33775     setCurrentSize : function(size){
33776         var oldAnimate = this.animate;
33777         this.animate = false;
33778         this.adapter.setElementSize(this, size);
33779         this.animate = oldAnimate;
33780     },
33781     
33782     /**
33783      * Destroy this splitbar. 
33784      * @param {Boolean} removeEl True to remove the element
33785      */
33786     destroy : function(removeEl){
33787         if(this.shim){
33788             this.shim.remove();
33789         }
33790         this.dd.unreg();
33791         this.proxy.parentNode.removeChild(this.proxy);
33792         if(removeEl){
33793             this.el.remove();
33794         }
33795     }
33796 });
33797
33798 /**
33799  * @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.
33800  */
33801 Roo.bootstrap.SplitBar.createProxy = function(dir){
33802     var proxy = new Roo.Element(document.createElement("div"));
33803     proxy.unselectable();
33804     var cls = 'roo-splitbar-proxy';
33805     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33806     document.body.appendChild(proxy.dom);
33807     return proxy.dom;
33808 };
33809
33810 /** 
33811  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33812  * Default Adapter. It assumes the splitter and resizing element are not positioned
33813  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33814  */
33815 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33816 };
33817
33818 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33819     // do nothing for now
33820     init : function(s){
33821     
33822     },
33823     /**
33824      * Called before drag operations to get the current size of the resizing element. 
33825      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33826      */
33827      getElementSize : function(s){
33828         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33829             return s.resizingEl.getWidth();
33830         }else{
33831             return s.resizingEl.getHeight();
33832         }
33833     },
33834     
33835     /**
33836      * Called after drag operations to set the size of the resizing element.
33837      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33838      * @param {Number} newSize The new size to set
33839      * @param {Function} onComplete A function to be invoked when resizing is complete
33840      */
33841     setElementSize : function(s, newSize, onComplete){
33842         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33843             if(!s.animate){
33844                 s.resizingEl.setWidth(newSize);
33845                 if(onComplete){
33846                     onComplete(s, newSize);
33847                 }
33848             }else{
33849                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33850             }
33851         }else{
33852             
33853             if(!s.animate){
33854                 s.resizingEl.setHeight(newSize);
33855                 if(onComplete){
33856                     onComplete(s, newSize);
33857                 }
33858             }else{
33859                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33860             }
33861         }
33862     }
33863 };
33864
33865 /** 
33866  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33867  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33868  * Adapter that  moves the splitter element to align with the resized sizing element. 
33869  * Used with an absolute positioned SplitBar.
33870  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33871  * document.body, make sure you assign an id to the body element.
33872  */
33873 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33874     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33875     this.container = Roo.get(container);
33876 };
33877
33878 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33879     init : function(s){
33880         this.basic.init(s);
33881     },
33882     
33883     getElementSize : function(s){
33884         return this.basic.getElementSize(s);
33885     },
33886     
33887     setElementSize : function(s, newSize, onComplete){
33888         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33889     },
33890     
33891     moveSplitter : function(s){
33892         var yes = Roo.bootstrap.SplitBar;
33893         switch(s.placement){
33894             case yes.LEFT:
33895                 s.el.setX(s.resizingEl.getRight());
33896                 break;
33897             case yes.RIGHT:
33898                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33899                 break;
33900             case yes.TOP:
33901                 s.el.setY(s.resizingEl.getBottom());
33902                 break;
33903             case yes.BOTTOM:
33904                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33905                 break;
33906         }
33907     }
33908 };
33909
33910 /**
33911  * Orientation constant - Create a vertical SplitBar
33912  * @static
33913  * @type Number
33914  */
33915 Roo.bootstrap.SplitBar.VERTICAL = 1;
33916
33917 /**
33918  * Orientation constant - Create a horizontal SplitBar
33919  * @static
33920  * @type Number
33921  */
33922 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33923
33924 /**
33925  * Placement constant - The resizing element is to the left of the splitter element
33926  * @static
33927  * @type Number
33928  */
33929 Roo.bootstrap.SplitBar.LEFT = 1;
33930
33931 /**
33932  * Placement constant - The resizing element is to the right of the splitter element
33933  * @static
33934  * @type Number
33935  */
33936 Roo.bootstrap.SplitBar.RIGHT = 2;
33937
33938 /**
33939  * Placement constant - The resizing element is positioned above the splitter element
33940  * @static
33941  * @type Number
33942  */
33943 Roo.bootstrap.SplitBar.TOP = 3;
33944
33945 /**
33946  * Placement constant - The resizing element is positioned under splitter element
33947  * @static
33948  * @type Number
33949  */
33950 Roo.bootstrap.SplitBar.BOTTOM = 4;
33951 Roo.namespace("Roo.bootstrap.layout");/*
33952  * Based on:
33953  * Ext JS Library 1.1.1
33954  * Copyright(c) 2006-2007, Ext JS, LLC.
33955  *
33956  * Originally Released Under LGPL - original licence link has changed is not relivant.
33957  *
33958  * Fork - LGPL
33959  * <script type="text/javascript">
33960  */
33961
33962 /**
33963  * @class Roo.bootstrap.layout.Manager
33964  * @extends Roo.bootstrap.Component
33965  * Base class for layout managers.
33966  */
33967 Roo.bootstrap.layout.Manager = function(config)
33968 {
33969     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33970
33971
33972
33973
33974
33975     /** false to disable window resize monitoring @type Boolean */
33976     this.monitorWindowResize = true;
33977     this.regions = {};
33978     this.addEvents({
33979         /**
33980          * @event layout
33981          * Fires when a layout is performed.
33982          * @param {Roo.LayoutManager} this
33983          */
33984         "layout" : true,
33985         /**
33986          * @event regionresized
33987          * Fires when the user resizes a region.
33988          * @param {Roo.LayoutRegion} region The resized region
33989          * @param {Number} newSize The new size (width for east/west, height for north/south)
33990          */
33991         "regionresized" : true,
33992         /**
33993          * @event regioncollapsed
33994          * Fires when a region is collapsed.
33995          * @param {Roo.LayoutRegion} region The collapsed region
33996          */
33997         "regioncollapsed" : true,
33998         /**
33999          * @event regionexpanded
34000          * Fires when a region is expanded.
34001          * @param {Roo.LayoutRegion} region The expanded region
34002          */
34003         "regionexpanded" : true
34004     });
34005     this.updating = false;
34006
34007     if (config.el) {
34008         this.el = Roo.get(config.el);
34009         this.initEvents();
34010     }
34011
34012 };
34013
34014 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34015
34016
34017     regions : null,
34018
34019     monitorWindowResize : true,
34020
34021
34022     updating : false,
34023
34024
34025     onRender : function(ct, position)
34026     {
34027         if(!this.el){
34028             this.el = Roo.get(ct);
34029             this.initEvents();
34030         }
34031         //this.fireEvent('render',this);
34032     },
34033
34034
34035     initEvents: function()
34036     {
34037
34038
34039         // ie scrollbar fix
34040         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34041             document.body.scroll = "no";
34042         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34043             this.el.position('relative');
34044         }
34045         this.id = this.el.id;
34046         this.el.addClass("roo-layout-container");
34047         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34048         if(this.el.dom != document.body ) {
34049             this.el.on('resize', this.layout,this);
34050             this.el.on('show', this.layout,this);
34051         }
34052
34053     },
34054
34055     /**
34056      * Returns true if this layout is currently being updated
34057      * @return {Boolean}
34058      */
34059     isUpdating : function(){
34060         return this.updating;
34061     },
34062
34063     /**
34064      * Suspend the LayoutManager from doing auto-layouts while
34065      * making multiple add or remove calls
34066      */
34067     beginUpdate : function(){
34068         this.updating = true;
34069     },
34070
34071     /**
34072      * Restore auto-layouts and optionally disable the manager from performing a layout
34073      * @param {Boolean} noLayout true to disable a layout update
34074      */
34075     endUpdate : function(noLayout){
34076         this.updating = false;
34077         if(!noLayout){
34078             this.layout();
34079         }
34080     },
34081
34082     layout: function(){
34083         // abstract...
34084     },
34085
34086     onRegionResized : function(region, newSize){
34087         this.fireEvent("regionresized", region, newSize);
34088         this.layout();
34089     },
34090
34091     onRegionCollapsed : function(region){
34092         this.fireEvent("regioncollapsed", region);
34093     },
34094
34095     onRegionExpanded : function(region){
34096         this.fireEvent("regionexpanded", region);
34097     },
34098
34099     /**
34100      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34101      * performs box-model adjustments.
34102      * @return {Object} The size as an object {width: (the width), height: (the height)}
34103      */
34104     getViewSize : function()
34105     {
34106         var size;
34107         if(this.el.dom != document.body){
34108             size = this.el.getSize();
34109         }else{
34110             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34111         }
34112         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34113         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34114         return size;
34115     },
34116
34117     /**
34118      * Returns the Element this layout is bound to.
34119      * @return {Roo.Element}
34120      */
34121     getEl : function(){
34122         return this.el;
34123     },
34124
34125     /**
34126      * Returns the specified region.
34127      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34128      * @return {Roo.LayoutRegion}
34129      */
34130     getRegion : function(target){
34131         return this.regions[target.toLowerCase()];
34132     },
34133
34134     onWindowResize : function(){
34135         if(this.monitorWindowResize){
34136             this.layout();
34137         }
34138     }
34139 });
34140 /*
34141  * Based on:
34142  * Ext JS Library 1.1.1
34143  * Copyright(c) 2006-2007, Ext JS, LLC.
34144  *
34145  * Originally Released Under LGPL - original licence link has changed is not relivant.
34146  *
34147  * Fork - LGPL
34148  * <script type="text/javascript">
34149  */
34150 /**
34151  * @class Roo.bootstrap.layout.Border
34152  * @extends Roo.bootstrap.layout.Manager
34153  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34154  * please see: examples/bootstrap/nested.html<br><br>
34155  
34156 <b>The container the layout is rendered into can be either the body element or any other element.
34157 If it is not the body element, the container needs to either be an absolute positioned element,
34158 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34159 the container size if it is not the body element.</b>
34160
34161 * @constructor
34162 * Create a new Border
34163 * @param {Object} config Configuration options
34164  */
34165 Roo.bootstrap.layout.Border = function(config){
34166     config = config || {};
34167     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34168     
34169     
34170     
34171     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34172         if(config[region]){
34173             config[region].region = region;
34174             this.addRegion(config[region]);
34175         }
34176     },this);
34177     
34178 };
34179
34180 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34181
34182 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34183     /**
34184      * Creates and adds a new region if it doesn't already exist.
34185      * @param {String} target The target region key (north, south, east, west or center).
34186      * @param {Object} config The regions config object
34187      * @return {BorderLayoutRegion} The new region
34188      */
34189     addRegion : function(config)
34190     {
34191         if(!this.regions[config.region]){
34192             var r = this.factory(config);
34193             this.bindRegion(r);
34194         }
34195         return this.regions[config.region];
34196     },
34197
34198     // private (kinda)
34199     bindRegion : function(r){
34200         this.regions[r.config.region] = r;
34201         
34202         r.on("visibilitychange",    this.layout, this);
34203         r.on("paneladded",          this.layout, this);
34204         r.on("panelremoved",        this.layout, this);
34205         r.on("invalidated",         this.layout, this);
34206         r.on("resized",             this.onRegionResized, this);
34207         r.on("collapsed",           this.onRegionCollapsed, this);
34208         r.on("expanded",            this.onRegionExpanded, this);
34209     },
34210
34211     /**
34212      * Performs a layout update.
34213      */
34214     layout : function()
34215     {
34216         if(this.updating) {
34217             return;
34218         }
34219         
34220         // render all the rebions if they have not been done alreayd?
34221         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34222             if(this.regions[region] && !this.regions[region].bodyEl){
34223                 this.regions[region].onRender(this.el)
34224             }
34225         },this);
34226         
34227         var size = this.getViewSize();
34228         var w = size.width;
34229         var h = size.height;
34230         var centerW = w;
34231         var centerH = h;
34232         var centerY = 0;
34233         var centerX = 0;
34234         //var x = 0, y = 0;
34235
34236         var rs = this.regions;
34237         var north = rs["north"];
34238         var south = rs["south"]; 
34239         var west = rs["west"];
34240         var east = rs["east"];
34241         var center = rs["center"];
34242         //if(this.hideOnLayout){ // not supported anymore
34243             //c.el.setStyle("display", "none");
34244         //}
34245         if(north && north.isVisible()){
34246             var b = north.getBox();
34247             var m = north.getMargins();
34248             b.width = w - (m.left+m.right);
34249             b.x = m.left;
34250             b.y = m.top;
34251             centerY = b.height + b.y + m.bottom;
34252             centerH -= centerY;
34253             north.updateBox(this.safeBox(b));
34254         }
34255         if(south && south.isVisible()){
34256             var b = south.getBox();
34257             var m = south.getMargins();
34258             b.width = w - (m.left+m.right);
34259             b.x = m.left;
34260             var totalHeight = (b.height + m.top + m.bottom);
34261             b.y = h - totalHeight + m.top;
34262             centerH -= totalHeight;
34263             south.updateBox(this.safeBox(b));
34264         }
34265         if(west && west.isVisible()){
34266             var b = west.getBox();
34267             var m = west.getMargins();
34268             b.height = centerH - (m.top+m.bottom);
34269             b.x = m.left;
34270             b.y = centerY + m.top;
34271             var totalWidth = (b.width + m.left + m.right);
34272             centerX += totalWidth;
34273             centerW -= totalWidth;
34274             west.updateBox(this.safeBox(b));
34275         }
34276         if(east && east.isVisible()){
34277             var b = east.getBox();
34278             var m = east.getMargins();
34279             b.height = centerH - (m.top+m.bottom);
34280             var totalWidth = (b.width + m.left + m.right);
34281             b.x = w - totalWidth + m.left;
34282             b.y = centerY + m.top;
34283             centerW -= totalWidth;
34284             east.updateBox(this.safeBox(b));
34285         }
34286         if(center){
34287             var m = center.getMargins();
34288             var centerBox = {
34289                 x: centerX + m.left,
34290                 y: centerY + m.top,
34291                 width: centerW - (m.left+m.right),
34292                 height: centerH - (m.top+m.bottom)
34293             };
34294             //if(this.hideOnLayout){
34295                 //center.el.setStyle("display", "block");
34296             //}
34297             center.updateBox(this.safeBox(centerBox));
34298         }
34299         this.el.repaint();
34300         this.fireEvent("layout", this);
34301     },
34302
34303     // private
34304     safeBox : function(box){
34305         box.width = Math.max(0, box.width);
34306         box.height = Math.max(0, box.height);
34307         return box;
34308     },
34309
34310     /**
34311      * Adds a ContentPanel (or subclass) to this layout.
34312      * @param {String} target The target region key (north, south, east, west or center).
34313      * @param {Roo.ContentPanel} panel The panel to add
34314      * @return {Roo.ContentPanel} The added panel
34315      */
34316     add : function(target, panel){
34317          
34318         target = target.toLowerCase();
34319         return this.regions[target].add(panel);
34320     },
34321
34322     /**
34323      * Remove a ContentPanel (or subclass) to this layout.
34324      * @param {String} target The target region key (north, south, east, west or center).
34325      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34326      * @return {Roo.ContentPanel} The removed panel
34327      */
34328     remove : function(target, panel){
34329         target = target.toLowerCase();
34330         return this.regions[target].remove(panel);
34331     },
34332
34333     /**
34334      * Searches all regions for a panel with the specified id
34335      * @param {String} panelId
34336      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34337      */
34338     findPanel : function(panelId){
34339         var rs = this.regions;
34340         for(var target in rs){
34341             if(typeof rs[target] != "function"){
34342                 var p = rs[target].getPanel(panelId);
34343                 if(p){
34344                     return p;
34345                 }
34346             }
34347         }
34348         return null;
34349     },
34350
34351     /**
34352      * Searches all regions for a panel with the specified id and activates (shows) it.
34353      * @param {String/ContentPanel} panelId The panels id or the panel itself
34354      * @return {Roo.ContentPanel} The shown panel or null
34355      */
34356     showPanel : function(panelId) {
34357       var rs = this.regions;
34358       for(var target in rs){
34359          var r = rs[target];
34360          if(typeof r != "function"){
34361             if(r.hasPanel(panelId)){
34362                return r.showPanel(panelId);
34363             }
34364          }
34365       }
34366       return null;
34367    },
34368
34369    /**
34370      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34371      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34372      */
34373    /*
34374     restoreState : function(provider){
34375         if(!provider){
34376             provider = Roo.state.Manager;
34377         }
34378         var sm = new Roo.LayoutStateManager();
34379         sm.init(this, provider);
34380     },
34381 */
34382  
34383  
34384     /**
34385      * Adds a xtype elements to the layout.
34386      * <pre><code>
34387
34388 layout.addxtype({
34389        xtype : 'ContentPanel',
34390        region: 'west',
34391        items: [ .... ]
34392    }
34393 );
34394
34395 layout.addxtype({
34396         xtype : 'NestedLayoutPanel',
34397         region: 'west',
34398         layout: {
34399            center: { },
34400            west: { }   
34401         },
34402         items : [ ... list of content panels or nested layout panels.. ]
34403    }
34404 );
34405 </code></pre>
34406      * @param {Object} cfg Xtype definition of item to add.
34407      */
34408     addxtype : function(cfg)
34409     {
34410         // basically accepts a pannel...
34411         // can accept a layout region..!?!?
34412         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34413         
34414         
34415         // theory?  children can only be panels??
34416         
34417         //if (!cfg.xtype.match(/Panel$/)) {
34418         //    return false;
34419         //}
34420         var ret = false;
34421         
34422         if (typeof(cfg.region) == 'undefined') {
34423             Roo.log("Failed to add Panel, region was not set");
34424             Roo.log(cfg);
34425             return false;
34426         }
34427         var region = cfg.region;
34428         delete cfg.region;
34429         
34430           
34431         var xitems = [];
34432         if (cfg.items) {
34433             xitems = cfg.items;
34434             delete cfg.items;
34435         }
34436         var nb = false;
34437         
34438         switch(cfg.xtype) 
34439         {
34440             case 'Content':  // ContentPanel (el, cfg)
34441             case 'Scroll':  // ContentPanel (el, cfg)
34442             case 'View': 
34443                 cfg.autoCreate = true;
34444                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34445                 //} else {
34446                 //    var el = this.el.createChild();
34447                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34448                 //}
34449                 
34450                 this.add(region, ret);
34451                 break;
34452             
34453             /*
34454             case 'TreePanel': // our new panel!
34455                 cfg.el = this.el.createChild();
34456                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34457                 this.add(region, ret);
34458                 break;
34459             */
34460             
34461             case 'Nest': 
34462                 // create a new Layout (which is  a Border Layout...
34463                 
34464                 var clayout = cfg.layout;
34465                 clayout.el  = this.el.createChild();
34466                 clayout.items   = clayout.items  || [];
34467                 
34468                 delete cfg.layout;
34469                 
34470                 // replace this exitems with the clayout ones..
34471                 xitems = clayout.items;
34472                  
34473                 // force background off if it's in center...
34474                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34475                     cfg.background = false;
34476                 }
34477                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34478                 
34479                 
34480                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34481                 //console.log('adding nested layout panel '  + cfg.toSource());
34482                 this.add(region, ret);
34483                 nb = {}; /// find first...
34484                 break;
34485             
34486             case 'Grid':
34487                 
34488                 // needs grid and region
34489                 
34490                 //var el = this.getRegion(region).el.createChild();
34491                 /*
34492                  *var el = this.el.createChild();
34493                 // create the grid first...
34494                 cfg.grid.container = el;
34495                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34496                 */
34497                 
34498                 if (region == 'center' && this.active ) {
34499                     cfg.background = false;
34500                 }
34501                 
34502                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34503                 
34504                 this.add(region, ret);
34505                 /*
34506                 if (cfg.background) {
34507                     // render grid on panel activation (if panel background)
34508                     ret.on('activate', function(gp) {
34509                         if (!gp.grid.rendered) {
34510                     //        gp.grid.render(el);
34511                         }
34512                     });
34513                 } else {
34514                   //  cfg.grid.render(el);
34515                 }
34516                 */
34517                 break;
34518            
34519            
34520             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34521                 // it was the old xcomponent building that caused this before.
34522                 // espeically if border is the top element in the tree.
34523                 ret = this;
34524                 break; 
34525                 
34526                     
34527                 
34528                 
34529                 
34530             default:
34531                 /*
34532                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34533                     
34534                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34535                     this.add(region, ret);
34536                 } else {
34537                 */
34538                     Roo.log(cfg);
34539                     throw "Can not add '" + cfg.xtype + "' to Border";
34540                     return null;
34541              
34542                                 
34543              
34544         }
34545         this.beginUpdate();
34546         // add children..
34547         var region = '';
34548         var abn = {};
34549         Roo.each(xitems, function(i)  {
34550             region = nb && i.region ? i.region : false;
34551             
34552             var add = ret.addxtype(i);
34553            
34554             if (region) {
34555                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34556                 if (!i.background) {
34557                     abn[region] = nb[region] ;
34558                 }
34559             }
34560             
34561         });
34562         this.endUpdate();
34563
34564         // make the last non-background panel active..
34565         //if (nb) { Roo.log(abn); }
34566         if (nb) {
34567             
34568             for(var r in abn) {
34569                 region = this.getRegion(r);
34570                 if (region) {
34571                     // tried using nb[r], but it does not work..
34572                      
34573                     region.showPanel(abn[r]);
34574                    
34575                 }
34576             }
34577         }
34578         return ret;
34579         
34580     },
34581     
34582     
34583 // private
34584     factory : function(cfg)
34585     {
34586         
34587         var validRegions = Roo.bootstrap.layout.Border.regions;
34588
34589         var target = cfg.region;
34590         cfg.mgr = this;
34591         
34592         var r = Roo.bootstrap.layout;
34593         Roo.log(target);
34594         switch(target){
34595             case "north":
34596                 return new r.North(cfg);
34597             case "south":
34598                 return new r.South(cfg);
34599             case "east":
34600                 return new r.East(cfg);
34601             case "west":
34602                 return new r.West(cfg);
34603             case "center":
34604                 return new r.Center(cfg);
34605         }
34606         throw 'Layout region "'+target+'" not supported.';
34607     }
34608     
34609     
34610 });
34611  /*
34612  * Based on:
34613  * Ext JS Library 1.1.1
34614  * Copyright(c) 2006-2007, Ext JS, LLC.
34615  *
34616  * Originally Released Under LGPL - original licence link has changed is not relivant.
34617  *
34618  * Fork - LGPL
34619  * <script type="text/javascript">
34620  */
34621  
34622 /**
34623  * @class Roo.bootstrap.layout.Basic
34624  * @extends Roo.util.Observable
34625  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34626  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34627  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34628  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34629  * @cfg {string}   region  the region that it inhabits..
34630  * @cfg {bool}   skipConfig skip config?
34631  * 
34632
34633  */
34634 Roo.bootstrap.layout.Basic = function(config){
34635     
34636     this.mgr = config.mgr;
34637     
34638     this.position = config.region;
34639     
34640     var skipConfig = config.skipConfig;
34641     
34642     this.events = {
34643         /**
34644          * @scope Roo.BasicLayoutRegion
34645          */
34646         
34647         /**
34648          * @event beforeremove
34649          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34650          * @param {Roo.LayoutRegion} this
34651          * @param {Roo.ContentPanel} panel The panel
34652          * @param {Object} e The cancel event object
34653          */
34654         "beforeremove" : true,
34655         /**
34656          * @event invalidated
34657          * Fires when the layout for this region is changed.
34658          * @param {Roo.LayoutRegion} this
34659          */
34660         "invalidated" : true,
34661         /**
34662          * @event visibilitychange
34663          * Fires when this region is shown or hidden 
34664          * @param {Roo.LayoutRegion} this
34665          * @param {Boolean} visibility true or false
34666          */
34667         "visibilitychange" : true,
34668         /**
34669          * @event paneladded
34670          * Fires when a panel is added. 
34671          * @param {Roo.LayoutRegion} this
34672          * @param {Roo.ContentPanel} panel The panel
34673          */
34674         "paneladded" : true,
34675         /**
34676          * @event panelremoved
34677          * Fires when a panel is removed. 
34678          * @param {Roo.LayoutRegion} this
34679          * @param {Roo.ContentPanel} panel The panel
34680          */
34681         "panelremoved" : true,
34682         /**
34683          * @event beforecollapse
34684          * Fires when this region before collapse.
34685          * @param {Roo.LayoutRegion} this
34686          */
34687         "beforecollapse" : true,
34688         /**
34689          * @event collapsed
34690          * Fires when this region is collapsed.
34691          * @param {Roo.LayoutRegion} this
34692          */
34693         "collapsed" : true,
34694         /**
34695          * @event expanded
34696          * Fires when this region is expanded.
34697          * @param {Roo.LayoutRegion} this
34698          */
34699         "expanded" : true,
34700         /**
34701          * @event slideshow
34702          * Fires when this region is slid into view.
34703          * @param {Roo.LayoutRegion} this
34704          */
34705         "slideshow" : true,
34706         /**
34707          * @event slidehide
34708          * Fires when this region slides out of view. 
34709          * @param {Roo.LayoutRegion} this
34710          */
34711         "slidehide" : true,
34712         /**
34713          * @event panelactivated
34714          * Fires when a panel is activated. 
34715          * @param {Roo.LayoutRegion} this
34716          * @param {Roo.ContentPanel} panel The activated panel
34717          */
34718         "panelactivated" : true,
34719         /**
34720          * @event resized
34721          * Fires when the user resizes this region. 
34722          * @param {Roo.LayoutRegion} this
34723          * @param {Number} newSize The new size (width for east/west, height for north/south)
34724          */
34725         "resized" : true
34726     };
34727     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34728     this.panels = new Roo.util.MixedCollection();
34729     this.panels.getKey = this.getPanelId.createDelegate(this);
34730     this.box = null;
34731     this.activePanel = null;
34732     // ensure listeners are added...
34733     
34734     if (config.listeners || config.events) {
34735         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34736             listeners : config.listeners || {},
34737             events : config.events || {}
34738         });
34739     }
34740     
34741     if(skipConfig !== true){
34742         this.applyConfig(config);
34743     }
34744 };
34745
34746 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34747 {
34748     getPanelId : function(p){
34749         return p.getId();
34750     },
34751     
34752     applyConfig : function(config){
34753         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34754         this.config = config;
34755         
34756     },
34757     
34758     /**
34759      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34760      * the width, for horizontal (north, south) the height.
34761      * @param {Number} newSize The new width or height
34762      */
34763     resizeTo : function(newSize){
34764         var el = this.el ? this.el :
34765                  (this.activePanel ? this.activePanel.getEl() : null);
34766         if(el){
34767             switch(this.position){
34768                 case "east":
34769                 case "west":
34770                     el.setWidth(newSize);
34771                     this.fireEvent("resized", this, newSize);
34772                 break;
34773                 case "north":
34774                 case "south":
34775                     el.setHeight(newSize);
34776                     this.fireEvent("resized", this, newSize);
34777                 break;                
34778             }
34779         }
34780     },
34781     
34782     getBox : function(){
34783         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34784     },
34785     
34786     getMargins : function(){
34787         return this.margins;
34788     },
34789     
34790     updateBox : function(box){
34791         this.box = box;
34792         var el = this.activePanel.getEl();
34793         el.dom.style.left = box.x + "px";
34794         el.dom.style.top = box.y + "px";
34795         this.activePanel.setSize(box.width, box.height);
34796     },
34797     
34798     /**
34799      * Returns the container element for this region.
34800      * @return {Roo.Element}
34801      */
34802     getEl : function(){
34803         return this.activePanel;
34804     },
34805     
34806     /**
34807      * Returns true if this region is currently visible.
34808      * @return {Boolean}
34809      */
34810     isVisible : function(){
34811         return this.activePanel ? true : false;
34812     },
34813     
34814     setActivePanel : function(panel){
34815         panel = this.getPanel(panel);
34816         if(this.activePanel && this.activePanel != panel){
34817             this.activePanel.setActiveState(false);
34818             this.activePanel.getEl().setLeftTop(-10000,-10000);
34819         }
34820         this.activePanel = panel;
34821         panel.setActiveState(true);
34822         if(this.box){
34823             panel.setSize(this.box.width, this.box.height);
34824         }
34825         this.fireEvent("panelactivated", this, panel);
34826         this.fireEvent("invalidated");
34827     },
34828     
34829     /**
34830      * Show the specified panel.
34831      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34832      * @return {Roo.ContentPanel} The shown panel or null
34833      */
34834     showPanel : function(panel){
34835         panel = this.getPanel(panel);
34836         if(panel){
34837             this.setActivePanel(panel);
34838         }
34839         return panel;
34840     },
34841     
34842     /**
34843      * Get the active panel for this region.
34844      * @return {Roo.ContentPanel} The active panel or null
34845      */
34846     getActivePanel : function(){
34847         return this.activePanel;
34848     },
34849     
34850     /**
34851      * Add the passed ContentPanel(s)
34852      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34853      * @return {Roo.ContentPanel} The panel added (if only one was added)
34854      */
34855     add : function(panel){
34856         if(arguments.length > 1){
34857             for(var i = 0, len = arguments.length; i < len; i++) {
34858                 this.add(arguments[i]);
34859             }
34860             return null;
34861         }
34862         if(this.hasPanel(panel)){
34863             this.showPanel(panel);
34864             return panel;
34865         }
34866         var el = panel.getEl();
34867         if(el.dom.parentNode != this.mgr.el.dom){
34868             this.mgr.el.dom.appendChild(el.dom);
34869         }
34870         if(panel.setRegion){
34871             panel.setRegion(this);
34872         }
34873         this.panels.add(panel);
34874         el.setStyle("position", "absolute");
34875         if(!panel.background){
34876             this.setActivePanel(panel);
34877             if(this.config.initialSize && this.panels.getCount()==1){
34878                 this.resizeTo(this.config.initialSize);
34879             }
34880         }
34881         this.fireEvent("paneladded", this, panel);
34882         return panel;
34883     },
34884     
34885     /**
34886      * Returns true if the panel is in this region.
34887      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34888      * @return {Boolean}
34889      */
34890     hasPanel : function(panel){
34891         if(typeof panel == "object"){ // must be panel obj
34892             panel = panel.getId();
34893         }
34894         return this.getPanel(panel) ? true : false;
34895     },
34896     
34897     /**
34898      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34899      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34900      * @param {Boolean} preservePanel Overrides the config preservePanel option
34901      * @return {Roo.ContentPanel} The panel that was removed
34902      */
34903     remove : function(panel, preservePanel){
34904         panel = this.getPanel(panel);
34905         if(!panel){
34906             return null;
34907         }
34908         var e = {};
34909         this.fireEvent("beforeremove", this, panel, e);
34910         if(e.cancel === true){
34911             return null;
34912         }
34913         var panelId = panel.getId();
34914         this.panels.removeKey(panelId);
34915         return panel;
34916     },
34917     
34918     /**
34919      * Returns the panel specified or null if it's not in this region.
34920      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34921      * @return {Roo.ContentPanel}
34922      */
34923     getPanel : function(id){
34924         if(typeof id == "object"){ // must be panel obj
34925             return id;
34926         }
34927         return this.panels.get(id);
34928     },
34929     
34930     /**
34931      * Returns this regions position (north/south/east/west/center).
34932      * @return {String} 
34933      */
34934     getPosition: function(){
34935         return this.position;    
34936     }
34937 });/*
34938  * Based on:
34939  * Ext JS Library 1.1.1
34940  * Copyright(c) 2006-2007, Ext JS, LLC.
34941  *
34942  * Originally Released Under LGPL - original licence link has changed is not relivant.
34943  *
34944  * Fork - LGPL
34945  * <script type="text/javascript">
34946  */
34947  
34948 /**
34949  * @class Roo.bootstrap.layout.Region
34950  * @extends Roo.bootstrap.layout.Basic
34951  * This class represents a region in a layout manager.
34952  
34953  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34954  * @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})
34955  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34956  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34957  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34958  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34959  * @cfg {String}    title           The title for the region (overrides panel titles)
34960  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34961  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34962  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34963  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34964  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34965  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34966  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34967  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34968  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34969  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34970
34971  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34972  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34973  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34974  * @cfg {Number}    width           For East/West panels
34975  * @cfg {Number}    height          For North/South panels
34976  * @cfg {Boolean}   split           To show the splitter
34977  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34978  * 
34979  * @cfg {string}   cls             Extra CSS classes to add to region
34980  * 
34981  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34982  * @cfg {string}   region  the region that it inhabits..
34983  *
34984
34985  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34986  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34987
34988  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34989  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34990  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34991  */
34992 Roo.bootstrap.layout.Region = function(config)
34993 {
34994     this.applyConfig(config);
34995
34996     var mgr = config.mgr;
34997     var pos = config.region;
34998     config.skipConfig = true;
34999     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35000     
35001     if (mgr.el) {
35002         this.onRender(mgr.el);   
35003     }
35004      
35005     this.visible = true;
35006     this.collapsed = false;
35007     this.unrendered_panels = [];
35008 };
35009
35010 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35011
35012     position: '', // set by wrapper (eg. north/south etc..)
35013     unrendered_panels : null,  // unrendered panels.
35014     createBody : function(){
35015         /** This region's body element 
35016         * @type Roo.Element */
35017         this.bodyEl = this.el.createChild({
35018                 tag: "div",
35019                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35020         });
35021     },
35022
35023     onRender: function(ctr, pos)
35024     {
35025         var dh = Roo.DomHelper;
35026         /** This region's container element 
35027         * @type Roo.Element */
35028         this.el = dh.append(ctr.dom, {
35029                 tag: "div",
35030                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35031             }, true);
35032         /** This region's title element 
35033         * @type Roo.Element */
35034     
35035         this.titleEl = dh.append(this.el.dom,
35036             {
35037                     tag: "div",
35038                     unselectable: "on",
35039                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35040                     children:[
35041                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35042                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35043                     ]}, true);
35044         
35045         this.titleEl.enableDisplayMode();
35046         /** This region's title text element 
35047         * @type HTMLElement */
35048         this.titleTextEl = this.titleEl.dom.firstChild;
35049         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35050         /*
35051         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35052         this.closeBtn.enableDisplayMode();
35053         this.closeBtn.on("click", this.closeClicked, this);
35054         this.closeBtn.hide();
35055     */
35056         this.createBody(this.config);
35057         if(this.config.hideWhenEmpty){
35058             this.hide();
35059             this.on("paneladded", this.validateVisibility, this);
35060             this.on("panelremoved", this.validateVisibility, this);
35061         }
35062         if(this.autoScroll){
35063             this.bodyEl.setStyle("overflow", "auto");
35064         }else{
35065             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35066         }
35067         //if(c.titlebar !== false){
35068             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35069                 this.titleEl.hide();
35070             }else{
35071                 this.titleEl.show();
35072                 if(this.config.title){
35073                     this.titleTextEl.innerHTML = this.config.title;
35074                 }
35075             }
35076         //}
35077         if(this.config.collapsed){
35078             this.collapse(true);
35079         }
35080         if(this.config.hidden){
35081             this.hide();
35082         }
35083         
35084         if (this.unrendered_panels && this.unrendered_panels.length) {
35085             for (var i =0;i< this.unrendered_panels.length; i++) {
35086                 this.add(this.unrendered_panels[i]);
35087             }
35088             this.unrendered_panels = null;
35089             
35090         }
35091         
35092     },
35093     
35094     applyConfig : function(c)
35095     {
35096         /*
35097          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35098             var dh = Roo.DomHelper;
35099             if(c.titlebar !== false){
35100                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35101                 this.collapseBtn.on("click", this.collapse, this);
35102                 this.collapseBtn.enableDisplayMode();
35103                 /*
35104                 if(c.showPin === true || this.showPin){
35105                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35106                     this.stickBtn.enableDisplayMode();
35107                     this.stickBtn.on("click", this.expand, this);
35108                     this.stickBtn.hide();
35109                 }
35110                 
35111             }
35112             */
35113             /** This region's collapsed element
35114             * @type Roo.Element */
35115             /*
35116              *
35117             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35118                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35119             ]}, true);
35120             
35121             if(c.floatable !== false){
35122                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35123                this.collapsedEl.on("click", this.collapseClick, this);
35124             }
35125
35126             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35127                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35128                    id: "message", unselectable: "on", style:{"float":"left"}});
35129                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35130              }
35131             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35132             this.expandBtn.on("click", this.expand, this);
35133             
35134         }
35135         
35136         if(this.collapseBtn){
35137             this.collapseBtn.setVisible(c.collapsible == true);
35138         }
35139         
35140         this.cmargins = c.cmargins || this.cmargins ||
35141                          (this.position == "west" || this.position == "east" ?
35142                              {top: 0, left: 2, right:2, bottom: 0} :
35143                              {top: 2, left: 0, right:0, bottom: 2});
35144         */
35145         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35146         
35147         
35148         this.bottomTabs = c.tabPosition != "top";
35149         
35150         this.autoScroll = c.autoScroll || false;
35151         
35152         
35153        
35154         
35155         this.duration = c.duration || .30;
35156         this.slideDuration = c.slideDuration || .45;
35157         this.config = c;
35158        
35159     },
35160     /**
35161      * Returns true if this region is currently visible.
35162      * @return {Boolean}
35163      */
35164     isVisible : function(){
35165         return this.visible;
35166     },
35167
35168     /**
35169      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35170      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35171      */
35172     //setCollapsedTitle : function(title){
35173     //    title = title || "&#160;";
35174      //   if(this.collapsedTitleTextEl){
35175       //      this.collapsedTitleTextEl.innerHTML = title;
35176        // }
35177     //},
35178
35179     getBox : function(){
35180         var b;
35181       //  if(!this.collapsed){
35182             b = this.el.getBox(false, true);
35183        // }else{
35184           //  b = this.collapsedEl.getBox(false, true);
35185         //}
35186         return b;
35187     },
35188
35189     getMargins : function(){
35190         return this.margins;
35191         //return this.collapsed ? this.cmargins : this.margins;
35192     },
35193 /*
35194     highlight : function(){
35195         this.el.addClass("x-layout-panel-dragover");
35196     },
35197
35198     unhighlight : function(){
35199         this.el.removeClass("x-layout-panel-dragover");
35200     },
35201 */
35202     updateBox : function(box)
35203     {
35204         if (!this.bodyEl) {
35205             return; // not rendered yet..
35206         }
35207         
35208         this.box = box;
35209         if(!this.collapsed){
35210             this.el.dom.style.left = box.x + "px";
35211             this.el.dom.style.top = box.y + "px";
35212             this.updateBody(box.width, box.height);
35213         }else{
35214             this.collapsedEl.dom.style.left = box.x + "px";
35215             this.collapsedEl.dom.style.top = box.y + "px";
35216             this.collapsedEl.setSize(box.width, box.height);
35217         }
35218         if(this.tabs){
35219             this.tabs.autoSizeTabs();
35220         }
35221     },
35222
35223     updateBody : function(w, h)
35224     {
35225         if(w !== null){
35226             this.el.setWidth(w);
35227             w -= this.el.getBorderWidth("rl");
35228             if(this.config.adjustments){
35229                 w += this.config.adjustments[0];
35230             }
35231         }
35232         if(h !== null && h > 0){
35233             this.el.setHeight(h);
35234             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35235             h -= this.el.getBorderWidth("tb");
35236             if(this.config.adjustments){
35237                 h += this.config.adjustments[1];
35238             }
35239             this.bodyEl.setHeight(h);
35240             if(this.tabs){
35241                 h = this.tabs.syncHeight(h);
35242             }
35243         }
35244         if(this.panelSize){
35245             w = w !== null ? w : this.panelSize.width;
35246             h = h !== null ? h : this.panelSize.height;
35247         }
35248         if(this.activePanel){
35249             var el = this.activePanel.getEl();
35250             w = w !== null ? w : el.getWidth();
35251             h = h !== null ? h : el.getHeight();
35252             this.panelSize = {width: w, height: h};
35253             this.activePanel.setSize(w, h);
35254         }
35255         if(Roo.isIE && this.tabs){
35256             this.tabs.el.repaint();
35257         }
35258     },
35259
35260     /**
35261      * Returns the container element for this region.
35262      * @return {Roo.Element}
35263      */
35264     getEl : function(){
35265         return this.el;
35266     },
35267
35268     /**
35269      * Hides this region.
35270      */
35271     hide : function(){
35272         //if(!this.collapsed){
35273             this.el.dom.style.left = "-2000px";
35274             this.el.hide();
35275         //}else{
35276          //   this.collapsedEl.dom.style.left = "-2000px";
35277          //   this.collapsedEl.hide();
35278        // }
35279         this.visible = false;
35280         this.fireEvent("visibilitychange", this, false);
35281     },
35282
35283     /**
35284      * Shows this region if it was previously hidden.
35285      */
35286     show : function(){
35287         //if(!this.collapsed){
35288             this.el.show();
35289         //}else{
35290         //    this.collapsedEl.show();
35291        // }
35292         this.visible = true;
35293         this.fireEvent("visibilitychange", this, true);
35294     },
35295 /*
35296     closeClicked : function(){
35297         if(this.activePanel){
35298             this.remove(this.activePanel);
35299         }
35300     },
35301
35302     collapseClick : function(e){
35303         if(this.isSlid){
35304            e.stopPropagation();
35305            this.slideIn();
35306         }else{
35307            e.stopPropagation();
35308            this.slideOut();
35309         }
35310     },
35311 */
35312     /**
35313      * Collapses this region.
35314      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35315      */
35316     /*
35317     collapse : function(skipAnim, skipCheck = false){
35318         if(this.collapsed) {
35319             return;
35320         }
35321         
35322         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35323             
35324             this.collapsed = true;
35325             if(this.split){
35326                 this.split.el.hide();
35327             }
35328             if(this.config.animate && skipAnim !== true){
35329                 this.fireEvent("invalidated", this);
35330                 this.animateCollapse();
35331             }else{
35332                 this.el.setLocation(-20000,-20000);
35333                 this.el.hide();
35334                 this.collapsedEl.show();
35335                 this.fireEvent("collapsed", this);
35336                 this.fireEvent("invalidated", this);
35337             }
35338         }
35339         
35340     },
35341 */
35342     animateCollapse : function(){
35343         // overridden
35344     },
35345
35346     /**
35347      * Expands this region if it was previously collapsed.
35348      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35349      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35350      */
35351     /*
35352     expand : function(e, skipAnim){
35353         if(e) {
35354             e.stopPropagation();
35355         }
35356         if(!this.collapsed || this.el.hasActiveFx()) {
35357             return;
35358         }
35359         if(this.isSlid){
35360             this.afterSlideIn();
35361             skipAnim = true;
35362         }
35363         this.collapsed = false;
35364         if(this.config.animate && skipAnim !== true){
35365             this.animateExpand();
35366         }else{
35367             this.el.show();
35368             if(this.split){
35369                 this.split.el.show();
35370             }
35371             this.collapsedEl.setLocation(-2000,-2000);
35372             this.collapsedEl.hide();
35373             this.fireEvent("invalidated", this);
35374             this.fireEvent("expanded", this);
35375         }
35376     },
35377 */
35378     animateExpand : function(){
35379         // overridden
35380     },
35381
35382     initTabs : function()
35383     {
35384         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35385         
35386         var ts = new Roo.bootstrap.panel.Tabs({
35387                 el: this.bodyEl.dom,
35388                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35389                 disableTooltips: this.config.disableTabTips,
35390                 toolbar : this.config.toolbar
35391             });
35392         
35393         if(this.config.hideTabs){
35394             ts.stripWrap.setDisplayed(false);
35395         }
35396         this.tabs = ts;
35397         ts.resizeTabs = this.config.resizeTabs === true;
35398         ts.minTabWidth = this.config.minTabWidth || 40;
35399         ts.maxTabWidth = this.config.maxTabWidth || 250;
35400         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35401         ts.monitorResize = false;
35402         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35403         ts.bodyEl.addClass('roo-layout-tabs-body');
35404         this.panels.each(this.initPanelAsTab, this);
35405     },
35406
35407     initPanelAsTab : function(panel){
35408         var ti = this.tabs.addTab(
35409             panel.getEl().id,
35410             panel.getTitle(),
35411             null,
35412             this.config.closeOnTab && panel.isClosable(),
35413             panel.tpl
35414         );
35415         if(panel.tabTip !== undefined){
35416             ti.setTooltip(panel.tabTip);
35417         }
35418         ti.on("activate", function(){
35419               this.setActivePanel(panel);
35420         }, this);
35421         
35422         if(this.config.closeOnTab){
35423             ti.on("beforeclose", function(t, e){
35424                 e.cancel = true;
35425                 this.remove(panel);
35426             }, this);
35427         }
35428         
35429         panel.tabItem = ti;
35430         
35431         return ti;
35432     },
35433
35434     updatePanelTitle : function(panel, title)
35435     {
35436         if(this.activePanel == panel){
35437             this.updateTitle(title);
35438         }
35439         if(this.tabs){
35440             var ti = this.tabs.getTab(panel.getEl().id);
35441             ti.setText(title);
35442             if(panel.tabTip !== undefined){
35443                 ti.setTooltip(panel.tabTip);
35444             }
35445         }
35446     },
35447
35448     updateTitle : function(title){
35449         if(this.titleTextEl && !this.config.title){
35450             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35451         }
35452     },
35453
35454     setActivePanel : function(panel)
35455     {
35456         panel = this.getPanel(panel);
35457         if(this.activePanel && this.activePanel != panel){
35458             this.activePanel.setActiveState(false);
35459         }
35460         this.activePanel = panel;
35461         panel.setActiveState(true);
35462         if(this.panelSize){
35463             panel.setSize(this.panelSize.width, this.panelSize.height);
35464         }
35465         if(this.closeBtn){
35466             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35467         }
35468         this.updateTitle(panel.getTitle());
35469         if(this.tabs){
35470             this.fireEvent("invalidated", this);
35471         }
35472         this.fireEvent("panelactivated", this, panel);
35473     },
35474
35475     /**
35476      * Shows the specified panel.
35477      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35478      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35479      */
35480     showPanel : function(panel)
35481     {
35482         panel = this.getPanel(panel);
35483         if(panel){
35484             if(this.tabs){
35485                 var tab = this.tabs.getTab(panel.getEl().id);
35486                 if(tab.isHidden()){
35487                     this.tabs.unhideTab(tab.id);
35488                 }
35489                 tab.activate();
35490             }else{
35491                 this.setActivePanel(panel);
35492             }
35493         }
35494         return panel;
35495     },
35496
35497     /**
35498      * Get the active panel for this region.
35499      * @return {Roo.ContentPanel} The active panel or null
35500      */
35501     getActivePanel : function(){
35502         return this.activePanel;
35503     },
35504
35505     validateVisibility : function(){
35506         if(this.panels.getCount() < 1){
35507             this.updateTitle("&#160;");
35508             this.closeBtn.hide();
35509             this.hide();
35510         }else{
35511             if(!this.isVisible()){
35512                 this.show();
35513             }
35514         }
35515     },
35516
35517     /**
35518      * Adds the passed ContentPanel(s) to this region.
35519      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35520      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35521      */
35522     add : function(panel)
35523     {
35524         if(arguments.length > 1){
35525             for(var i = 0, len = arguments.length; i < len; i++) {
35526                 this.add(arguments[i]);
35527             }
35528             return null;
35529         }
35530         
35531         // if we have not been rendered yet, then we can not really do much of this..
35532         if (!this.bodyEl) {
35533             this.unrendered_panels.push(panel);
35534             return panel;
35535         }
35536         
35537         
35538         
35539         
35540         if(this.hasPanel(panel)){
35541             this.showPanel(panel);
35542             return panel;
35543         }
35544         panel.setRegion(this);
35545         this.panels.add(panel);
35546        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35547             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35548             // and hide them... ???
35549             this.bodyEl.dom.appendChild(panel.getEl().dom);
35550             if(panel.background !== true){
35551                 this.setActivePanel(panel);
35552             }
35553             this.fireEvent("paneladded", this, panel);
35554             return panel;
35555         }
35556         */
35557         if(!this.tabs){
35558             this.initTabs();
35559         }else{
35560             this.initPanelAsTab(panel);
35561         }
35562         
35563         
35564         if(panel.background !== true){
35565             this.tabs.activate(panel.getEl().id);
35566         }
35567         this.fireEvent("paneladded", this, panel);
35568         return panel;
35569     },
35570
35571     /**
35572      * Hides the tab for the specified panel.
35573      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35574      */
35575     hidePanel : function(panel){
35576         if(this.tabs && (panel = this.getPanel(panel))){
35577             this.tabs.hideTab(panel.getEl().id);
35578         }
35579     },
35580
35581     /**
35582      * Unhides the tab for a previously hidden panel.
35583      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35584      */
35585     unhidePanel : function(panel){
35586         if(this.tabs && (panel = this.getPanel(panel))){
35587             this.tabs.unhideTab(panel.getEl().id);
35588         }
35589     },
35590
35591     clearPanels : function(){
35592         while(this.panels.getCount() > 0){
35593              this.remove(this.panels.first());
35594         }
35595     },
35596
35597     /**
35598      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35599      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35600      * @param {Boolean} preservePanel Overrides the config preservePanel option
35601      * @return {Roo.ContentPanel} The panel that was removed
35602      */
35603     remove : function(panel, preservePanel)
35604     {
35605         panel = this.getPanel(panel);
35606         if(!panel){
35607             return null;
35608         }
35609         var e = {};
35610         this.fireEvent("beforeremove", this, panel, e);
35611         if(e.cancel === true){
35612             return null;
35613         }
35614         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35615         var panelId = panel.getId();
35616         this.panels.removeKey(panelId);
35617         if(preservePanel){
35618             document.body.appendChild(panel.getEl().dom);
35619         }
35620         if(this.tabs){
35621             this.tabs.removeTab(panel.getEl().id);
35622         }else if (!preservePanel){
35623             this.bodyEl.dom.removeChild(panel.getEl().dom);
35624         }
35625         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35626             var p = this.panels.first();
35627             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35628             tempEl.appendChild(p.getEl().dom);
35629             this.bodyEl.update("");
35630             this.bodyEl.dom.appendChild(p.getEl().dom);
35631             tempEl = null;
35632             this.updateTitle(p.getTitle());
35633             this.tabs = null;
35634             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35635             this.setActivePanel(p);
35636         }
35637         panel.setRegion(null);
35638         if(this.activePanel == panel){
35639             this.activePanel = null;
35640         }
35641         if(this.config.autoDestroy !== false && preservePanel !== true){
35642             try{panel.destroy();}catch(e){}
35643         }
35644         this.fireEvent("panelremoved", this, panel);
35645         return panel;
35646     },
35647
35648     /**
35649      * Returns the TabPanel component used by this region
35650      * @return {Roo.TabPanel}
35651      */
35652     getTabs : function(){
35653         return this.tabs;
35654     },
35655
35656     createTool : function(parentEl, className){
35657         var btn = Roo.DomHelper.append(parentEl, {
35658             tag: "div",
35659             cls: "x-layout-tools-button",
35660             children: [ {
35661                 tag: "div",
35662                 cls: "roo-layout-tools-button-inner " + className,
35663                 html: "&#160;"
35664             }]
35665         }, true);
35666         btn.addClassOnOver("roo-layout-tools-button-over");
35667         return btn;
35668     }
35669 });/*
35670  * Based on:
35671  * Ext JS Library 1.1.1
35672  * Copyright(c) 2006-2007, Ext JS, LLC.
35673  *
35674  * Originally Released Under LGPL - original licence link has changed is not relivant.
35675  *
35676  * Fork - LGPL
35677  * <script type="text/javascript">
35678  */
35679  
35680
35681
35682 /**
35683  * @class Roo.SplitLayoutRegion
35684  * @extends Roo.LayoutRegion
35685  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35686  */
35687 Roo.bootstrap.layout.Split = function(config){
35688     this.cursor = config.cursor;
35689     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35690 };
35691
35692 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35693 {
35694     splitTip : "Drag to resize.",
35695     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35696     useSplitTips : false,
35697
35698     applyConfig : function(config){
35699         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35700     },
35701     
35702     onRender : function(ctr,pos) {
35703         
35704         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35705         if(!this.config.split){
35706             return;
35707         }
35708         if(!this.split){
35709             
35710             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35711                             tag: "div",
35712                             id: this.el.id + "-split",
35713                             cls: "roo-layout-split roo-layout-split-"+this.position,
35714                             html: "&#160;"
35715             });
35716             /** The SplitBar for this region 
35717             * @type Roo.SplitBar */
35718             // does not exist yet...
35719             Roo.log([this.position, this.orientation]);
35720             
35721             this.split = new Roo.bootstrap.SplitBar({
35722                 dragElement : splitEl,
35723                 resizingElement: this.el,
35724                 orientation : this.orientation
35725             });
35726             
35727             this.split.on("moved", this.onSplitMove, this);
35728             this.split.useShim = this.config.useShim === true;
35729             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35730             if(this.useSplitTips){
35731                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35732             }
35733             //if(config.collapsible){
35734             //    this.split.el.on("dblclick", this.collapse,  this);
35735             //}
35736         }
35737         if(typeof this.config.minSize != "undefined"){
35738             this.split.minSize = this.config.minSize;
35739         }
35740         if(typeof this.config.maxSize != "undefined"){
35741             this.split.maxSize = this.config.maxSize;
35742         }
35743         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35744             this.hideSplitter();
35745         }
35746         
35747     },
35748
35749     getHMaxSize : function(){
35750          var cmax = this.config.maxSize || 10000;
35751          var center = this.mgr.getRegion("center");
35752          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35753     },
35754
35755     getVMaxSize : function(){
35756          var cmax = this.config.maxSize || 10000;
35757          var center = this.mgr.getRegion("center");
35758          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35759     },
35760
35761     onSplitMove : function(split, newSize){
35762         this.fireEvent("resized", this, newSize);
35763     },
35764     
35765     /** 
35766      * Returns the {@link Roo.SplitBar} for this region.
35767      * @return {Roo.SplitBar}
35768      */
35769     getSplitBar : function(){
35770         return this.split;
35771     },
35772     
35773     hide : function(){
35774         this.hideSplitter();
35775         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35776     },
35777
35778     hideSplitter : function(){
35779         if(this.split){
35780             this.split.el.setLocation(-2000,-2000);
35781             this.split.el.hide();
35782         }
35783     },
35784
35785     show : function(){
35786         if(this.split){
35787             this.split.el.show();
35788         }
35789         Roo.bootstrap.layout.Split.superclass.show.call(this);
35790     },
35791     
35792     beforeSlide: function(){
35793         if(Roo.isGecko){// firefox overflow auto bug workaround
35794             this.bodyEl.clip();
35795             if(this.tabs) {
35796                 this.tabs.bodyEl.clip();
35797             }
35798             if(this.activePanel){
35799                 this.activePanel.getEl().clip();
35800                 
35801                 if(this.activePanel.beforeSlide){
35802                     this.activePanel.beforeSlide();
35803                 }
35804             }
35805         }
35806     },
35807     
35808     afterSlide : function(){
35809         if(Roo.isGecko){// firefox overflow auto bug workaround
35810             this.bodyEl.unclip();
35811             if(this.tabs) {
35812                 this.tabs.bodyEl.unclip();
35813             }
35814             if(this.activePanel){
35815                 this.activePanel.getEl().unclip();
35816                 if(this.activePanel.afterSlide){
35817                     this.activePanel.afterSlide();
35818                 }
35819             }
35820         }
35821     },
35822
35823     initAutoHide : function(){
35824         if(this.autoHide !== false){
35825             if(!this.autoHideHd){
35826                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35827                 this.autoHideHd = {
35828                     "mouseout": function(e){
35829                         if(!e.within(this.el, true)){
35830                             st.delay(500);
35831                         }
35832                     },
35833                     "mouseover" : function(e){
35834                         st.cancel();
35835                     },
35836                     scope : this
35837                 };
35838             }
35839             this.el.on(this.autoHideHd);
35840         }
35841     },
35842
35843     clearAutoHide : function(){
35844         if(this.autoHide !== false){
35845             this.el.un("mouseout", this.autoHideHd.mouseout);
35846             this.el.un("mouseover", this.autoHideHd.mouseover);
35847         }
35848     },
35849
35850     clearMonitor : function(){
35851         Roo.get(document).un("click", this.slideInIf, this);
35852     },
35853
35854     // these names are backwards but not changed for compat
35855     slideOut : function(){
35856         if(this.isSlid || this.el.hasActiveFx()){
35857             return;
35858         }
35859         this.isSlid = true;
35860         if(this.collapseBtn){
35861             this.collapseBtn.hide();
35862         }
35863         this.closeBtnState = this.closeBtn.getStyle('display');
35864         this.closeBtn.hide();
35865         if(this.stickBtn){
35866             this.stickBtn.show();
35867         }
35868         this.el.show();
35869         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35870         this.beforeSlide();
35871         this.el.setStyle("z-index", 10001);
35872         this.el.slideIn(this.getSlideAnchor(), {
35873             callback: function(){
35874                 this.afterSlide();
35875                 this.initAutoHide();
35876                 Roo.get(document).on("click", this.slideInIf, this);
35877                 this.fireEvent("slideshow", this);
35878             },
35879             scope: this,
35880             block: true
35881         });
35882     },
35883
35884     afterSlideIn : function(){
35885         this.clearAutoHide();
35886         this.isSlid = false;
35887         this.clearMonitor();
35888         this.el.setStyle("z-index", "");
35889         if(this.collapseBtn){
35890             this.collapseBtn.show();
35891         }
35892         this.closeBtn.setStyle('display', this.closeBtnState);
35893         if(this.stickBtn){
35894             this.stickBtn.hide();
35895         }
35896         this.fireEvent("slidehide", this);
35897     },
35898
35899     slideIn : function(cb){
35900         if(!this.isSlid || this.el.hasActiveFx()){
35901             Roo.callback(cb);
35902             return;
35903         }
35904         this.isSlid = false;
35905         this.beforeSlide();
35906         this.el.slideOut(this.getSlideAnchor(), {
35907             callback: function(){
35908                 this.el.setLeftTop(-10000, -10000);
35909                 this.afterSlide();
35910                 this.afterSlideIn();
35911                 Roo.callback(cb);
35912             },
35913             scope: this,
35914             block: true
35915         });
35916     },
35917     
35918     slideInIf : function(e){
35919         if(!e.within(this.el)){
35920             this.slideIn();
35921         }
35922     },
35923
35924     animateCollapse : function(){
35925         this.beforeSlide();
35926         this.el.setStyle("z-index", 20000);
35927         var anchor = this.getSlideAnchor();
35928         this.el.slideOut(anchor, {
35929             callback : function(){
35930                 this.el.setStyle("z-index", "");
35931                 this.collapsedEl.slideIn(anchor, {duration:.3});
35932                 this.afterSlide();
35933                 this.el.setLocation(-10000,-10000);
35934                 this.el.hide();
35935                 this.fireEvent("collapsed", this);
35936             },
35937             scope: this,
35938             block: true
35939         });
35940     },
35941
35942     animateExpand : function(){
35943         this.beforeSlide();
35944         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35945         this.el.setStyle("z-index", 20000);
35946         this.collapsedEl.hide({
35947             duration:.1
35948         });
35949         this.el.slideIn(this.getSlideAnchor(), {
35950             callback : function(){
35951                 this.el.setStyle("z-index", "");
35952                 this.afterSlide();
35953                 if(this.split){
35954                     this.split.el.show();
35955                 }
35956                 this.fireEvent("invalidated", this);
35957                 this.fireEvent("expanded", this);
35958             },
35959             scope: this,
35960             block: true
35961         });
35962     },
35963
35964     anchors : {
35965         "west" : "left",
35966         "east" : "right",
35967         "north" : "top",
35968         "south" : "bottom"
35969     },
35970
35971     sanchors : {
35972         "west" : "l",
35973         "east" : "r",
35974         "north" : "t",
35975         "south" : "b"
35976     },
35977
35978     canchors : {
35979         "west" : "tl-tr",
35980         "east" : "tr-tl",
35981         "north" : "tl-bl",
35982         "south" : "bl-tl"
35983     },
35984
35985     getAnchor : function(){
35986         return this.anchors[this.position];
35987     },
35988
35989     getCollapseAnchor : function(){
35990         return this.canchors[this.position];
35991     },
35992
35993     getSlideAnchor : function(){
35994         return this.sanchors[this.position];
35995     },
35996
35997     getAlignAdj : function(){
35998         var cm = this.cmargins;
35999         switch(this.position){
36000             case "west":
36001                 return [0, 0];
36002             break;
36003             case "east":
36004                 return [0, 0];
36005             break;
36006             case "north":
36007                 return [0, 0];
36008             break;
36009             case "south":
36010                 return [0, 0];
36011             break;
36012         }
36013     },
36014
36015     getExpandAdj : function(){
36016         var c = this.collapsedEl, cm = this.cmargins;
36017         switch(this.position){
36018             case "west":
36019                 return [-(cm.right+c.getWidth()+cm.left), 0];
36020             break;
36021             case "east":
36022                 return [cm.right+c.getWidth()+cm.left, 0];
36023             break;
36024             case "north":
36025                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36026             break;
36027             case "south":
36028                 return [0, cm.top+cm.bottom+c.getHeight()];
36029             break;
36030         }
36031     }
36032 });/*
36033  * Based on:
36034  * Ext JS Library 1.1.1
36035  * Copyright(c) 2006-2007, Ext JS, LLC.
36036  *
36037  * Originally Released Under LGPL - original licence link has changed is not relivant.
36038  *
36039  * Fork - LGPL
36040  * <script type="text/javascript">
36041  */
36042 /*
36043  * These classes are private internal classes
36044  */
36045 Roo.bootstrap.layout.Center = function(config){
36046     config.region = "center";
36047     Roo.bootstrap.layout.Region.call(this, config);
36048     this.visible = true;
36049     this.minWidth = config.minWidth || 20;
36050     this.minHeight = config.minHeight || 20;
36051 };
36052
36053 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36054     hide : function(){
36055         // center panel can't be hidden
36056     },
36057     
36058     show : function(){
36059         // center panel can't be hidden
36060     },
36061     
36062     getMinWidth: function(){
36063         return this.minWidth;
36064     },
36065     
36066     getMinHeight: function(){
36067         return this.minHeight;
36068     }
36069 });
36070
36071
36072
36073
36074  
36075
36076
36077
36078
36079
36080 Roo.bootstrap.layout.North = function(config)
36081 {
36082     config.region = 'north';
36083     config.cursor = 'n-resize';
36084     
36085     Roo.bootstrap.layout.Split.call(this, config);
36086     
36087     
36088     if(this.split){
36089         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36090         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36091         this.split.el.addClass("roo-layout-split-v");
36092     }
36093     var size = config.initialSize || config.height;
36094     if(typeof size != "undefined"){
36095         this.el.setHeight(size);
36096     }
36097 };
36098 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36099 {
36100     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36101     
36102     
36103     
36104     getBox : function(){
36105         if(this.collapsed){
36106             return this.collapsedEl.getBox();
36107         }
36108         var box = this.el.getBox();
36109         if(this.split){
36110             box.height += this.split.el.getHeight();
36111         }
36112         return box;
36113     },
36114     
36115     updateBox : function(box){
36116         if(this.split && !this.collapsed){
36117             box.height -= this.split.el.getHeight();
36118             this.split.el.setLeft(box.x);
36119             this.split.el.setTop(box.y+box.height);
36120             this.split.el.setWidth(box.width);
36121         }
36122         if(this.collapsed){
36123             this.updateBody(box.width, null);
36124         }
36125         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36126     }
36127 });
36128
36129
36130
36131
36132
36133 Roo.bootstrap.layout.South = function(config){
36134     config.region = 'south';
36135     config.cursor = 's-resize';
36136     Roo.bootstrap.layout.Split.call(this, config);
36137     if(this.split){
36138         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36139         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36140         this.split.el.addClass("roo-layout-split-v");
36141     }
36142     var size = config.initialSize || config.height;
36143     if(typeof size != "undefined"){
36144         this.el.setHeight(size);
36145     }
36146 };
36147
36148 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36149     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36150     getBox : function(){
36151         if(this.collapsed){
36152             return this.collapsedEl.getBox();
36153         }
36154         var box = this.el.getBox();
36155         if(this.split){
36156             var sh = this.split.el.getHeight();
36157             box.height += sh;
36158             box.y -= sh;
36159         }
36160         return box;
36161     },
36162     
36163     updateBox : function(box){
36164         if(this.split && !this.collapsed){
36165             var sh = this.split.el.getHeight();
36166             box.height -= sh;
36167             box.y += sh;
36168             this.split.el.setLeft(box.x);
36169             this.split.el.setTop(box.y-sh);
36170             this.split.el.setWidth(box.width);
36171         }
36172         if(this.collapsed){
36173             this.updateBody(box.width, null);
36174         }
36175         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36176     }
36177 });
36178
36179 Roo.bootstrap.layout.East = function(config){
36180     config.region = "east";
36181     config.cursor = "e-resize";
36182     Roo.bootstrap.layout.Split.call(this, config);
36183     if(this.split){
36184         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36185         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36186         this.split.el.addClass("roo-layout-split-h");
36187     }
36188     var size = config.initialSize || config.width;
36189     if(typeof size != "undefined"){
36190         this.el.setWidth(size);
36191     }
36192 };
36193 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36194     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36195     getBox : function(){
36196         if(this.collapsed){
36197             return this.collapsedEl.getBox();
36198         }
36199         var box = this.el.getBox();
36200         if(this.split){
36201             var sw = this.split.el.getWidth();
36202             box.width += sw;
36203             box.x -= sw;
36204         }
36205         return box;
36206     },
36207
36208     updateBox : function(box){
36209         if(this.split && !this.collapsed){
36210             var sw = this.split.el.getWidth();
36211             box.width -= sw;
36212             this.split.el.setLeft(box.x);
36213             this.split.el.setTop(box.y);
36214             this.split.el.setHeight(box.height);
36215             box.x += sw;
36216         }
36217         if(this.collapsed){
36218             this.updateBody(null, box.height);
36219         }
36220         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36221     }
36222 });
36223
36224 Roo.bootstrap.layout.West = function(config){
36225     config.region = "west";
36226     config.cursor = "w-resize";
36227     
36228     Roo.bootstrap.layout.Split.call(this, config);
36229     if(this.split){
36230         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36231         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36232         this.split.el.addClass("roo-layout-split-h");
36233     }
36234     
36235 };
36236 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36237     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36238     
36239     onRender: function(ctr, pos)
36240     {
36241         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36242         var size = this.config.initialSize || this.config.width;
36243         if(typeof size != "undefined"){
36244             this.el.setWidth(size);
36245         }
36246     },
36247     
36248     getBox : function(){
36249         if(this.collapsed){
36250             return this.collapsedEl.getBox();
36251         }
36252         var box = this.el.getBox();
36253         if(this.split){
36254             box.width += this.split.el.getWidth();
36255         }
36256         return box;
36257     },
36258     
36259     updateBox : function(box){
36260         if(this.split && !this.collapsed){
36261             var sw = this.split.el.getWidth();
36262             box.width -= sw;
36263             this.split.el.setLeft(box.x+box.width);
36264             this.split.el.setTop(box.y);
36265             this.split.el.setHeight(box.height);
36266         }
36267         if(this.collapsed){
36268             this.updateBody(null, box.height);
36269         }
36270         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36271     }
36272 });
36273 Roo.namespace("Roo.bootstrap.panel");/*
36274  * Based on:
36275  * Ext JS Library 1.1.1
36276  * Copyright(c) 2006-2007, Ext JS, LLC.
36277  *
36278  * Originally Released Under LGPL - original licence link has changed is not relivant.
36279  *
36280  * Fork - LGPL
36281  * <script type="text/javascript">
36282  */
36283 /**
36284  * @class Roo.ContentPanel
36285  * @extends Roo.util.Observable
36286  * A basic ContentPanel element.
36287  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36288  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36289  * @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
36290  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36291  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36292  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36293  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36294  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36295  * @cfg {String} title          The title for this panel
36296  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36297  * @cfg {String} url            Calls {@link #setUrl} with this value
36298  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36299  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36300  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36301  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36302  * @cfg {Boolean} badges render the badges
36303
36304  * @constructor
36305  * Create a new ContentPanel.
36306  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36307  * @param {String/Object} config A string to set only the title or a config object
36308  * @param {String} content (optional) Set the HTML content for this panel
36309  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36310  */
36311 Roo.bootstrap.panel.Content = function( config){
36312     
36313     this.tpl = config.tpl || false;
36314     
36315     var el = config.el;
36316     var content = config.content;
36317
36318     if(config.autoCreate){ // xtype is available if this is called from factory
36319         el = Roo.id();
36320     }
36321     this.el = Roo.get(el);
36322     if(!this.el && config && config.autoCreate){
36323         if(typeof config.autoCreate == "object"){
36324             if(!config.autoCreate.id){
36325                 config.autoCreate.id = config.id||el;
36326             }
36327             this.el = Roo.DomHelper.append(document.body,
36328                         config.autoCreate, true);
36329         }else{
36330             var elcfg =  {   tag: "div",
36331                             cls: "roo-layout-inactive-content",
36332                             id: config.id||el
36333                             };
36334             if (config.html) {
36335                 elcfg.html = config.html;
36336                 
36337             }
36338                         
36339             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36340         }
36341     } 
36342     this.closable = false;
36343     this.loaded = false;
36344     this.active = false;
36345    
36346       
36347     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36348         
36349         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36350         
36351         this.wrapEl = this.el; //this.el.wrap();
36352         var ti = [];
36353         if (config.toolbar.items) {
36354             ti = config.toolbar.items ;
36355             delete config.toolbar.items ;
36356         }
36357         
36358         var nitems = [];
36359         this.toolbar.render(this.wrapEl, 'before');
36360         for(var i =0;i < ti.length;i++) {
36361           //  Roo.log(['add child', items[i]]);
36362             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36363         }
36364         this.toolbar.items = nitems;
36365         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36366         delete config.toolbar;
36367         
36368     }
36369     /*
36370     // xtype created footer. - not sure if will work as we normally have to render first..
36371     if (this.footer && !this.footer.el && this.footer.xtype) {
36372         if (!this.wrapEl) {
36373             this.wrapEl = this.el.wrap();
36374         }
36375     
36376         this.footer.container = this.wrapEl.createChild();
36377          
36378         this.footer = Roo.factory(this.footer, Roo);
36379         
36380     }
36381     */
36382     
36383      if(typeof config == "string"){
36384         this.title = config;
36385     }else{
36386         Roo.apply(this, config);
36387     }
36388     
36389     if(this.resizeEl){
36390         this.resizeEl = Roo.get(this.resizeEl, true);
36391     }else{
36392         this.resizeEl = this.el;
36393     }
36394     // handle view.xtype
36395     
36396  
36397     
36398     
36399     this.addEvents({
36400         /**
36401          * @event activate
36402          * Fires when this panel is activated. 
36403          * @param {Roo.ContentPanel} this
36404          */
36405         "activate" : true,
36406         /**
36407          * @event deactivate
36408          * Fires when this panel is activated. 
36409          * @param {Roo.ContentPanel} this
36410          */
36411         "deactivate" : true,
36412
36413         /**
36414          * @event resize
36415          * Fires when this panel is resized if fitToFrame is true.
36416          * @param {Roo.ContentPanel} this
36417          * @param {Number} width The width after any component adjustments
36418          * @param {Number} height The height after any component adjustments
36419          */
36420         "resize" : true,
36421         
36422          /**
36423          * @event render
36424          * Fires when this tab is created
36425          * @param {Roo.ContentPanel} this
36426          */
36427         "render" : true
36428         
36429         
36430         
36431     });
36432     
36433
36434     
36435     
36436     if(this.autoScroll){
36437         this.resizeEl.setStyle("overflow", "auto");
36438     } else {
36439         // fix randome scrolling
36440         //this.el.on('scroll', function() {
36441         //    Roo.log('fix random scolling');
36442         //    this.scrollTo('top',0); 
36443         //});
36444     }
36445     content = content || this.content;
36446     if(content){
36447         this.setContent(content);
36448     }
36449     if(config && config.url){
36450         this.setUrl(this.url, this.params, this.loadOnce);
36451     }
36452     
36453     
36454     
36455     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36456     
36457     if (this.view && typeof(this.view.xtype) != 'undefined') {
36458         this.view.el = this.el.appendChild(document.createElement("div"));
36459         this.view = Roo.factory(this.view); 
36460         this.view.render  &&  this.view.render(false, '');  
36461     }
36462     
36463     
36464     this.fireEvent('render', this);
36465 };
36466
36467 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36468     
36469     tabTip : '',
36470     
36471     setRegion : function(region){
36472         this.region = region;
36473         this.setActiveClass(region && !this.background);
36474     },
36475     
36476     
36477     setActiveClass: function(state)
36478     {
36479         if(state){
36480            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36481            this.el.setStyle('position','relative');
36482         }else{
36483            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36484            this.el.setStyle('position', 'absolute');
36485         } 
36486     },
36487     
36488     /**
36489      * Returns the toolbar for this Panel if one was configured. 
36490      * @return {Roo.Toolbar} 
36491      */
36492     getToolbar : function(){
36493         return this.toolbar;
36494     },
36495     
36496     setActiveState : function(active)
36497     {
36498         this.active = active;
36499         this.setActiveClass(active);
36500         if(!active){
36501             this.fireEvent("deactivate", this);
36502         }else{
36503             this.fireEvent("activate", this);
36504         }
36505     },
36506     /**
36507      * Updates this panel's element
36508      * @param {String} content The new content
36509      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36510     */
36511     setContent : function(content, loadScripts){
36512         this.el.update(content, loadScripts);
36513     },
36514
36515     ignoreResize : function(w, h){
36516         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36517             return true;
36518         }else{
36519             this.lastSize = {width: w, height: h};
36520             return false;
36521         }
36522     },
36523     /**
36524      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36525      * @return {Roo.UpdateManager} The UpdateManager
36526      */
36527     getUpdateManager : function(){
36528         return this.el.getUpdateManager();
36529     },
36530      /**
36531      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36532      * @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:
36533 <pre><code>
36534 panel.load({
36535     url: "your-url.php",
36536     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36537     callback: yourFunction,
36538     scope: yourObject, //(optional scope)
36539     discardUrl: false,
36540     nocache: false,
36541     text: "Loading...",
36542     timeout: 30,
36543     scripts: false
36544 });
36545 </code></pre>
36546      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36547      * 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.
36548      * @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}
36549      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36550      * @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.
36551      * @return {Roo.ContentPanel} this
36552      */
36553     load : function(){
36554         var um = this.el.getUpdateManager();
36555         um.update.apply(um, arguments);
36556         return this;
36557     },
36558
36559
36560     /**
36561      * 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.
36562      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36563      * @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)
36564      * @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)
36565      * @return {Roo.UpdateManager} The UpdateManager
36566      */
36567     setUrl : function(url, params, loadOnce){
36568         if(this.refreshDelegate){
36569             this.removeListener("activate", this.refreshDelegate);
36570         }
36571         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36572         this.on("activate", this.refreshDelegate);
36573         return this.el.getUpdateManager();
36574     },
36575     
36576     _handleRefresh : function(url, params, loadOnce){
36577         if(!loadOnce || !this.loaded){
36578             var updater = this.el.getUpdateManager();
36579             updater.update(url, params, this._setLoaded.createDelegate(this));
36580         }
36581     },
36582     
36583     _setLoaded : function(){
36584         this.loaded = true;
36585     }, 
36586     
36587     /**
36588      * Returns this panel's id
36589      * @return {String} 
36590      */
36591     getId : function(){
36592         return this.el.id;
36593     },
36594     
36595     /** 
36596      * Returns this panel's element - used by regiosn to add.
36597      * @return {Roo.Element} 
36598      */
36599     getEl : function(){
36600         return this.wrapEl || this.el;
36601     },
36602     
36603    
36604     
36605     adjustForComponents : function(width, height)
36606     {
36607         //Roo.log('adjustForComponents ');
36608         if(this.resizeEl != this.el){
36609             width -= this.el.getFrameWidth('lr');
36610             height -= this.el.getFrameWidth('tb');
36611         }
36612         if(this.toolbar){
36613             var te = this.toolbar.getEl();
36614             te.setWidth(width);
36615             height -= te.getHeight();
36616         }
36617         if(this.footer){
36618             var te = this.footer.getEl();
36619             te.setWidth(width);
36620             height -= te.getHeight();
36621         }
36622         
36623         
36624         if(this.adjustments){
36625             width += this.adjustments[0];
36626             height += this.adjustments[1];
36627         }
36628         return {"width": width, "height": height};
36629     },
36630     
36631     setSize : function(width, height){
36632         if(this.fitToFrame && !this.ignoreResize(width, height)){
36633             if(this.fitContainer && this.resizeEl != this.el){
36634                 this.el.setSize(width, height);
36635             }
36636             var size = this.adjustForComponents(width, height);
36637             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36638             this.fireEvent('resize', this, size.width, size.height);
36639         }
36640     },
36641     
36642     /**
36643      * Returns this panel's title
36644      * @return {String} 
36645      */
36646     getTitle : function(){
36647         
36648         if (typeof(this.title) != 'object') {
36649             return this.title;
36650         }
36651         
36652         var t = '';
36653         for (var k in this.title) {
36654             if (!this.title.hasOwnProperty(k)) {
36655                 continue;
36656             }
36657             
36658             if (k.indexOf('-') >= 0) {
36659                 var s = k.split('-');
36660                 for (var i = 0; i<s.length; i++) {
36661                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36662                 }
36663             } else {
36664                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36665             }
36666         }
36667         return t;
36668     },
36669     
36670     /**
36671      * Set this panel's title
36672      * @param {String} title
36673      */
36674     setTitle : function(title){
36675         this.title = title;
36676         if(this.region){
36677             this.region.updatePanelTitle(this, title);
36678         }
36679     },
36680     
36681     /**
36682      * Returns true is this panel was configured to be closable
36683      * @return {Boolean} 
36684      */
36685     isClosable : function(){
36686         return this.closable;
36687     },
36688     
36689     beforeSlide : function(){
36690         this.el.clip();
36691         this.resizeEl.clip();
36692     },
36693     
36694     afterSlide : function(){
36695         this.el.unclip();
36696         this.resizeEl.unclip();
36697     },
36698     
36699     /**
36700      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36701      *   Will fail silently if the {@link #setUrl} method has not been called.
36702      *   This does not activate the panel, just updates its content.
36703      */
36704     refresh : function(){
36705         if(this.refreshDelegate){
36706            this.loaded = false;
36707            this.refreshDelegate();
36708         }
36709     },
36710     
36711     /**
36712      * Destroys this panel
36713      */
36714     destroy : function(){
36715         this.el.removeAllListeners();
36716         var tempEl = document.createElement("span");
36717         tempEl.appendChild(this.el.dom);
36718         tempEl.innerHTML = "";
36719         this.el.remove();
36720         this.el = null;
36721     },
36722     
36723     /**
36724      * form - if the content panel contains a form - this is a reference to it.
36725      * @type {Roo.form.Form}
36726      */
36727     form : false,
36728     /**
36729      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36730      *    This contains a reference to it.
36731      * @type {Roo.View}
36732      */
36733     view : false,
36734     
36735       /**
36736      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36737      * <pre><code>
36738
36739 layout.addxtype({
36740        xtype : 'Form',
36741        items: [ .... ]
36742    }
36743 );
36744
36745 </code></pre>
36746      * @param {Object} cfg Xtype definition of item to add.
36747      */
36748     
36749     
36750     getChildContainer: function () {
36751         return this.getEl();
36752     }
36753     
36754     
36755     /*
36756         var  ret = new Roo.factory(cfg);
36757         return ret;
36758         
36759         
36760         // add form..
36761         if (cfg.xtype.match(/^Form$/)) {
36762             
36763             var el;
36764             //if (this.footer) {
36765             //    el = this.footer.container.insertSibling(false, 'before');
36766             //} else {
36767                 el = this.el.createChild();
36768             //}
36769
36770             this.form = new  Roo.form.Form(cfg);
36771             
36772             
36773             if ( this.form.allItems.length) {
36774                 this.form.render(el.dom);
36775             }
36776             return this.form;
36777         }
36778         // should only have one of theses..
36779         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36780             // views.. should not be just added - used named prop 'view''
36781             
36782             cfg.el = this.el.appendChild(document.createElement("div"));
36783             // factory?
36784             
36785             var ret = new Roo.factory(cfg);
36786              
36787              ret.render && ret.render(false, ''); // render blank..
36788             this.view = ret;
36789             return ret;
36790         }
36791         return false;
36792     }
36793     \*/
36794 });
36795  
36796 /**
36797  * @class Roo.bootstrap.panel.Grid
36798  * @extends Roo.bootstrap.panel.Content
36799  * @constructor
36800  * Create a new GridPanel.
36801  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36802  * @param {Object} config A the config object
36803   
36804  */
36805
36806
36807
36808 Roo.bootstrap.panel.Grid = function(config)
36809 {
36810     
36811       
36812     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36813         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36814
36815     config.el = this.wrapper;
36816     //this.el = this.wrapper;
36817     
36818       if (config.container) {
36819         // ctor'ed from a Border/panel.grid
36820         
36821         
36822         this.wrapper.setStyle("overflow", "hidden");
36823         this.wrapper.addClass('roo-grid-container');
36824
36825     }
36826     
36827     
36828     if(config.toolbar){
36829         var tool_el = this.wrapper.createChild();    
36830         this.toolbar = Roo.factory(config.toolbar);
36831         var ti = [];
36832         if (config.toolbar.items) {
36833             ti = config.toolbar.items ;
36834             delete config.toolbar.items ;
36835         }
36836         
36837         var nitems = [];
36838         this.toolbar.render(tool_el);
36839         for(var i =0;i < ti.length;i++) {
36840           //  Roo.log(['add child', items[i]]);
36841             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36842         }
36843         this.toolbar.items = nitems;
36844         
36845         delete config.toolbar;
36846     }
36847     
36848     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36849     config.grid.scrollBody = true;;
36850     config.grid.monitorWindowResize = false; // turn off autosizing
36851     config.grid.autoHeight = false;
36852     config.grid.autoWidth = false;
36853     
36854     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36855     
36856     if (config.background) {
36857         // render grid on panel activation (if panel background)
36858         this.on('activate', function(gp) {
36859             if (!gp.grid.rendered) {
36860                 gp.grid.render(this.wrapper);
36861                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36862             }
36863         });
36864             
36865     } else {
36866         this.grid.render(this.wrapper);
36867         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36868
36869     }
36870     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36871     // ??? needed ??? config.el = this.wrapper;
36872     
36873     
36874     
36875   
36876     // xtype created footer. - not sure if will work as we normally have to render first..
36877     if (this.footer && !this.footer.el && this.footer.xtype) {
36878         
36879         var ctr = this.grid.getView().getFooterPanel(true);
36880         this.footer.dataSource = this.grid.dataSource;
36881         this.footer = Roo.factory(this.footer, Roo);
36882         this.footer.render(ctr);
36883         
36884     }
36885     
36886     
36887     
36888     
36889      
36890 };
36891
36892 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36893     getId : function(){
36894         return this.grid.id;
36895     },
36896     
36897     /**
36898      * Returns the grid for this panel
36899      * @return {Roo.bootstrap.Table} 
36900      */
36901     getGrid : function(){
36902         return this.grid;    
36903     },
36904     
36905     setSize : function(width, height){
36906         if(!this.ignoreResize(width, height)){
36907             var grid = this.grid;
36908             var size = this.adjustForComponents(width, height);
36909             var gridel = grid.getGridEl();
36910             gridel.setSize(size.width, size.height);
36911             /*
36912             var thd = grid.getGridEl().select('thead',true).first();
36913             var tbd = grid.getGridEl().select('tbody', true).first();
36914             if (tbd) {
36915                 tbd.setSize(width, height - thd.getHeight());
36916             }
36917             */
36918             grid.autoSize();
36919         }
36920     },
36921      
36922     
36923     
36924     beforeSlide : function(){
36925         this.grid.getView().scroller.clip();
36926     },
36927     
36928     afterSlide : function(){
36929         this.grid.getView().scroller.unclip();
36930     },
36931     
36932     destroy : function(){
36933         this.grid.destroy();
36934         delete this.grid;
36935         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36936     }
36937 });
36938
36939 /**
36940  * @class Roo.bootstrap.panel.Nest
36941  * @extends Roo.bootstrap.panel.Content
36942  * @constructor
36943  * Create a new Panel, that can contain a layout.Border.
36944  * 
36945  * 
36946  * @param {Roo.BorderLayout} layout The layout for this panel
36947  * @param {String/Object} config A string to set only the title or a config object
36948  */
36949 Roo.bootstrap.panel.Nest = function(config)
36950 {
36951     // construct with only one argument..
36952     /* FIXME - implement nicer consturctors
36953     if (layout.layout) {
36954         config = layout;
36955         layout = config.layout;
36956         delete config.layout;
36957     }
36958     if (layout.xtype && !layout.getEl) {
36959         // then layout needs constructing..
36960         layout = Roo.factory(layout, Roo);
36961     }
36962     */
36963     
36964     config.el =  config.layout.getEl();
36965     
36966     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36967     
36968     config.layout.monitorWindowResize = false; // turn off autosizing
36969     this.layout = config.layout;
36970     this.layout.getEl().addClass("roo-layout-nested-layout");
36971     
36972     
36973     
36974     
36975 };
36976
36977 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36978
36979     setSize : function(width, height){
36980         if(!this.ignoreResize(width, height)){
36981             var size = this.adjustForComponents(width, height);
36982             var el = this.layout.getEl();
36983             if (size.height < 1) {
36984                 el.setWidth(size.width);   
36985             } else {
36986                 el.setSize(size.width, size.height);
36987             }
36988             var touch = el.dom.offsetWidth;
36989             this.layout.layout();
36990             // ie requires a double layout on the first pass
36991             if(Roo.isIE && !this.initialized){
36992                 this.initialized = true;
36993                 this.layout.layout();
36994             }
36995         }
36996     },
36997     
36998     // activate all subpanels if not currently active..
36999     
37000     setActiveState : function(active){
37001         this.active = active;
37002         this.setActiveClass(active);
37003         
37004         if(!active){
37005             this.fireEvent("deactivate", this);
37006             return;
37007         }
37008         
37009         this.fireEvent("activate", this);
37010         // not sure if this should happen before or after..
37011         if (!this.layout) {
37012             return; // should not happen..
37013         }
37014         var reg = false;
37015         for (var r in this.layout.regions) {
37016             reg = this.layout.getRegion(r);
37017             if (reg.getActivePanel()) {
37018                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37019                 reg.setActivePanel(reg.getActivePanel());
37020                 continue;
37021             }
37022             if (!reg.panels.length) {
37023                 continue;
37024             }
37025             reg.showPanel(reg.getPanel(0));
37026         }
37027         
37028         
37029         
37030         
37031     },
37032     
37033     /**
37034      * Returns the nested BorderLayout for this panel
37035      * @return {Roo.BorderLayout} 
37036      */
37037     getLayout : function(){
37038         return this.layout;
37039     },
37040     
37041      /**
37042      * Adds a xtype elements to the layout of the nested panel
37043      * <pre><code>
37044
37045 panel.addxtype({
37046        xtype : 'ContentPanel',
37047        region: 'west',
37048        items: [ .... ]
37049    }
37050 );
37051
37052 panel.addxtype({
37053         xtype : 'NestedLayoutPanel',
37054         region: 'west',
37055         layout: {
37056            center: { },
37057            west: { }   
37058         },
37059         items : [ ... list of content panels or nested layout panels.. ]
37060    }
37061 );
37062 </code></pre>
37063      * @param {Object} cfg Xtype definition of item to add.
37064      */
37065     addxtype : function(cfg) {
37066         return this.layout.addxtype(cfg);
37067     
37068     }
37069 });        /*
37070  * Based on:
37071  * Ext JS Library 1.1.1
37072  * Copyright(c) 2006-2007, Ext JS, LLC.
37073  *
37074  * Originally Released Under LGPL - original licence link has changed is not relivant.
37075  *
37076  * Fork - LGPL
37077  * <script type="text/javascript">
37078  */
37079 /**
37080  * @class Roo.TabPanel
37081  * @extends Roo.util.Observable
37082  * A lightweight tab container.
37083  * <br><br>
37084  * Usage:
37085  * <pre><code>
37086 // basic tabs 1, built from existing content
37087 var tabs = new Roo.TabPanel("tabs1");
37088 tabs.addTab("script", "View Script");
37089 tabs.addTab("markup", "View Markup");
37090 tabs.activate("script");
37091
37092 // more advanced tabs, built from javascript
37093 var jtabs = new Roo.TabPanel("jtabs");
37094 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37095
37096 // set up the UpdateManager
37097 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37098 var updater = tab2.getUpdateManager();
37099 updater.setDefaultUrl("ajax1.htm");
37100 tab2.on('activate', updater.refresh, updater, true);
37101
37102 // Use setUrl for Ajax loading
37103 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37104 tab3.setUrl("ajax2.htm", null, true);
37105
37106 // Disabled tab
37107 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37108 tab4.disable();
37109
37110 jtabs.activate("jtabs-1");
37111  * </code></pre>
37112  * @constructor
37113  * Create a new TabPanel.
37114  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37115  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37116  */
37117 Roo.bootstrap.panel.Tabs = function(config){
37118     /**
37119     * The container element for this TabPanel.
37120     * @type Roo.Element
37121     */
37122     this.el = Roo.get(config.el);
37123     delete config.el;
37124     if(config){
37125         if(typeof config == "boolean"){
37126             this.tabPosition = config ? "bottom" : "top";
37127         }else{
37128             Roo.apply(this, config);
37129         }
37130     }
37131     
37132     if(this.tabPosition == "bottom"){
37133         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37134         this.el.addClass("roo-tabs-bottom");
37135     }
37136     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37137     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37138     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37139     if(Roo.isIE){
37140         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37141     }
37142     if(this.tabPosition != "bottom"){
37143         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37144          * @type Roo.Element
37145          */
37146         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37147         this.el.addClass("roo-tabs-top");
37148     }
37149     this.items = [];
37150
37151     this.bodyEl.setStyle("position", "relative");
37152
37153     this.active = null;
37154     this.activateDelegate = this.activate.createDelegate(this);
37155
37156     this.addEvents({
37157         /**
37158          * @event tabchange
37159          * Fires when the active tab changes
37160          * @param {Roo.TabPanel} this
37161          * @param {Roo.TabPanelItem} activePanel The new active tab
37162          */
37163         "tabchange": true,
37164         /**
37165          * @event beforetabchange
37166          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37167          * @param {Roo.TabPanel} this
37168          * @param {Object} e Set cancel to true on this object to cancel the tab change
37169          * @param {Roo.TabPanelItem} tab The tab being changed to
37170          */
37171         "beforetabchange" : true
37172     });
37173
37174     Roo.EventManager.onWindowResize(this.onResize, this);
37175     this.cpad = this.el.getPadding("lr");
37176     this.hiddenCount = 0;
37177
37178
37179     // toolbar on the tabbar support...
37180     if (this.toolbar) {
37181         alert("no toolbar support yet");
37182         this.toolbar  = false;
37183         /*
37184         var tcfg = this.toolbar;
37185         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37186         this.toolbar = new Roo.Toolbar(tcfg);
37187         if (Roo.isSafari) {
37188             var tbl = tcfg.container.child('table', true);
37189             tbl.setAttribute('width', '100%');
37190         }
37191         */
37192         
37193     }
37194    
37195
37196
37197     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37198 };
37199
37200 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37201     /*
37202      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37203      */
37204     tabPosition : "top",
37205     /*
37206      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37207      */
37208     currentTabWidth : 0,
37209     /*
37210      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37211      */
37212     minTabWidth : 40,
37213     /*
37214      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37215      */
37216     maxTabWidth : 250,
37217     /*
37218      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37219      */
37220     preferredTabWidth : 175,
37221     /*
37222      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37223      */
37224     resizeTabs : false,
37225     /*
37226      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37227      */
37228     monitorResize : true,
37229     /*
37230      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37231      */
37232     toolbar : false,
37233
37234     /**
37235      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37236      * @param {String} id The id of the div to use <b>or create</b>
37237      * @param {String} text The text for the tab
37238      * @param {String} content (optional) Content to put in the TabPanelItem body
37239      * @param {Boolean} closable (optional) True to create a close icon on the tab
37240      * @return {Roo.TabPanelItem} The created TabPanelItem
37241      */
37242     addTab : function(id, text, content, closable, tpl)
37243     {
37244         var item = new Roo.bootstrap.panel.TabItem({
37245             panel: this,
37246             id : id,
37247             text : text,
37248             closable : closable,
37249             tpl : tpl
37250         });
37251         this.addTabItem(item);
37252         if(content){
37253             item.setContent(content);
37254         }
37255         return item;
37256     },
37257
37258     /**
37259      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37260      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37261      * @return {Roo.TabPanelItem}
37262      */
37263     getTab : function(id){
37264         return this.items[id];
37265     },
37266
37267     /**
37268      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37269      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37270      */
37271     hideTab : function(id){
37272         var t = this.items[id];
37273         if(!t.isHidden()){
37274            t.setHidden(true);
37275            this.hiddenCount++;
37276            this.autoSizeTabs();
37277         }
37278     },
37279
37280     /**
37281      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37282      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37283      */
37284     unhideTab : function(id){
37285         var t = this.items[id];
37286         if(t.isHidden()){
37287            t.setHidden(false);
37288            this.hiddenCount--;
37289            this.autoSizeTabs();
37290         }
37291     },
37292
37293     /**
37294      * Adds an existing {@link Roo.TabPanelItem}.
37295      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37296      */
37297     addTabItem : function(item){
37298         this.items[item.id] = item;
37299         this.items.push(item);
37300       //  if(this.resizeTabs){
37301     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37302   //         this.autoSizeTabs();
37303 //        }else{
37304 //            item.autoSize();
37305        // }
37306     },
37307
37308     /**
37309      * Removes a {@link Roo.TabPanelItem}.
37310      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37311      */
37312     removeTab : function(id){
37313         var items = this.items;
37314         var tab = items[id];
37315         if(!tab) { return; }
37316         var index = items.indexOf(tab);
37317         if(this.active == tab && items.length > 1){
37318             var newTab = this.getNextAvailable(index);
37319             if(newTab) {
37320                 newTab.activate();
37321             }
37322         }
37323         this.stripEl.dom.removeChild(tab.pnode.dom);
37324         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37325             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37326         }
37327         items.splice(index, 1);
37328         delete this.items[tab.id];
37329         tab.fireEvent("close", tab);
37330         tab.purgeListeners();
37331         this.autoSizeTabs();
37332     },
37333
37334     getNextAvailable : function(start){
37335         var items = this.items;
37336         var index = start;
37337         // look for a next tab that will slide over to
37338         // replace the one being removed
37339         while(index < items.length){
37340             var item = items[++index];
37341             if(item && !item.isHidden()){
37342                 return item;
37343             }
37344         }
37345         // if one isn't found select the previous tab (on the left)
37346         index = start;
37347         while(index >= 0){
37348             var item = items[--index];
37349             if(item && !item.isHidden()){
37350                 return item;
37351             }
37352         }
37353         return null;
37354     },
37355
37356     /**
37357      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37358      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37359      */
37360     disableTab : function(id){
37361         var tab = this.items[id];
37362         if(tab && this.active != tab){
37363             tab.disable();
37364         }
37365     },
37366
37367     /**
37368      * Enables a {@link Roo.TabPanelItem} that is disabled.
37369      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37370      */
37371     enableTab : function(id){
37372         var tab = this.items[id];
37373         tab.enable();
37374     },
37375
37376     /**
37377      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37378      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37379      * @return {Roo.TabPanelItem} The TabPanelItem.
37380      */
37381     activate : function(id){
37382         var tab = this.items[id];
37383         if(!tab){
37384             return null;
37385         }
37386         if(tab == this.active || tab.disabled){
37387             return tab;
37388         }
37389         var e = {};
37390         this.fireEvent("beforetabchange", this, e, tab);
37391         if(e.cancel !== true && !tab.disabled){
37392             if(this.active){
37393                 this.active.hide();
37394             }
37395             this.active = this.items[id];
37396             this.active.show();
37397             this.fireEvent("tabchange", this, this.active);
37398         }
37399         return tab;
37400     },
37401
37402     /**
37403      * Gets the active {@link Roo.TabPanelItem}.
37404      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37405      */
37406     getActiveTab : function(){
37407         return this.active;
37408     },
37409
37410     /**
37411      * Updates the tab body element to fit the height of the container element
37412      * for overflow scrolling
37413      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37414      */
37415     syncHeight : function(targetHeight){
37416         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37417         var bm = this.bodyEl.getMargins();
37418         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37419         this.bodyEl.setHeight(newHeight);
37420         return newHeight;
37421     },
37422
37423     onResize : function(){
37424         if(this.monitorResize){
37425             this.autoSizeTabs();
37426         }
37427     },
37428
37429     /**
37430      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37431      */
37432     beginUpdate : function(){
37433         this.updating = true;
37434     },
37435
37436     /**
37437      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37438      */
37439     endUpdate : function(){
37440         this.updating = false;
37441         this.autoSizeTabs();
37442     },
37443
37444     /**
37445      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37446      */
37447     autoSizeTabs : function(){
37448         var count = this.items.length;
37449         var vcount = count - this.hiddenCount;
37450         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37451             return;
37452         }
37453         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37454         var availWidth = Math.floor(w / vcount);
37455         var b = this.stripBody;
37456         if(b.getWidth() > w){
37457             var tabs = this.items;
37458             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37459             if(availWidth < this.minTabWidth){
37460                 /*if(!this.sleft){    // incomplete scrolling code
37461                     this.createScrollButtons();
37462                 }
37463                 this.showScroll();
37464                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37465             }
37466         }else{
37467             if(this.currentTabWidth < this.preferredTabWidth){
37468                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37469             }
37470         }
37471     },
37472
37473     /**
37474      * Returns the number of tabs in this TabPanel.
37475      * @return {Number}
37476      */
37477      getCount : function(){
37478          return this.items.length;
37479      },
37480
37481     /**
37482      * Resizes all the tabs to the passed width
37483      * @param {Number} The new width
37484      */
37485     setTabWidth : function(width){
37486         this.currentTabWidth = width;
37487         for(var i = 0, len = this.items.length; i < len; i++) {
37488                 if(!this.items[i].isHidden()) {
37489                 this.items[i].setWidth(width);
37490             }
37491         }
37492     },
37493
37494     /**
37495      * Destroys this TabPanel
37496      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37497      */
37498     destroy : function(removeEl){
37499         Roo.EventManager.removeResizeListener(this.onResize, this);
37500         for(var i = 0, len = this.items.length; i < len; i++){
37501             this.items[i].purgeListeners();
37502         }
37503         if(removeEl === true){
37504             this.el.update("");
37505             this.el.remove();
37506         }
37507     },
37508     
37509     createStrip : function(container)
37510     {
37511         var strip = document.createElement("nav");
37512         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37513         container.appendChild(strip);
37514         return strip;
37515     },
37516     
37517     createStripList : function(strip)
37518     {
37519         // div wrapper for retard IE
37520         // returns the "tr" element.
37521         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37522         //'<div class="x-tabs-strip-wrap">'+
37523           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37524           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37525         return strip.firstChild; //.firstChild.firstChild.firstChild;
37526     },
37527     createBody : function(container)
37528     {
37529         var body = document.createElement("div");
37530         Roo.id(body, "tab-body");
37531         //Roo.fly(body).addClass("x-tabs-body");
37532         Roo.fly(body).addClass("tab-content");
37533         container.appendChild(body);
37534         return body;
37535     },
37536     createItemBody :function(bodyEl, id){
37537         var body = Roo.getDom(id);
37538         if(!body){
37539             body = document.createElement("div");
37540             body.id = id;
37541         }
37542         //Roo.fly(body).addClass("x-tabs-item-body");
37543         Roo.fly(body).addClass("tab-pane");
37544          bodyEl.insertBefore(body, bodyEl.firstChild);
37545         return body;
37546     },
37547     /** @private */
37548     createStripElements :  function(stripEl, text, closable, tpl)
37549     {
37550         var td = document.createElement("li"); // was td..
37551         
37552         
37553         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37554         
37555         
37556         stripEl.appendChild(td);
37557         /*if(closable){
37558             td.className = "x-tabs-closable";
37559             if(!this.closeTpl){
37560                 this.closeTpl = new Roo.Template(
37561                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37562                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37563                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37564                 );
37565             }
37566             var el = this.closeTpl.overwrite(td, {"text": text});
37567             var close = el.getElementsByTagName("div")[0];
37568             var inner = el.getElementsByTagName("em")[0];
37569             return {"el": el, "close": close, "inner": inner};
37570         } else {
37571         */
37572         // not sure what this is..
37573 //            if(!this.tabTpl){
37574                 //this.tabTpl = new Roo.Template(
37575                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37576                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37577                 //);
37578 //                this.tabTpl = new Roo.Template(
37579 //                   '<a href="#">' +
37580 //                   '<span unselectable="on"' +
37581 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37582 //                            ' >{text}</span></a>'
37583 //                );
37584 //                
37585 //            }
37586
37587
37588             var template = tpl || this.tabTpl || false;
37589             
37590             if(!template){
37591                 
37592                 template = new Roo.Template(
37593                    '<a href="#">' +
37594                    '<span unselectable="on"' +
37595                             (this.disableTooltips ? '' : ' title="{text}"') +
37596                             ' >{text}</span></a>'
37597                 );
37598             }
37599             
37600             switch (typeof(template)) {
37601                 case 'object' :
37602                     break;
37603                 case 'string' :
37604                     template = new Roo.Template(template);
37605                     break;
37606                 default :
37607                     break;
37608             }
37609             
37610             var el = template.overwrite(td, {"text": text});
37611             
37612             var inner = el.getElementsByTagName("span")[0];
37613             
37614             return {"el": el, "inner": inner};
37615             
37616     }
37617         
37618     
37619 });
37620
37621 /**
37622  * @class Roo.TabPanelItem
37623  * @extends Roo.util.Observable
37624  * Represents an individual item (tab plus body) in a TabPanel.
37625  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37626  * @param {String} id The id of this TabPanelItem
37627  * @param {String} text The text for the tab of this TabPanelItem
37628  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37629  */
37630 Roo.bootstrap.panel.TabItem = function(config){
37631     /**
37632      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37633      * @type Roo.TabPanel
37634      */
37635     this.tabPanel = config.panel;
37636     /**
37637      * The id for this TabPanelItem
37638      * @type String
37639      */
37640     this.id = config.id;
37641     /** @private */
37642     this.disabled = false;
37643     /** @private */
37644     this.text = config.text;
37645     /** @private */
37646     this.loaded = false;
37647     this.closable = config.closable;
37648
37649     /**
37650      * The body element for this TabPanelItem.
37651      * @type Roo.Element
37652      */
37653     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37654     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37655     this.bodyEl.setStyle("display", "block");
37656     this.bodyEl.setStyle("zoom", "1");
37657     //this.hideAction();
37658
37659     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37660     /** @private */
37661     this.el = Roo.get(els.el);
37662     this.inner = Roo.get(els.inner, true);
37663     this.textEl = Roo.get(this.el.dom.firstChild, true);
37664     this.pnode = Roo.get(els.el.parentNode, true);
37665     this.el.on("mousedown", this.onTabMouseDown, this);
37666     this.el.on("click", this.onTabClick, this);
37667     /** @private */
37668     if(config.closable){
37669         var c = Roo.get(els.close, true);
37670         c.dom.title = this.closeText;
37671         c.addClassOnOver("close-over");
37672         c.on("click", this.closeClick, this);
37673      }
37674
37675     this.addEvents({
37676          /**
37677          * @event activate
37678          * Fires when this tab becomes the active tab.
37679          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37680          * @param {Roo.TabPanelItem} this
37681          */
37682         "activate": true,
37683         /**
37684          * @event beforeclose
37685          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37686          * @param {Roo.TabPanelItem} this
37687          * @param {Object} e Set cancel to true on this object to cancel the close.
37688          */
37689         "beforeclose": true,
37690         /**
37691          * @event close
37692          * Fires when this tab is closed.
37693          * @param {Roo.TabPanelItem} this
37694          */
37695          "close": true,
37696         /**
37697          * @event deactivate
37698          * Fires when this tab is no longer the active tab.
37699          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37700          * @param {Roo.TabPanelItem} this
37701          */
37702          "deactivate" : true
37703     });
37704     this.hidden = false;
37705
37706     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37707 };
37708
37709 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37710            {
37711     purgeListeners : function(){
37712        Roo.util.Observable.prototype.purgeListeners.call(this);
37713        this.el.removeAllListeners();
37714     },
37715     /**
37716      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37717      */
37718     show : function(){
37719         this.pnode.addClass("active");
37720         this.showAction();
37721         if(Roo.isOpera){
37722             this.tabPanel.stripWrap.repaint();
37723         }
37724         this.fireEvent("activate", this.tabPanel, this);
37725     },
37726
37727     /**
37728      * Returns true if this tab is the active tab.
37729      * @return {Boolean}
37730      */
37731     isActive : function(){
37732         return this.tabPanel.getActiveTab() == this;
37733     },
37734
37735     /**
37736      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37737      */
37738     hide : function(){
37739         this.pnode.removeClass("active");
37740         this.hideAction();
37741         this.fireEvent("deactivate", this.tabPanel, this);
37742     },
37743
37744     hideAction : function(){
37745         this.bodyEl.hide();
37746         this.bodyEl.setStyle("position", "absolute");
37747         this.bodyEl.setLeft("-20000px");
37748         this.bodyEl.setTop("-20000px");
37749     },
37750
37751     showAction : function(){
37752         this.bodyEl.setStyle("position", "relative");
37753         this.bodyEl.setTop("");
37754         this.bodyEl.setLeft("");
37755         this.bodyEl.show();
37756     },
37757
37758     /**
37759      * Set the tooltip for the tab.
37760      * @param {String} tooltip The tab's tooltip
37761      */
37762     setTooltip : function(text){
37763         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37764             this.textEl.dom.qtip = text;
37765             this.textEl.dom.removeAttribute('title');
37766         }else{
37767             this.textEl.dom.title = text;
37768         }
37769     },
37770
37771     onTabClick : function(e){
37772         e.preventDefault();
37773         this.tabPanel.activate(this.id);
37774     },
37775
37776     onTabMouseDown : function(e){
37777         e.preventDefault();
37778         this.tabPanel.activate(this.id);
37779     },
37780 /*
37781     getWidth : function(){
37782         return this.inner.getWidth();
37783     },
37784
37785     setWidth : function(width){
37786         var iwidth = width - this.pnode.getPadding("lr");
37787         this.inner.setWidth(iwidth);
37788         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37789         this.pnode.setWidth(width);
37790     },
37791 */
37792     /**
37793      * Show or hide the tab
37794      * @param {Boolean} hidden True to hide or false to show.
37795      */
37796     setHidden : function(hidden){
37797         this.hidden = hidden;
37798         this.pnode.setStyle("display", hidden ? "none" : "");
37799     },
37800
37801     /**
37802      * Returns true if this tab is "hidden"
37803      * @return {Boolean}
37804      */
37805     isHidden : function(){
37806         return this.hidden;
37807     },
37808
37809     /**
37810      * Returns the text for this tab
37811      * @return {String}
37812      */
37813     getText : function(){
37814         return this.text;
37815     },
37816     /*
37817     autoSize : function(){
37818         //this.el.beginMeasure();
37819         this.textEl.setWidth(1);
37820         /*
37821          *  #2804 [new] Tabs in Roojs
37822          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37823          */
37824         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37825         //this.el.endMeasure();
37826     //},
37827
37828     /**
37829      * Sets the text for the tab (Note: this also sets the tooltip text)
37830      * @param {String} text The tab's text and tooltip
37831      */
37832     setText : function(text){
37833         this.text = text;
37834         this.textEl.update(text);
37835         this.setTooltip(text);
37836         //if(!this.tabPanel.resizeTabs){
37837         //    this.autoSize();
37838         //}
37839     },
37840     /**
37841      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37842      */
37843     activate : function(){
37844         this.tabPanel.activate(this.id);
37845     },
37846
37847     /**
37848      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37849      */
37850     disable : function(){
37851         if(this.tabPanel.active != this){
37852             this.disabled = true;
37853             this.pnode.addClass("disabled");
37854         }
37855     },
37856
37857     /**
37858      * Enables this TabPanelItem if it was previously disabled.
37859      */
37860     enable : function(){
37861         this.disabled = false;
37862         this.pnode.removeClass("disabled");
37863     },
37864
37865     /**
37866      * Sets the content for this TabPanelItem.
37867      * @param {String} content The content
37868      * @param {Boolean} loadScripts true to look for and load scripts
37869      */
37870     setContent : function(content, loadScripts){
37871         this.bodyEl.update(content, loadScripts);
37872     },
37873
37874     /**
37875      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37876      * @return {Roo.UpdateManager} The UpdateManager
37877      */
37878     getUpdateManager : function(){
37879         return this.bodyEl.getUpdateManager();
37880     },
37881
37882     /**
37883      * Set a URL to be used to load the content for this TabPanelItem.
37884      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37885      * @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)
37886      * @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)
37887      * @return {Roo.UpdateManager} The UpdateManager
37888      */
37889     setUrl : function(url, params, loadOnce){
37890         if(this.refreshDelegate){
37891             this.un('activate', this.refreshDelegate);
37892         }
37893         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37894         this.on("activate", this.refreshDelegate);
37895         return this.bodyEl.getUpdateManager();
37896     },
37897
37898     /** @private */
37899     _handleRefresh : function(url, params, loadOnce){
37900         if(!loadOnce || !this.loaded){
37901             var updater = this.bodyEl.getUpdateManager();
37902             updater.update(url, params, this._setLoaded.createDelegate(this));
37903         }
37904     },
37905
37906     /**
37907      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37908      *   Will fail silently if the setUrl method has not been called.
37909      *   This does not activate the panel, just updates its content.
37910      */
37911     refresh : function(){
37912         if(this.refreshDelegate){
37913            this.loaded = false;
37914            this.refreshDelegate();
37915         }
37916     },
37917
37918     /** @private */
37919     _setLoaded : function(){
37920         this.loaded = true;
37921     },
37922
37923     /** @private */
37924     closeClick : function(e){
37925         var o = {};
37926         e.stopEvent();
37927         this.fireEvent("beforeclose", this, o);
37928         if(o.cancel !== true){
37929             this.tabPanel.removeTab(this.id);
37930         }
37931     },
37932     /**
37933      * The text displayed in the tooltip for the close icon.
37934      * @type String
37935      */
37936     closeText : "Close this tab"
37937 });
37938 /**
37939 *    This script refer to:
37940 *    Title: International Telephone Input
37941 *    Author: Jack O'Connor
37942 *    Code version:  v12.1.12
37943 *    Availability: https://github.com/jackocnr/intl-tel-input.git
37944 **/
37945
37946 Roo.bootstrap.PhoneInputData = function() {
37947     var d = [
37948       [
37949         "Afghanistan (‫افغانستان‬‎)",
37950         "af",
37951         "93"
37952       ],
37953       [
37954         "Albania (Shqipëri)",
37955         "al",
37956         "355"
37957       ],
37958       [
37959         "Algeria (‫الجزائر‬‎)",
37960         "dz",
37961         "213"
37962       ],
37963       [
37964         "American Samoa",
37965         "as",
37966         "1684"
37967       ],
37968       [
37969         "Andorra",
37970         "ad",
37971         "376"
37972       ],
37973       [
37974         "Angola",
37975         "ao",
37976         "244"
37977       ],
37978       [
37979         "Anguilla",
37980         "ai",
37981         "1264"
37982       ],
37983       [
37984         "Antigua and Barbuda",
37985         "ag",
37986         "1268"
37987       ],
37988       [
37989         "Argentina",
37990         "ar",
37991         "54"
37992       ],
37993       [
37994         "Armenia (Հայաստան)",
37995         "am",
37996         "374"
37997       ],
37998       [
37999         "Aruba",
38000         "aw",
38001         "297"
38002       ],
38003       [
38004         "Australia",
38005         "au",
38006         "61",
38007         0
38008       ],
38009       [
38010         "Austria (Österreich)",
38011         "at",
38012         "43"
38013       ],
38014       [
38015         "Azerbaijan (Azərbaycan)",
38016         "az",
38017         "994"
38018       ],
38019       [
38020         "Bahamas",
38021         "bs",
38022         "1242"
38023       ],
38024       [
38025         "Bahrain (‫البحرين‬‎)",
38026         "bh",
38027         "973"
38028       ],
38029       [
38030         "Bangladesh (বাংলাদেশ)",
38031         "bd",
38032         "880"
38033       ],
38034       [
38035         "Barbados",
38036         "bb",
38037         "1246"
38038       ],
38039       [
38040         "Belarus (Беларусь)",
38041         "by",
38042         "375"
38043       ],
38044       [
38045         "Belgium (België)",
38046         "be",
38047         "32"
38048       ],
38049       [
38050         "Belize",
38051         "bz",
38052         "501"
38053       ],
38054       [
38055         "Benin (Bénin)",
38056         "bj",
38057         "229"
38058       ],
38059       [
38060         "Bermuda",
38061         "bm",
38062         "1441"
38063       ],
38064       [
38065         "Bhutan (འབྲུག)",
38066         "bt",
38067         "975"
38068       ],
38069       [
38070         "Bolivia",
38071         "bo",
38072         "591"
38073       ],
38074       [
38075         "Bosnia and Herzegovina (Босна и Херцеговина)",
38076         "ba",
38077         "387"
38078       ],
38079       [
38080         "Botswana",
38081         "bw",
38082         "267"
38083       ],
38084       [
38085         "Brazil (Brasil)",
38086         "br",
38087         "55"
38088       ],
38089       [
38090         "British Indian Ocean Territory",
38091         "io",
38092         "246"
38093       ],
38094       [
38095         "British Virgin Islands",
38096         "vg",
38097         "1284"
38098       ],
38099       [
38100         "Brunei",
38101         "bn",
38102         "673"
38103       ],
38104       [
38105         "Bulgaria (България)",
38106         "bg",
38107         "359"
38108       ],
38109       [
38110         "Burkina Faso",
38111         "bf",
38112         "226"
38113       ],
38114       [
38115         "Burundi (Uburundi)",
38116         "bi",
38117         "257"
38118       ],
38119       [
38120         "Cambodia (កម្ពុជា)",
38121         "kh",
38122         "855"
38123       ],
38124       [
38125         "Cameroon (Cameroun)",
38126         "cm",
38127         "237"
38128       ],
38129       [
38130         "Canada",
38131         "ca",
38132         "1",
38133         1,
38134         ["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"]
38135       ],
38136       [
38137         "Cape Verde (Kabu Verdi)",
38138         "cv",
38139         "238"
38140       ],
38141       [
38142         "Caribbean Netherlands",
38143         "bq",
38144         "599",
38145         1
38146       ],
38147       [
38148         "Cayman Islands",
38149         "ky",
38150         "1345"
38151       ],
38152       [
38153         "Central African Republic (République centrafricaine)",
38154         "cf",
38155         "236"
38156       ],
38157       [
38158         "Chad (Tchad)",
38159         "td",
38160         "235"
38161       ],
38162       [
38163         "Chile",
38164         "cl",
38165         "56"
38166       ],
38167       [
38168         "China (中国)",
38169         "cn",
38170         "86"
38171       ],
38172       [
38173         "Christmas Island",
38174         "cx",
38175         "61",
38176         2
38177       ],
38178       [
38179         "Cocos (Keeling) Islands",
38180         "cc",
38181         "61",
38182         1
38183       ],
38184       [
38185         "Colombia",
38186         "co",
38187         "57"
38188       ],
38189       [
38190         "Comoros (‫جزر القمر‬‎)",
38191         "km",
38192         "269"
38193       ],
38194       [
38195         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38196         "cd",
38197         "243"
38198       ],
38199       [
38200         "Congo (Republic) (Congo-Brazzaville)",
38201         "cg",
38202         "242"
38203       ],
38204       [
38205         "Cook Islands",
38206         "ck",
38207         "682"
38208       ],
38209       [
38210         "Costa Rica",
38211         "cr",
38212         "506"
38213       ],
38214       [
38215         "Côte d’Ivoire",
38216         "ci",
38217         "225"
38218       ],
38219       [
38220         "Croatia (Hrvatska)",
38221         "hr",
38222         "385"
38223       ],
38224       [
38225         "Cuba",
38226         "cu",
38227         "53"
38228       ],
38229       [
38230         "Curaçao",
38231         "cw",
38232         "599",
38233         0
38234       ],
38235       [
38236         "Cyprus (Κύπρος)",
38237         "cy",
38238         "357"
38239       ],
38240       [
38241         "Czech Republic (Česká republika)",
38242         "cz",
38243         "420"
38244       ],
38245       [
38246         "Denmark (Danmark)",
38247         "dk",
38248         "45"
38249       ],
38250       [
38251         "Djibouti",
38252         "dj",
38253         "253"
38254       ],
38255       [
38256         "Dominica",
38257         "dm",
38258         "1767"
38259       ],
38260       [
38261         "Dominican Republic (República Dominicana)",
38262         "do",
38263         "1",
38264         2,
38265         ["809", "829", "849"]
38266       ],
38267       [
38268         "Ecuador",
38269         "ec",
38270         "593"
38271       ],
38272       [
38273         "Egypt (‫مصر‬‎)",
38274         "eg",
38275         "20"
38276       ],
38277       [
38278         "El Salvador",
38279         "sv",
38280         "503"
38281       ],
38282       [
38283         "Equatorial Guinea (Guinea Ecuatorial)",
38284         "gq",
38285         "240"
38286       ],
38287       [
38288         "Eritrea",
38289         "er",
38290         "291"
38291       ],
38292       [
38293         "Estonia (Eesti)",
38294         "ee",
38295         "372"
38296       ],
38297       [
38298         "Ethiopia",
38299         "et",
38300         "251"
38301       ],
38302       [
38303         "Falkland Islands (Islas Malvinas)",
38304         "fk",
38305         "500"
38306       ],
38307       [
38308         "Faroe Islands (Føroyar)",
38309         "fo",
38310         "298"
38311       ],
38312       [
38313         "Fiji",
38314         "fj",
38315         "679"
38316       ],
38317       [
38318         "Finland (Suomi)",
38319         "fi",
38320         "358",
38321         0
38322       ],
38323       [
38324         "France",
38325         "fr",
38326         "33"
38327       ],
38328       [
38329         "French Guiana (Guyane française)",
38330         "gf",
38331         "594"
38332       ],
38333       [
38334         "French Polynesia (Polynésie française)",
38335         "pf",
38336         "689"
38337       ],
38338       [
38339         "Gabon",
38340         "ga",
38341         "241"
38342       ],
38343       [
38344         "Gambia",
38345         "gm",
38346         "220"
38347       ],
38348       [
38349         "Georgia (საქართველო)",
38350         "ge",
38351         "995"
38352       ],
38353       [
38354         "Germany (Deutschland)",
38355         "de",
38356         "49"
38357       ],
38358       [
38359         "Ghana (Gaana)",
38360         "gh",
38361         "233"
38362       ],
38363       [
38364         "Gibraltar",
38365         "gi",
38366         "350"
38367       ],
38368       [
38369         "Greece (Ελλάδα)",
38370         "gr",
38371         "30"
38372       ],
38373       [
38374         "Greenland (Kalaallit Nunaat)",
38375         "gl",
38376         "299"
38377       ],
38378       [
38379         "Grenada",
38380         "gd",
38381         "1473"
38382       ],
38383       [
38384         "Guadeloupe",
38385         "gp",
38386         "590",
38387         0
38388       ],
38389       [
38390         "Guam",
38391         "gu",
38392         "1671"
38393       ],
38394       [
38395         "Guatemala",
38396         "gt",
38397         "502"
38398       ],
38399       [
38400         "Guernsey",
38401         "gg",
38402         "44",
38403         1
38404       ],
38405       [
38406         "Guinea (Guinée)",
38407         "gn",
38408         "224"
38409       ],
38410       [
38411         "Guinea-Bissau (Guiné Bissau)",
38412         "gw",
38413         "245"
38414       ],
38415       [
38416         "Guyana",
38417         "gy",
38418         "592"
38419       ],
38420       [
38421         "Haiti",
38422         "ht",
38423         "509"
38424       ],
38425       [
38426         "Honduras",
38427         "hn",
38428         "504"
38429       ],
38430       [
38431         "Hong Kong (香港)",
38432         "hk",
38433         "852"
38434       ],
38435       [
38436         "Hungary (Magyarország)",
38437         "hu",
38438         "36"
38439       ],
38440       [
38441         "Iceland (Ísland)",
38442         "is",
38443         "354"
38444       ],
38445       [
38446         "India (भारत)",
38447         "in",
38448         "91"
38449       ],
38450       [
38451         "Indonesia",
38452         "id",
38453         "62"
38454       ],
38455       [
38456         "Iran (‫ایران‬‎)",
38457         "ir",
38458         "98"
38459       ],
38460       [
38461         "Iraq (‫العراق‬‎)",
38462         "iq",
38463         "964"
38464       ],
38465       [
38466         "Ireland",
38467         "ie",
38468         "353"
38469       ],
38470       [
38471         "Isle of Man",
38472         "im",
38473         "44",
38474         2
38475       ],
38476       [
38477         "Israel (‫ישראל‬‎)",
38478         "il",
38479         "972"
38480       ],
38481       [
38482         "Italy (Italia)",
38483         "it",
38484         "39",
38485         0
38486       ],
38487       [
38488         "Jamaica",
38489         "jm",
38490         "1876"
38491       ],
38492       [
38493         "Japan (日本)",
38494         "jp",
38495         "81"
38496       ],
38497       [
38498         "Jersey",
38499         "je",
38500         "44",
38501         3
38502       ],
38503       [
38504         "Jordan (‫الأردن‬‎)",
38505         "jo",
38506         "962"
38507       ],
38508       [
38509         "Kazakhstan (Казахстан)",
38510         "kz",
38511         "7",
38512         1
38513       ],
38514       [
38515         "Kenya",
38516         "ke",
38517         "254"
38518       ],
38519       [
38520         "Kiribati",
38521         "ki",
38522         "686"
38523       ],
38524       [
38525         "Kosovo",
38526         "xk",
38527         "383"
38528       ],
38529       [
38530         "Kuwait (‫الكويت‬‎)",
38531         "kw",
38532         "965"
38533       ],
38534       [
38535         "Kyrgyzstan (Кыргызстан)",
38536         "kg",
38537         "996"
38538       ],
38539       [
38540         "Laos (ລາວ)",
38541         "la",
38542         "856"
38543       ],
38544       [
38545         "Latvia (Latvija)",
38546         "lv",
38547         "371"
38548       ],
38549       [
38550         "Lebanon (‫لبنان‬‎)",
38551         "lb",
38552         "961"
38553       ],
38554       [
38555         "Lesotho",
38556         "ls",
38557         "266"
38558       ],
38559       [
38560         "Liberia",
38561         "lr",
38562         "231"
38563       ],
38564       [
38565         "Libya (‫ليبيا‬‎)",
38566         "ly",
38567         "218"
38568       ],
38569       [
38570         "Liechtenstein",
38571         "li",
38572         "423"
38573       ],
38574       [
38575         "Lithuania (Lietuva)",
38576         "lt",
38577         "370"
38578       ],
38579       [
38580         "Luxembourg",
38581         "lu",
38582         "352"
38583       ],
38584       [
38585         "Macau (澳門)",
38586         "mo",
38587         "853"
38588       ],
38589       [
38590         "Macedonia (FYROM) (Македонија)",
38591         "mk",
38592         "389"
38593       ],
38594       [
38595         "Madagascar (Madagasikara)",
38596         "mg",
38597         "261"
38598       ],
38599       [
38600         "Malawi",
38601         "mw",
38602         "265"
38603       ],
38604       [
38605         "Malaysia",
38606         "my",
38607         "60"
38608       ],
38609       [
38610         "Maldives",
38611         "mv",
38612         "960"
38613       ],
38614       [
38615         "Mali",
38616         "ml",
38617         "223"
38618       ],
38619       [
38620         "Malta",
38621         "mt",
38622         "356"
38623       ],
38624       [
38625         "Marshall Islands",
38626         "mh",
38627         "692"
38628       ],
38629       [
38630         "Martinique",
38631         "mq",
38632         "596"
38633       ],
38634       [
38635         "Mauritania (‫موريتانيا‬‎)",
38636         "mr",
38637         "222"
38638       ],
38639       [
38640         "Mauritius (Moris)",
38641         "mu",
38642         "230"
38643       ],
38644       [
38645         "Mayotte",
38646         "yt",
38647         "262",
38648         1
38649       ],
38650       [
38651         "Mexico (México)",
38652         "mx",
38653         "52"
38654       ],
38655       [
38656         "Micronesia",
38657         "fm",
38658         "691"
38659       ],
38660       [
38661         "Moldova (Republica Moldova)",
38662         "md",
38663         "373"
38664       ],
38665       [
38666         "Monaco",
38667         "mc",
38668         "377"
38669       ],
38670       [
38671         "Mongolia (Монгол)",
38672         "mn",
38673         "976"
38674       ],
38675       [
38676         "Montenegro (Crna Gora)",
38677         "me",
38678         "382"
38679       ],
38680       [
38681         "Montserrat",
38682         "ms",
38683         "1664"
38684       ],
38685       [
38686         "Morocco (‫المغرب‬‎)",
38687         "ma",
38688         "212",
38689         0
38690       ],
38691       [
38692         "Mozambique (Moçambique)",
38693         "mz",
38694         "258"
38695       ],
38696       [
38697         "Myanmar (Burma) (မြန်မာ)",
38698         "mm",
38699         "95"
38700       ],
38701       [
38702         "Namibia (Namibië)",
38703         "na",
38704         "264"
38705       ],
38706       [
38707         "Nauru",
38708         "nr",
38709         "674"
38710       ],
38711       [
38712         "Nepal (नेपाल)",
38713         "np",
38714         "977"
38715       ],
38716       [
38717         "Netherlands (Nederland)",
38718         "nl",
38719         "31"
38720       ],
38721       [
38722         "New Caledonia (Nouvelle-Calédonie)",
38723         "nc",
38724         "687"
38725       ],
38726       [
38727         "New Zealand",
38728         "nz",
38729         "64"
38730       ],
38731       [
38732         "Nicaragua",
38733         "ni",
38734         "505"
38735       ],
38736       [
38737         "Niger (Nijar)",
38738         "ne",
38739         "227"
38740       ],
38741       [
38742         "Nigeria",
38743         "ng",
38744         "234"
38745       ],
38746       [
38747         "Niue",
38748         "nu",
38749         "683"
38750       ],
38751       [
38752         "Norfolk Island",
38753         "nf",
38754         "672"
38755       ],
38756       [
38757         "North Korea (조선 민주주의 인민 공화국)",
38758         "kp",
38759         "850"
38760       ],
38761       [
38762         "Northern Mariana Islands",
38763         "mp",
38764         "1670"
38765       ],
38766       [
38767         "Norway (Norge)",
38768         "no",
38769         "47",
38770         0
38771       ],
38772       [
38773         "Oman (‫عُمان‬‎)",
38774         "om",
38775         "968"
38776       ],
38777       [
38778         "Pakistan (‫پاکستان‬‎)",
38779         "pk",
38780         "92"
38781       ],
38782       [
38783         "Palau",
38784         "pw",
38785         "680"
38786       ],
38787       [
38788         "Palestine (‫فلسطين‬‎)",
38789         "ps",
38790         "970"
38791       ],
38792       [
38793         "Panama (Panamá)",
38794         "pa",
38795         "507"
38796       ],
38797       [
38798         "Papua New Guinea",
38799         "pg",
38800         "675"
38801       ],
38802       [
38803         "Paraguay",
38804         "py",
38805         "595"
38806       ],
38807       [
38808         "Peru (Perú)",
38809         "pe",
38810         "51"
38811       ],
38812       [
38813         "Philippines",
38814         "ph",
38815         "63"
38816       ],
38817       [
38818         "Poland (Polska)",
38819         "pl",
38820         "48"
38821       ],
38822       [
38823         "Portugal",
38824         "pt",
38825         "351"
38826       ],
38827       [
38828         "Puerto Rico",
38829         "pr",
38830         "1",
38831         3,
38832         ["787", "939"]
38833       ],
38834       [
38835         "Qatar (‫قطر‬‎)",
38836         "qa",
38837         "974"
38838       ],
38839       [
38840         "Réunion (La Réunion)",
38841         "re",
38842         "262",
38843         0
38844       ],
38845       [
38846         "Romania (România)",
38847         "ro",
38848         "40"
38849       ],
38850       [
38851         "Russia (Россия)",
38852         "ru",
38853         "7",
38854         0
38855       ],
38856       [
38857         "Rwanda",
38858         "rw",
38859         "250"
38860       ],
38861       [
38862         "Saint Barthélemy",
38863         "bl",
38864         "590",
38865         1
38866       ],
38867       [
38868         "Saint Helena",
38869         "sh",
38870         "290"
38871       ],
38872       [
38873         "Saint Kitts and Nevis",
38874         "kn",
38875         "1869"
38876       ],
38877       [
38878         "Saint Lucia",
38879         "lc",
38880         "1758"
38881       ],
38882       [
38883         "Saint Martin (Saint-Martin (partie française))",
38884         "mf",
38885         "590",
38886         2
38887       ],
38888       [
38889         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38890         "pm",
38891         "508"
38892       ],
38893       [
38894         "Saint Vincent and the Grenadines",
38895         "vc",
38896         "1784"
38897       ],
38898       [
38899         "Samoa",
38900         "ws",
38901         "685"
38902       ],
38903       [
38904         "San Marino",
38905         "sm",
38906         "378"
38907       ],
38908       [
38909         "São Tomé and Príncipe (São Tomé e Príncipe)",
38910         "st",
38911         "239"
38912       ],
38913       [
38914         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
38915         "sa",
38916         "966"
38917       ],
38918       [
38919         "Senegal (Sénégal)",
38920         "sn",
38921         "221"
38922       ],
38923       [
38924         "Serbia (Србија)",
38925         "rs",
38926         "381"
38927       ],
38928       [
38929         "Seychelles",
38930         "sc",
38931         "248"
38932       ],
38933       [
38934         "Sierra Leone",
38935         "sl",
38936         "232"
38937       ],
38938       [
38939         "Singapore",
38940         "sg",
38941         "65"
38942       ],
38943       [
38944         "Sint Maarten",
38945         "sx",
38946         "1721"
38947       ],
38948       [
38949         "Slovakia (Slovensko)",
38950         "sk",
38951         "421"
38952       ],
38953       [
38954         "Slovenia (Slovenija)",
38955         "si",
38956         "386"
38957       ],
38958       [
38959         "Solomon Islands",
38960         "sb",
38961         "677"
38962       ],
38963       [
38964         "Somalia (Soomaaliya)",
38965         "so",
38966         "252"
38967       ],
38968       [
38969         "South Africa",
38970         "za",
38971         "27"
38972       ],
38973       [
38974         "South Korea (대한민국)",
38975         "kr",
38976         "82"
38977       ],
38978       [
38979         "South Sudan (‫جنوب السودان‬‎)",
38980         "ss",
38981         "211"
38982       ],
38983       [
38984         "Spain (España)",
38985         "es",
38986         "34"
38987       ],
38988       [
38989         "Sri Lanka (ශ්‍රී ලංකාව)",
38990         "lk",
38991         "94"
38992       ],
38993       [
38994         "Sudan (‫السودان‬‎)",
38995         "sd",
38996         "249"
38997       ],
38998       [
38999         "Suriname",
39000         "sr",
39001         "597"
39002       ],
39003       [
39004         "Svalbard and Jan Mayen",
39005         "sj",
39006         "47",
39007         1
39008       ],
39009       [
39010         "Swaziland",
39011         "sz",
39012         "268"
39013       ],
39014       [
39015         "Sweden (Sverige)",
39016         "se",
39017         "46"
39018       ],
39019       [
39020         "Switzerland (Schweiz)",
39021         "ch",
39022         "41"
39023       ],
39024       [
39025         "Syria (‫سوريا‬‎)",
39026         "sy",
39027         "963"
39028       ],
39029       [
39030         "Taiwan (台灣)",
39031         "tw",
39032         "886"
39033       ],
39034       [
39035         "Tajikistan",
39036         "tj",
39037         "992"
39038       ],
39039       [
39040         "Tanzania",
39041         "tz",
39042         "255"
39043       ],
39044       [
39045         "Thailand (ไทย)",
39046         "th",
39047         "66"
39048       ],
39049       [
39050         "Timor-Leste",
39051         "tl",
39052         "670"
39053       ],
39054       [
39055         "Togo",
39056         "tg",
39057         "228"
39058       ],
39059       [
39060         "Tokelau",
39061         "tk",
39062         "690"
39063       ],
39064       [
39065         "Tonga",
39066         "to",
39067         "676"
39068       ],
39069       [
39070         "Trinidad and Tobago",
39071         "tt",
39072         "1868"
39073       ],
39074       [
39075         "Tunisia (‫تونس‬‎)",
39076         "tn",
39077         "216"
39078       ],
39079       [
39080         "Turkey (Türkiye)",
39081         "tr",
39082         "90"
39083       ],
39084       [
39085         "Turkmenistan",
39086         "tm",
39087         "993"
39088       ],
39089       [
39090         "Turks and Caicos Islands",
39091         "tc",
39092         "1649"
39093       ],
39094       [
39095         "Tuvalu",
39096         "tv",
39097         "688"
39098       ],
39099       [
39100         "U.S. Virgin Islands",
39101         "vi",
39102         "1340"
39103       ],
39104       [
39105         "Uganda",
39106         "ug",
39107         "256"
39108       ],
39109       [
39110         "Ukraine (Україна)",
39111         "ua",
39112         "380"
39113       ],
39114       [
39115         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39116         "ae",
39117         "971"
39118       ],
39119       [
39120         "United Kingdom",
39121         "gb",
39122         "44",
39123         0
39124       ],
39125       [
39126         "United States",
39127         "us",
39128         "1",
39129         0
39130       ],
39131       [
39132         "Uruguay",
39133         "uy",
39134         "598"
39135       ],
39136       [
39137         "Uzbekistan (Oʻzbekiston)",
39138         "uz",
39139         "998"
39140       ],
39141       [
39142         "Vanuatu",
39143         "vu",
39144         "678"
39145       ],
39146       [
39147         "Vatican City (Città del Vaticano)",
39148         "va",
39149         "39",
39150         1
39151       ],
39152       [
39153         "Venezuela",
39154         "ve",
39155         "58"
39156       ],
39157       [
39158         "Vietnam (Việt Nam)",
39159         "vn",
39160         "84"
39161       ],
39162       [
39163         "Wallis and Futuna (Wallis-et-Futuna)",
39164         "wf",
39165         "681"
39166       ],
39167       [
39168         "Western Sahara (‫الصحراء الغربية‬‎)",
39169         "eh",
39170         "212",
39171         1
39172       ],
39173       [
39174         "Yemen (‫اليمن‬‎)",
39175         "ye",
39176         "967"
39177       ],
39178       [
39179         "Zambia",
39180         "zm",
39181         "260"
39182       ],
39183       [
39184         "Zimbabwe",
39185         "zw",
39186         "263"
39187       ],
39188       [
39189         "Åland Islands",
39190         "ax",
39191         "358",
39192         1
39193       ]
39194   ];
39195   
39196   return d;
39197 }/**
39198 *    This script refer to:
39199 *    Title: International Telephone Input
39200 *    Author: Jack O'Connor
39201 *    Code version:  v12.1.12
39202 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39203 **/
39204
39205 /**
39206  * @class Roo.bootstrap.PhoneInput
39207  * @extends Roo.bootstrap.TriggerField
39208  * An input with International dial-code selection
39209  
39210  * @cfg {String} defaultDialCode default '+852'
39211  * @cfg {Array} preferedCountries default []
39212   
39213  * @constructor
39214  * Create a new PhoneInput.
39215  * @param {Object} config Configuration options
39216  */
39217
39218 Roo.bootstrap.PhoneInput = function(config) {
39219     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39220 };
39221
39222 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39223         
39224         listWidth: undefined,
39225         
39226         selectedClass: 'active',
39227         
39228         invalidClass : "has-warning",
39229         
39230         validClass: 'has-success',
39231         
39232         allowed: '0123456789',
39233         
39234         /**
39235          * @cfg {String} defaultDialCode The default dial code when initializing the input
39236          */
39237         defaultDialCode: '+852',
39238         
39239         /**
39240          * @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
39241          */
39242         preferedCountries: false,
39243         
39244         getAutoCreate : function()
39245         {
39246             var data = Roo.bootstrap.PhoneInputData();
39247             var align = this.labelAlign || this.parentLabelAlign();
39248             var id = Roo.id();
39249             
39250             this.allCountries = [];
39251             this.dialCodeMapping = [];
39252             
39253             for (var i = 0; i < data.length; i++) {
39254               var c = data[i];
39255               this.allCountries[i] = {
39256                 name: c[0],
39257                 iso2: c[1],
39258                 dialCode: c[2],
39259                 priority: c[3] || 0,
39260                 areaCodes: c[4] || null
39261               };
39262               this.dialCodeMapping[c[2]] = {
39263                   name: c[0],
39264                   iso2: c[1],
39265                   priority: c[3] || 0,
39266                   areaCodes: c[4] || null
39267               };
39268             }
39269             
39270             var cfg = {
39271                 cls: 'form-group',
39272                 cn: []
39273             };
39274             
39275             var input =  {
39276                 tag: 'input',
39277                 id : id,
39278                 cls : 'form-control tel-input',
39279                 autocomplete: 'new-password'
39280             };
39281             
39282             var hiddenInput = {
39283                 tag: 'input',
39284                 type: 'hidden',
39285                 cls: 'hidden-tel-input'
39286             };
39287             
39288             if (this.name) {
39289                 hiddenInput.name = this.name;
39290             }
39291             
39292             if (this.disabled) {
39293                 input.disabled = true;
39294             }
39295             
39296             var flag_container = {
39297                 tag: 'div',
39298                 cls: 'flag-box',
39299                 cn: [
39300                     {
39301                         tag: 'div',
39302                         cls: 'flag'
39303                     },
39304                     {
39305                         tag: 'div',
39306                         cls: 'caret'
39307                     }
39308                 ]
39309             };
39310             
39311             var box = {
39312                 tag: 'div',
39313                 cls: this.hasFeedback ? 'has-feedback' : '',
39314                 cn: [
39315                     hiddenInput,
39316                     input,
39317                     {
39318                         tag: 'input',
39319                         cls: 'dial-code-holder',
39320                         disabled: true
39321                     }
39322                 ]
39323             };
39324             
39325             var container = {
39326                 cls: 'roo-select2-container input-group',
39327                 cn: [
39328                     flag_container,
39329                     box
39330                 ]
39331             };
39332             
39333             if (this.fieldLabel.length) {
39334                 var indicator = {
39335                     tag: 'i',
39336                     tooltip: 'This field is required'
39337                 };
39338                 
39339                 var label = {
39340                     tag: 'label',
39341                     'for':  id,
39342                     cls: 'control-label',
39343                     cn: []
39344                 };
39345                 
39346                 var label_text = {
39347                     tag: 'span',
39348                     html: this.fieldLabel
39349                 };
39350                 
39351                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39352                 label.cn = [
39353                     indicator,
39354                     label_text
39355                 ];
39356                 
39357                 if(this.indicatorpos == 'right') {
39358                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39359                     label.cn = [
39360                         label_text,
39361                         indicator
39362                     ];
39363                 }
39364                 
39365                 if(align == 'left') {
39366                     container = {
39367                         tag: 'div',
39368                         cn: [
39369                             container
39370                         ]
39371                     };
39372                     
39373                     if(this.labelWidth > 12){
39374                         label.style = "width: " + this.labelWidth + 'px';
39375                     }
39376                     if(this.labelWidth < 13 && this.labelmd == 0){
39377                         this.labelmd = this.labelWidth;
39378                     }
39379                     if(this.labellg > 0){
39380                         label.cls += ' col-lg-' + this.labellg;
39381                         input.cls += ' col-lg-' + (12 - this.labellg);
39382                     }
39383                     if(this.labelmd > 0){
39384                         label.cls += ' col-md-' + this.labelmd;
39385                         container.cls += ' col-md-' + (12 - this.labelmd);
39386                     }
39387                     if(this.labelsm > 0){
39388                         label.cls += ' col-sm-' + this.labelsm;
39389                         container.cls += ' col-sm-' + (12 - this.labelsm);
39390                     }
39391                     if(this.labelxs > 0){
39392                         label.cls += ' col-xs-' + this.labelxs;
39393                         container.cls += ' col-xs-' + (12 - this.labelxs);
39394                     }
39395                 }
39396             }
39397             
39398             cfg.cn = [
39399                 label,
39400                 container
39401             ];
39402             
39403             var settings = this;
39404             
39405             ['xs','sm','md','lg'].map(function(size){
39406                 if (settings[size]) {
39407                     cfg.cls += ' col-' + size + '-' + settings[size];
39408                 }
39409             });
39410             
39411             this.store = new Roo.data.Store({
39412                 proxy : new Roo.data.MemoryProxy({}),
39413                 reader : new Roo.data.JsonReader({
39414                     fields : [
39415                         {
39416                             'name' : 'name',
39417                             'type' : 'string'
39418                         },
39419                         {
39420                             'name' : 'iso2',
39421                             'type' : 'string'
39422                         },
39423                         {
39424                             'name' : 'dialCode',
39425                             'type' : 'string'
39426                         },
39427                         {
39428                             'name' : 'priority',
39429                             'type' : 'string'
39430                         },
39431                         {
39432                             'name' : 'areaCodes',
39433                             'type' : 'string'
39434                         }
39435                     ]
39436                 })
39437             });
39438             
39439             if(!this.preferedCountries) {
39440                 this.preferedCountries = [
39441                     'hk',
39442                     'gb',
39443                     'us'
39444                 ];
39445             }
39446             
39447             var p = this.preferedCountries.reverse();
39448             
39449             if(p) {
39450                 for (var i = 0; i < p.length; i++) {
39451                     for (var j = 0; j < this.allCountries.length; j++) {
39452                         if(this.allCountries[j].iso2 == p[i]) {
39453                             var t = this.allCountries[j];
39454                             this.allCountries.splice(j,1);
39455                             this.allCountries.unshift(t);
39456                         }
39457                     } 
39458                 }
39459             }
39460             
39461             this.store.proxy.data = {
39462                 success: true,
39463                 data: this.allCountries
39464             };
39465             
39466             return cfg;
39467         },
39468         
39469         initEvents : function()
39470         {
39471             this.createList();
39472             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39473             
39474             this.indicator = this.indicatorEl();
39475             this.flag = this.flagEl();
39476             this.dialCodeHolder = this.dialCodeHolderEl();
39477             
39478             this.trigger = this.el.select('div.flag-box',true).first();
39479             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39480             
39481             var _this = this;
39482             
39483             (function(){
39484                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39485                 _this.list.setWidth(lw);
39486             }).defer(100);
39487             
39488             this.list.on('mouseover', this.onViewOver, this);
39489             this.list.on('mousemove', this.onViewMove, this);
39490             this.inputEl().on("keypress", this.onKeyPress, this);
39491             
39492             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39493
39494             this.view = new Roo.View(this.list, this.tpl, {
39495                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39496             });
39497             
39498             this.view.on('click', this.onViewClick, this);
39499             this.setValue(this.defaultDialCode);
39500         },
39501         
39502         onTriggerClick : function(e)
39503         {
39504             Roo.log('trigger click');
39505             if(this.disabled){
39506                 return;
39507             }
39508             
39509             if(this.isExpanded()){
39510                 this.collapse();
39511                 this.hasFocus = false;
39512             }else {
39513                 this.store.load({});
39514                 this.hasFocus = true;
39515                 this.expand();
39516             }
39517         },
39518         
39519         isExpanded : function()
39520         {
39521             return this.list.isVisible();
39522         },
39523         
39524         collapse : function()
39525         {
39526             if(!this.isExpanded()){
39527                 return;
39528             }
39529             this.list.hide();
39530             Roo.get(document).un('mousedown', this.collapseIf, this);
39531             Roo.get(document).un('mousewheel', this.collapseIf, this);
39532             this.fireEvent('collapse', this);
39533             this.validate();
39534         },
39535         
39536         expand : function()
39537         {
39538             Roo.log('expand');
39539
39540             if(this.isExpanded() || !this.hasFocus){
39541                 return;
39542             }
39543             
39544             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39545             this.list.setWidth(lw);
39546             
39547             this.list.show();
39548             this.restrictHeight();
39549             
39550             Roo.get(document).on('mousedown', this.collapseIf, this);
39551             Roo.get(document).on('mousewheel', this.collapseIf, this);
39552             
39553             this.fireEvent('expand', this);
39554         },
39555         
39556         restrictHeight : function()
39557         {
39558             this.list.alignTo(this.inputEl(), this.listAlign);
39559             this.list.alignTo(this.inputEl(), this.listAlign);
39560         },
39561         
39562         onViewOver : function(e, t)
39563         {
39564             if(this.inKeyMode){
39565                 return;
39566             }
39567             var item = this.view.findItemFromChild(t);
39568             
39569             if(item){
39570                 var index = this.view.indexOf(item);
39571                 this.select(index, false);
39572             }
39573         },
39574
39575         // private
39576         onViewClick : function(view, doFocus, el, e)
39577         {
39578             var index = this.view.getSelectedIndexes()[0];
39579             
39580             var r = this.store.getAt(index);
39581             
39582             if(r){
39583                 this.onSelect(r, index);
39584             }
39585             if(doFocus !== false && !this.blockFocus){
39586                 this.inputEl().focus();
39587             }
39588         },
39589         
39590         onViewMove : function(e, t)
39591         {
39592             this.inKeyMode = false;
39593         },
39594         
39595         select : function(index, scrollIntoView)
39596         {
39597             this.selectedIndex = index;
39598             this.view.select(index);
39599             if(scrollIntoView !== false){
39600                 var el = this.view.getNode(index);
39601                 if(el){
39602                     this.list.scrollChildIntoView(el, false);
39603                 }
39604             }
39605         },
39606         
39607         createList : function()
39608         {
39609             this.list = Roo.get(document.body).createChild({
39610                 tag: 'ul',
39611                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39612                 style: 'display:none'
39613             });
39614             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39615         },
39616         
39617         collapseIf : function(e)
39618         {
39619             var in_combo  = e.within(this.el);
39620             var in_list =  e.within(this.list);
39621             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39622             
39623             if (in_combo || in_list || is_list) {
39624                 return;
39625             }
39626             this.collapse();
39627         },
39628         
39629         onSelect : function(record, index)
39630         {
39631             if(this.fireEvent('beforeselect', this, record, index) !== false){
39632                 
39633                 this.setFlagClass(record.data.iso2);
39634                 this.setDialCode(record.data.dialCode);
39635                 this.hasFocus = false;
39636                 this.collapse();
39637                 this.fireEvent('select', this, record, index);
39638             }
39639         },
39640         
39641         flagEl : function()
39642         {
39643             var flag = this.el.select('div.flag',true).first();
39644             if(!flag){
39645                 return false;
39646             }
39647             return flag;
39648         },
39649         
39650         dialCodeHolderEl : function()
39651         {
39652             var d = this.el.select('input.dial-code-holder',true).first();
39653             if(!d){
39654                 return false;
39655             }
39656             return d;
39657         },
39658         
39659         setDialCode : function(v)
39660         {
39661             this.dialCodeHolder.dom.value = '+'+v;
39662         },
39663         
39664         setFlagClass : function(n)
39665         {
39666             this.flag.dom.className = 'flag '+n;
39667         },
39668         
39669         getValue : function()
39670         {
39671             var v = this.inputEl().getValue();
39672             if(this.dialCodeHolder) {
39673                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39674             }
39675             return v;
39676         },
39677         
39678         setValue : function(v)
39679         {
39680             var d = this.getDialCode(v);
39681             
39682             //invalid dial code
39683             if(v.length == 0 || !d || d.length == 0) {
39684                 if(this.rendered){
39685                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39686                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39687                 }
39688                 return;
39689             }
39690             
39691             //valid dial code
39692             this.setFlagClass(this.dialCodeMapping[d].iso2);
39693             this.setDialCode(d);
39694             this.inputEl().dom.value = v.replace('+'+d,'');
39695             this.hiddenEl().dom.value = this.getValue();
39696             
39697             this.validate();
39698         },
39699         
39700         getDialCode : function(v = '')
39701         {
39702             if (v.length == 0) {
39703                 return this.dialCodeHolder.dom.value;
39704             }
39705             
39706             var dialCode = "";
39707             if (v.charAt(0) != "+") {
39708                 return false;
39709             }
39710             var numericChars = "";
39711             for (var i = 1; i < v.length; i++) {
39712               var c = v.charAt(i);
39713               if (!isNaN(c)) {
39714                 numericChars += c;
39715                 if (this.dialCodeMapping[numericChars]) {
39716                   dialCode = v.substr(1, i);
39717                 }
39718                 if (numericChars.length == 4) {
39719                   break;
39720                 }
39721               }
39722             }
39723             return dialCode;
39724         },
39725         
39726         reset : function()
39727         {
39728             this.setValue(this.defaultDialCode);
39729             this.validate();
39730         },
39731         
39732         hiddenEl : function()
39733         {
39734             return this.el.select('input.hidden-tel-input',true).first();
39735         },
39736         
39737         onKeyPress : function(e){
39738             
39739             var k = e.getKey();
39740             var c = e.getCharCode();
39741             
39742             if(
39743                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39744                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39745             ){
39746                 e.stopEvent();
39747                 return;
39748             }
39749             
39750             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39751                 return;
39752             }
39753             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39754                 e.stopEvent();
39755             }
39756             
39757             this.setValue(this.getValue());
39758         }
39759         
39760 });