roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
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             if(f.validate()){
7679                 return;
7680             }
7681             valid = false;
7682
7683             if(!target && f.el.isVisible(true)){
7684                 target = f;
7685             }
7686            
7687         });
7688         
7689         if(this.errorMask && !valid){
7690             Roo.bootstrap.Form.popover.mask(this, target);
7691         }
7692         
7693         return valid;
7694     },
7695     
7696     /**
7697      * Returns true if any fields in this form have changed since their original load.
7698      * @return Boolean
7699      */
7700     isDirty : function(){
7701         var dirty = false;
7702         var items = this.getItems();
7703         items.each(function(f){
7704            if(f.isDirty()){
7705                dirty = true;
7706                return false;
7707            }
7708            return true;
7709         });
7710         return dirty;
7711     },
7712      /**
7713      * Performs a predefined action (submit or load) or custom actions you define on this form.
7714      * @param {String} actionName The name of the action type
7715      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7716      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7717      * accept other config options):
7718      * <pre>
7719 Property          Type             Description
7720 ----------------  ---------------  ----------------------------------------------------------------------------------
7721 url               String           The url for the action (defaults to the form's url)
7722 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7723 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7724 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7725                                    validate the form on the client (defaults to false)
7726      * </pre>
7727      * @return {BasicForm} this
7728      */
7729     doAction : function(action, options){
7730         if(typeof action == 'string'){
7731             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7732         }
7733         if(this.fireEvent('beforeaction', this, action) !== false){
7734             this.beforeAction(action);
7735             action.run.defer(100, action);
7736         }
7737         return this;
7738     },
7739
7740     // private
7741     beforeAction : function(action){
7742         var o = action.options;
7743
7744         if(this.loadMask){
7745             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7746         }
7747         // not really supported yet.. ??
7748
7749         //if(this.waitMsgTarget === true){
7750         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7751         //}else if(this.waitMsgTarget){
7752         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7753         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7754         //}else {
7755         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7756        // }
7757
7758     },
7759
7760     // private
7761     afterAction : function(action, success){
7762         this.activeAction = null;
7763         var o = action.options;
7764
7765         //if(this.waitMsgTarget === true){
7766             this.el.unmask();
7767         //}else if(this.waitMsgTarget){
7768         //    this.waitMsgTarget.unmask();
7769         //}else{
7770         //    Roo.MessageBox.updateProgress(1);
7771         //    Roo.MessageBox.hide();
7772        // }
7773         //
7774         if(success){
7775             if(o.reset){
7776                 this.reset();
7777             }
7778             Roo.callback(o.success, o.scope, [this, action]);
7779             this.fireEvent('actioncomplete', this, action);
7780
7781         }else{
7782
7783             // failure condition..
7784             // we have a scenario where updates need confirming.
7785             // eg. if a locking scenario exists..
7786             // we look for { errors : { needs_confirm : true }} in the response.
7787             if (
7788                 (typeof(action.result) != 'undefined')  &&
7789                 (typeof(action.result.errors) != 'undefined')  &&
7790                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7791            ){
7792                 var _t = this;
7793                 Roo.log("not supported yet");
7794                  /*
7795
7796                 Roo.MessageBox.confirm(
7797                     "Change requires confirmation",
7798                     action.result.errorMsg,
7799                     function(r) {
7800                         if (r != 'yes') {
7801                             return;
7802                         }
7803                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7804                     }
7805
7806                 );
7807                 */
7808
7809
7810                 return;
7811             }
7812
7813             Roo.callback(o.failure, o.scope, [this, action]);
7814             // show an error message if no failed handler is set..
7815             if (!this.hasListener('actionfailed')) {
7816                 Roo.log("need to add dialog support");
7817                 /*
7818                 Roo.MessageBox.alert("Error",
7819                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7820                         action.result.errorMsg :
7821                         "Saving Failed, please check your entries or try again"
7822                 );
7823                 */
7824             }
7825
7826             this.fireEvent('actionfailed', this, action);
7827         }
7828
7829     },
7830     /**
7831      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7832      * @param {String} id The value to search for
7833      * @return Field
7834      */
7835     findField : function(id){
7836         var items = this.getItems();
7837         var field = items.get(id);
7838         if(!field){
7839              items.each(function(f){
7840                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7841                     field = f;
7842                     return false;
7843                 }
7844                 return true;
7845             });
7846         }
7847         return field || null;
7848     },
7849      /**
7850      * Mark fields in this form invalid in bulk.
7851      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7852      * @return {BasicForm} this
7853      */
7854     markInvalid : function(errors){
7855         if(errors instanceof Array){
7856             for(var i = 0, len = errors.length; i < len; i++){
7857                 var fieldError = errors[i];
7858                 var f = this.findField(fieldError.id);
7859                 if(f){
7860                     f.markInvalid(fieldError.msg);
7861                 }
7862             }
7863         }else{
7864             var field, id;
7865             for(id in errors){
7866                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7867                     field.markInvalid(errors[id]);
7868                 }
7869             }
7870         }
7871         //Roo.each(this.childForms || [], function (f) {
7872         //    f.markInvalid(errors);
7873         //});
7874
7875         return this;
7876     },
7877
7878     /**
7879      * Set values for fields in this form in bulk.
7880      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7881      * @return {BasicForm} this
7882      */
7883     setValues : function(values){
7884         if(values instanceof Array){ // array of objects
7885             for(var i = 0, len = values.length; i < len; i++){
7886                 var v = values[i];
7887                 var f = this.findField(v.id);
7888                 if(f){
7889                     f.setValue(v.value);
7890                     if(this.trackResetOnLoad){
7891                         f.originalValue = f.getValue();
7892                     }
7893                 }
7894             }
7895         }else{ // object hash
7896             var field, id;
7897             for(id in values){
7898                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7899
7900                     if (field.setFromData &&
7901                         field.valueField &&
7902                         field.displayField &&
7903                         // combos' with local stores can
7904                         // be queried via setValue()
7905                         // to set their value..
7906                         (field.store && !field.store.isLocal)
7907                         ) {
7908                         // it's a combo
7909                         var sd = { };
7910                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7911                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7912                         field.setFromData(sd);
7913
7914                     } else {
7915                         field.setValue(values[id]);
7916                     }
7917
7918
7919                     if(this.trackResetOnLoad){
7920                         field.originalValue = field.getValue();
7921                     }
7922                 }
7923             }
7924         }
7925
7926         //Roo.each(this.childForms || [], function (f) {
7927         //    f.setValues(values);
7928         //});
7929
7930         return this;
7931     },
7932
7933     /**
7934      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7935      * they are returned as an array.
7936      * @param {Boolean} asString
7937      * @return {Object}
7938      */
7939     getValues : function(asString){
7940         //if (this.childForms) {
7941             // copy values from the child forms
7942         //    Roo.each(this.childForms, function (f) {
7943         //        this.setValues(f.getValues());
7944         //    }, this);
7945         //}
7946
7947
7948
7949         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7950         if(asString === true){
7951             return fs;
7952         }
7953         return Roo.urlDecode(fs);
7954     },
7955
7956     /**
7957      * Returns the fields in this form as an object with key/value pairs.
7958      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7959      * @return {Object}
7960      */
7961     getFieldValues : function(with_hidden)
7962     {
7963         var items = this.getItems();
7964         var ret = {};
7965         items.each(function(f){
7966             if (!f.getName()) {
7967                 return;
7968             }
7969             var v = f.getValue();
7970             if (f.inputType =='radio') {
7971                 if (typeof(ret[f.getName()]) == 'undefined') {
7972                     ret[f.getName()] = ''; // empty..
7973                 }
7974
7975                 if (!f.el.dom.checked) {
7976                     return;
7977
7978                 }
7979                 v = f.el.dom.value;
7980
7981             }
7982
7983             // not sure if this supported any more..
7984             if ((typeof(v) == 'object') && f.getRawValue) {
7985                 v = f.getRawValue() ; // dates..
7986             }
7987             // combo boxes where name != hiddenName...
7988             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7989                 ret[f.name] = f.getRawValue();
7990             }
7991             ret[f.getName()] = v;
7992         });
7993
7994         return ret;
7995     },
7996
7997     /**
7998      * Clears all invalid messages in this form.
7999      * @return {BasicForm} this
8000      */
8001     clearInvalid : function(){
8002         var items = this.getItems();
8003
8004         items.each(function(f){
8005            f.clearInvalid();
8006         });
8007
8008
8009
8010         return this;
8011     },
8012
8013     /**
8014      * Resets this form.
8015      * @return {BasicForm} this
8016      */
8017     reset : function(){
8018         var items = this.getItems();
8019         items.each(function(f){
8020             f.reset();
8021         });
8022
8023         Roo.each(this.childForms || [], function (f) {
8024             f.reset();
8025         });
8026
8027
8028         return this;
8029     },
8030     getItems : function()
8031     {
8032         var r=new Roo.util.MixedCollection(false, function(o){
8033             return o.id || (o.id = Roo.id());
8034         });
8035         var iter = function(el) {
8036             if (el.inputEl) {
8037                 r.add(el);
8038             }
8039             if (!el.items) {
8040                 return;
8041             }
8042             Roo.each(el.items,function(e) {
8043                 iter(e);
8044             });
8045
8046
8047         };
8048
8049         iter(this);
8050         return r;
8051
8052
8053
8054
8055     }
8056
8057 });
8058
8059 Roo.apply(Roo.bootstrap.Form, {
8060     
8061     popover : {
8062         
8063         padding : 5,
8064         
8065         isApplied : false,
8066         
8067         isMasked : false,
8068         
8069         form : false,
8070         
8071         target : false,
8072         
8073         toolTip : false,
8074         
8075         intervalID : false,
8076         
8077         maskEl : false,
8078         
8079         apply : function()
8080         {
8081             if(this.isApplied){
8082                 return;
8083             }
8084             
8085             this.maskEl = {
8086                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8087                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8088                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8089                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8090             };
8091             
8092             this.maskEl.top.enableDisplayMode("block");
8093             this.maskEl.left.enableDisplayMode("block");
8094             this.maskEl.bottom.enableDisplayMode("block");
8095             this.maskEl.right.enableDisplayMode("block");
8096             
8097             this.toolTip = new Roo.bootstrap.Tooltip({
8098                 cls : 'roo-form-error-popover',
8099                 alignment : {
8100                     'left' : ['r-l', [-2,0], 'right'],
8101                     'right' : ['l-r', [2,0], 'left'],
8102                     'bottom' : ['tl-bl', [0,2], 'top'],
8103                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8104                 }
8105             });
8106             
8107             this.toolTip.render(Roo.get(document.body));
8108
8109             this.toolTip.el.enableDisplayMode("block");
8110             
8111             Roo.get(document.body).on('click', function(){
8112                 this.unmask();
8113             }, this);
8114             
8115             Roo.get(document.body).on('touchstart', function(){
8116                 this.unmask();
8117             }, this);
8118             
8119             this.isApplied = true
8120         },
8121         
8122         mask : function(form, target)
8123         {
8124             this.form = form;
8125             
8126             this.target = target;
8127             
8128             if(!this.form.errorMask || !target.el){
8129                 return;
8130             }
8131             
8132             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8133             
8134             Roo.log(scrollable);
8135             
8136             var ot = this.target.el.calcOffsetsTo(scrollable);
8137             
8138             var scrollTo = ot[1] - this.form.maskOffset;
8139             
8140             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8141             
8142             scrollable.scrollTo('top', scrollTo);
8143             
8144             var box = this.target.el.getBox();
8145             Roo.log(box);
8146             var zIndex = Roo.bootstrap.Modal.zIndex++;
8147
8148             
8149             this.maskEl.top.setStyle('position', 'absolute');
8150             this.maskEl.top.setStyle('z-index', zIndex);
8151             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8152             this.maskEl.top.setLeft(0);
8153             this.maskEl.top.setTop(0);
8154             this.maskEl.top.show();
8155             
8156             this.maskEl.left.setStyle('position', 'absolute');
8157             this.maskEl.left.setStyle('z-index', zIndex);
8158             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8159             this.maskEl.left.setLeft(0);
8160             this.maskEl.left.setTop(box.y - this.padding);
8161             this.maskEl.left.show();
8162
8163             this.maskEl.bottom.setStyle('position', 'absolute');
8164             this.maskEl.bottom.setStyle('z-index', zIndex);
8165             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8166             this.maskEl.bottom.setLeft(0);
8167             this.maskEl.bottom.setTop(box.bottom + this.padding);
8168             this.maskEl.bottom.show();
8169
8170             this.maskEl.right.setStyle('position', 'absolute');
8171             this.maskEl.right.setStyle('z-index', zIndex);
8172             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8173             this.maskEl.right.setLeft(box.right + this.padding);
8174             this.maskEl.right.setTop(box.y - this.padding);
8175             this.maskEl.right.show();
8176
8177             this.toolTip.bindEl = this.target.el;
8178
8179             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8180
8181             var tip = this.target.blankText;
8182
8183             if(this.target.getValue() !== '' ) {
8184                 
8185                 if (this.target.invalidText.length) {
8186                     tip = this.target.invalidText;
8187                 } else if (this.target.regexText.length){
8188                     tip = this.target.regexText;
8189                 }
8190             }
8191
8192             this.toolTip.show(tip);
8193
8194             this.intervalID = window.setInterval(function() {
8195                 Roo.bootstrap.Form.popover.unmask();
8196             }, 10000);
8197
8198             window.onwheel = function(){ return false;};
8199             
8200             (function(){ this.isMasked = true; }).defer(500, this);
8201             
8202         },
8203         
8204         unmask : function()
8205         {
8206             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8207                 return;
8208             }
8209             
8210             this.maskEl.top.setStyle('position', 'absolute');
8211             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8212             this.maskEl.top.hide();
8213
8214             this.maskEl.left.setStyle('position', 'absolute');
8215             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8216             this.maskEl.left.hide();
8217
8218             this.maskEl.bottom.setStyle('position', 'absolute');
8219             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8220             this.maskEl.bottom.hide();
8221
8222             this.maskEl.right.setStyle('position', 'absolute');
8223             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8224             this.maskEl.right.hide();
8225             
8226             this.toolTip.hide();
8227             
8228             this.toolTip.el.hide();
8229             
8230             window.onwheel = function(){ return true;};
8231             
8232             if(this.intervalID){
8233                 window.clearInterval(this.intervalID);
8234                 this.intervalID = false;
8235             }
8236             
8237             this.isMasked = false;
8238             
8239         }
8240         
8241     }
8242     
8243 });
8244
8245 /*
8246  * Based on:
8247  * Ext JS Library 1.1.1
8248  * Copyright(c) 2006-2007, Ext JS, LLC.
8249  *
8250  * Originally Released Under LGPL - original licence link has changed is not relivant.
8251  *
8252  * Fork - LGPL
8253  * <script type="text/javascript">
8254  */
8255 /**
8256  * @class Roo.form.VTypes
8257  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8258  * @singleton
8259  */
8260 Roo.form.VTypes = function(){
8261     // closure these in so they are only created once.
8262     var alpha = /^[a-zA-Z_]+$/;
8263     var alphanum = /^[a-zA-Z0-9_]+$/;
8264     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8265     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8266
8267     // All these messages and functions are configurable
8268     return {
8269         /**
8270          * The function used to validate email addresses
8271          * @param {String} value The email address
8272          */
8273         'email' : function(v){
8274             return email.test(v);
8275         },
8276         /**
8277          * The error text to display when the email validation function returns false
8278          * @type String
8279          */
8280         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8281         /**
8282          * The keystroke filter mask to be applied on email input
8283          * @type RegExp
8284          */
8285         'emailMask' : /[a-z0-9_\.\-@]/i,
8286
8287         /**
8288          * The function used to validate URLs
8289          * @param {String} value The URL
8290          */
8291         'url' : function(v){
8292             return url.test(v);
8293         },
8294         /**
8295          * The error text to display when the url validation function returns false
8296          * @type String
8297          */
8298         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8299         
8300         /**
8301          * The function used to validate alpha values
8302          * @param {String} value The value
8303          */
8304         'alpha' : function(v){
8305             return alpha.test(v);
8306         },
8307         /**
8308          * The error text to display when the alpha validation function returns false
8309          * @type String
8310          */
8311         'alphaText' : 'This field should only contain letters and _',
8312         /**
8313          * The keystroke filter mask to be applied on alpha input
8314          * @type RegExp
8315          */
8316         'alphaMask' : /[a-z_]/i,
8317
8318         /**
8319          * The function used to validate alphanumeric values
8320          * @param {String} value The value
8321          */
8322         'alphanum' : function(v){
8323             return alphanum.test(v);
8324         },
8325         /**
8326          * The error text to display when the alphanumeric validation function returns false
8327          * @type String
8328          */
8329         'alphanumText' : 'This field should only contain letters, numbers and _',
8330         /**
8331          * The keystroke filter mask to be applied on alphanumeric input
8332          * @type RegExp
8333          */
8334         'alphanumMask' : /[a-z0-9_]/i
8335     };
8336 }();/*
8337  * - LGPL
8338  *
8339  * Input
8340  * 
8341  */
8342
8343 /**
8344  * @class Roo.bootstrap.Input
8345  * @extends Roo.bootstrap.Component
8346  * Bootstrap Input class
8347  * @cfg {Boolean} disabled is it disabled
8348  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8349  * @cfg {String} name name of the input
8350  * @cfg {string} fieldLabel - the label associated
8351  * @cfg {string} placeholder - placeholder to put in text.
8352  * @cfg {string}  before - input group add on before
8353  * @cfg {string} after - input group add on after
8354  * @cfg {string} size - (lg|sm) or leave empty..
8355  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8356  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8357  * @cfg {Number} md colspan out of 12 for computer-sized screens
8358  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8359  * @cfg {string} value default value of the input
8360  * @cfg {Number} labelWidth set the width of label 
8361  * @cfg {Number} labellg set the width of label (1-12)
8362  * @cfg {Number} labelmd set the width of label (1-12)
8363  * @cfg {Number} labelsm set the width of label (1-12)
8364  * @cfg {Number} labelxs set the width of label (1-12)
8365  * @cfg {String} labelAlign (top|left)
8366  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8367  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8368  * @cfg {String} indicatorpos (left|right) default left
8369
8370  * @cfg {String} align (left|center|right) Default left
8371  * @cfg {Boolean} forceFeedback (true|false) Default false
8372  * 
8373  * 
8374  * 
8375  * 
8376  * @constructor
8377  * Create a new Input
8378  * @param {Object} config The config object
8379  */
8380
8381 Roo.bootstrap.Input = function(config){
8382     
8383     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8384     
8385     this.addEvents({
8386         /**
8387          * @event focus
8388          * Fires when this field receives input focus.
8389          * @param {Roo.form.Field} this
8390          */
8391         focus : true,
8392         /**
8393          * @event blur
8394          * Fires when this field loses input focus.
8395          * @param {Roo.form.Field} this
8396          */
8397         blur : true,
8398         /**
8399          * @event specialkey
8400          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8401          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8402          * @param {Roo.form.Field} this
8403          * @param {Roo.EventObject} e The event object
8404          */
8405         specialkey : true,
8406         /**
8407          * @event change
8408          * Fires just before the field blurs if the field value has changed.
8409          * @param {Roo.form.Field} this
8410          * @param {Mixed} newValue The new value
8411          * @param {Mixed} oldValue The original value
8412          */
8413         change : true,
8414         /**
8415          * @event invalid
8416          * Fires after the field has been marked as invalid.
8417          * @param {Roo.form.Field} this
8418          * @param {String} msg The validation message
8419          */
8420         invalid : true,
8421         /**
8422          * @event valid
8423          * Fires after the field has been validated with no errors.
8424          * @param {Roo.form.Field} this
8425          */
8426         valid : true,
8427          /**
8428          * @event keyup
8429          * Fires after the key up
8430          * @param {Roo.form.Field} this
8431          * @param {Roo.EventObject}  e The event Object
8432          */
8433         keyup : true
8434     });
8435 };
8436
8437 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8438      /**
8439      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8440       automatic validation (defaults to "keyup").
8441      */
8442     validationEvent : "keyup",
8443      /**
8444      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8445      */
8446     validateOnBlur : true,
8447     /**
8448      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8449      */
8450     validationDelay : 250,
8451      /**
8452      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8453      */
8454     focusClass : "x-form-focus",  // not needed???
8455     
8456        
8457     /**
8458      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8459      */
8460     invalidClass : "has-warning",
8461     
8462     /**
8463      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8464      */
8465     validClass : "has-success",
8466     
8467     /**
8468      * @cfg {Boolean} hasFeedback (true|false) default true
8469      */
8470     hasFeedback : true,
8471     
8472     /**
8473      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8474      */
8475     invalidFeedbackClass : "glyphicon-warning-sign",
8476     
8477     /**
8478      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8479      */
8480     validFeedbackClass : "glyphicon-ok",
8481     
8482     /**
8483      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8484      */
8485     selectOnFocus : false,
8486     
8487      /**
8488      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8489      */
8490     maskRe : null,
8491        /**
8492      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8493      */
8494     vtype : null,
8495     
8496       /**
8497      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8498      */
8499     disableKeyFilter : false,
8500     
8501        /**
8502      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8503      */
8504     disabled : false,
8505      /**
8506      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8507      */
8508     allowBlank : true,
8509     /**
8510      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8511      */
8512     blankText : "Please complete this mandatory field",
8513     
8514      /**
8515      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8516      */
8517     minLength : 0,
8518     /**
8519      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8520      */
8521     maxLength : Number.MAX_VALUE,
8522     /**
8523      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8524      */
8525     minLengthText : "The minimum length for this field is {0}",
8526     /**
8527      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8528      */
8529     maxLengthText : "The maximum length for this field is {0}",
8530   
8531     
8532     /**
8533      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8534      * If available, this function will be called only after the basic validators all return true, and will be passed the
8535      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8536      */
8537     validator : null,
8538     /**
8539      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8540      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8541      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8542      */
8543     regex : null,
8544     /**
8545      * @cfg {String} regexText -- Depricated - use Invalid Text
8546      */
8547     regexText : "",
8548     
8549     /**
8550      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8551      */
8552     invalidText : "",
8553     
8554     
8555     
8556     autocomplete: false,
8557     
8558     
8559     fieldLabel : '',
8560     inputType : 'text',
8561     
8562     name : false,
8563     placeholder: false,
8564     before : false,
8565     after : false,
8566     size : false,
8567     hasFocus : false,
8568     preventMark: false,
8569     isFormField : true,
8570     value : '',
8571     labelWidth : 2,
8572     labelAlign : false,
8573     readOnly : false,
8574     align : false,
8575     formatedValue : false,
8576     forceFeedback : false,
8577     
8578     indicatorpos : 'left',
8579     
8580     labellg : 0,
8581     labelmd : 0,
8582     labelsm : 0,
8583     labelxs : 0,
8584     
8585     parentLabelAlign : function()
8586     {
8587         var parent = this;
8588         while (parent.parent()) {
8589             parent = parent.parent();
8590             if (typeof(parent.labelAlign) !='undefined') {
8591                 return parent.labelAlign;
8592             }
8593         }
8594         return 'left';
8595         
8596     },
8597     
8598     getAutoCreate : function()
8599     {
8600         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8601         
8602         var id = Roo.id();
8603         
8604         var cfg = {};
8605         
8606         if(this.inputType != 'hidden'){
8607             cfg.cls = 'form-group' //input-group
8608         }
8609         
8610         var input =  {
8611             tag: 'input',
8612             id : id,
8613             type : this.inputType,
8614             value : this.value,
8615             cls : 'form-control',
8616             placeholder : this.placeholder || '',
8617             autocomplete : this.autocomplete || 'new-password'
8618         };
8619         
8620         if(this.align){
8621             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8622         }
8623         
8624         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8625             input.maxLength = this.maxLength;
8626         }
8627         
8628         if (this.disabled) {
8629             input.disabled=true;
8630         }
8631         
8632         if (this.readOnly) {
8633             input.readonly=true;
8634         }
8635         
8636         if (this.name) {
8637             input.name = this.name;
8638         }
8639         
8640         if (this.size) {
8641             input.cls += ' input-' + this.size;
8642         }
8643         
8644         var settings=this;
8645         ['xs','sm','md','lg'].map(function(size){
8646             if (settings[size]) {
8647                 cfg.cls += ' col-' + size + '-' + settings[size];
8648             }
8649         });
8650         
8651         var inputblock = input;
8652         
8653         var feedback = {
8654             tag: 'span',
8655             cls: 'glyphicon form-control-feedback'
8656         };
8657             
8658         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8659             
8660             inputblock = {
8661                 cls : 'has-feedback',
8662                 cn :  [
8663                     input,
8664                     feedback
8665                 ] 
8666             };  
8667         }
8668         
8669         if (this.before || this.after) {
8670             
8671             inputblock = {
8672                 cls : 'input-group',
8673                 cn :  [] 
8674             };
8675             
8676             if (this.before && typeof(this.before) == 'string') {
8677                 
8678                 inputblock.cn.push({
8679                     tag :'span',
8680                     cls : 'roo-input-before input-group-addon',
8681                     html : this.before
8682                 });
8683             }
8684             if (this.before && typeof(this.before) == 'object') {
8685                 this.before = Roo.factory(this.before);
8686                 
8687                 inputblock.cn.push({
8688                     tag :'span',
8689                     cls : 'roo-input-before input-group-' +
8690                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8691                 });
8692             }
8693             
8694             inputblock.cn.push(input);
8695             
8696             if (this.after && typeof(this.after) == 'string') {
8697                 inputblock.cn.push({
8698                     tag :'span',
8699                     cls : 'roo-input-after input-group-addon',
8700                     html : this.after
8701                 });
8702             }
8703             if (this.after && typeof(this.after) == 'object') {
8704                 this.after = Roo.factory(this.after);
8705                 
8706                 inputblock.cn.push({
8707                     tag :'span',
8708                     cls : 'roo-input-after input-group-' +
8709                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8710                 });
8711             }
8712             
8713             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8714                 inputblock.cls += ' has-feedback';
8715                 inputblock.cn.push(feedback);
8716             }
8717         };
8718         
8719         if (align ==='left' && this.fieldLabel.length) {
8720             
8721             cfg.cls += ' roo-form-group-label-left';
8722             
8723             cfg.cn = [
8724                 {
8725                     tag : 'i',
8726                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8727                     tooltip : 'This field is required'
8728                 },
8729                 {
8730                     tag: 'label',
8731                     'for' :  id,
8732                     cls : 'control-label',
8733                     html : this.fieldLabel
8734
8735                 },
8736                 {
8737                     cls : "", 
8738                     cn: [
8739                         inputblock
8740                     ]
8741                 }
8742             ];
8743             
8744             var labelCfg = cfg.cn[1];
8745             var contentCfg = cfg.cn[2];
8746             
8747             if(this.indicatorpos == 'right'){
8748                 cfg.cn = [
8749                     {
8750                         tag: 'label',
8751                         'for' :  id,
8752                         cls : 'control-label',
8753                         cn : [
8754                             {
8755                                 tag : 'span',
8756                                 html : this.fieldLabel
8757                             },
8758                             {
8759                                 tag : 'i',
8760                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8761                                 tooltip : 'This field is required'
8762                             }
8763                         ]
8764                     },
8765                     {
8766                         cls : "",
8767                         cn: [
8768                             inputblock
8769                         ]
8770                     }
8771
8772                 ];
8773                 
8774                 labelCfg = cfg.cn[0];
8775                 contentCfg = cfg.cn[1];
8776             
8777             }
8778             
8779             if(this.labelWidth > 12){
8780                 labelCfg.style = "width: " + this.labelWidth + 'px';
8781             }
8782             
8783             if(this.labelWidth < 13 && this.labelmd == 0){
8784                 this.labelmd = this.labelWidth;
8785             }
8786             
8787             if(this.labellg > 0){
8788                 labelCfg.cls += ' col-lg-' + this.labellg;
8789                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8790             }
8791             
8792             if(this.labelmd > 0){
8793                 labelCfg.cls += ' col-md-' + this.labelmd;
8794                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8795             }
8796             
8797             if(this.labelsm > 0){
8798                 labelCfg.cls += ' col-sm-' + this.labelsm;
8799                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8800             }
8801             
8802             if(this.labelxs > 0){
8803                 labelCfg.cls += ' col-xs-' + this.labelxs;
8804                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8805             }
8806             
8807             
8808         } else if ( this.fieldLabel.length) {
8809                 
8810             cfg.cn = [
8811                 {
8812                     tag : 'i',
8813                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8814                     tooltip : 'This field is required'
8815                 },
8816                 {
8817                     tag: 'label',
8818                    //cls : 'input-group-addon',
8819                     html : this.fieldLabel
8820
8821                 },
8822
8823                inputblock
8824
8825            ];
8826            
8827            if(this.indicatorpos == 'right'){
8828                 
8829                 cfg.cn = [
8830                     {
8831                         tag: 'label',
8832                        //cls : 'input-group-addon',
8833                         html : this.fieldLabel
8834
8835                     },
8836                     {
8837                         tag : 'i',
8838                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8839                         tooltip : 'This field is required'
8840                     },
8841
8842                    inputblock
8843
8844                ];
8845
8846             }
8847
8848         } else {
8849             
8850             cfg.cn = [
8851
8852                     inputblock
8853
8854             ];
8855                 
8856                 
8857         };
8858         
8859         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8860            cfg.cls += ' navbar-form';
8861         }
8862         
8863         if (this.parentType === 'NavGroup') {
8864            cfg.cls += ' navbar-form';
8865            cfg.tag = 'li';
8866         }
8867         
8868         return cfg;
8869         
8870     },
8871     /**
8872      * return the real input element.
8873      */
8874     inputEl: function ()
8875     {
8876         return this.el.select('input.form-control',true).first();
8877     },
8878     
8879     tooltipEl : function()
8880     {
8881         return this.inputEl();
8882     },
8883     
8884     indicatorEl : function()
8885     {
8886         var indicator = this.el.select('i.roo-required-indicator',true).first();
8887         
8888         if(!indicator){
8889             return false;
8890         }
8891         
8892         return indicator;
8893         
8894     },
8895     
8896     setDisabled : function(v)
8897     {
8898         var i  = this.inputEl().dom;
8899         if (!v) {
8900             i.removeAttribute('disabled');
8901             return;
8902             
8903         }
8904         i.setAttribute('disabled','true');
8905     },
8906     initEvents : function()
8907     {
8908           
8909         this.inputEl().on("keydown" , this.fireKey,  this);
8910         this.inputEl().on("focus", this.onFocus,  this);
8911         this.inputEl().on("blur", this.onBlur,  this);
8912         
8913         this.inputEl().relayEvent('keyup', this);
8914         
8915         this.indicator = this.indicatorEl();
8916         
8917         if(this.indicator){
8918             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8919             this.indicator.hide();
8920         }
8921  
8922         // reference to original value for reset
8923         this.originalValue = this.getValue();
8924         //Roo.form.TextField.superclass.initEvents.call(this);
8925         if(this.validationEvent == 'keyup'){
8926             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8927             this.inputEl().on('keyup', this.filterValidation, this);
8928         }
8929         else if(this.validationEvent !== false){
8930             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8931         }
8932         
8933         if(this.selectOnFocus){
8934             this.on("focus", this.preFocus, this);
8935             
8936         }
8937         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8938             this.inputEl().on("keypress", this.filterKeys, this);
8939         } else {
8940             this.inputEl().relayEvent('keypress', this);
8941         }
8942        /* if(this.grow){
8943             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8944             this.el.on("click", this.autoSize,  this);
8945         }
8946         */
8947         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8948             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8949         }
8950         
8951         if (typeof(this.before) == 'object') {
8952             this.before.render(this.el.select('.roo-input-before',true).first());
8953         }
8954         if (typeof(this.after) == 'object') {
8955             this.after.render(this.el.select('.roo-input-after',true).first());
8956         }
8957         
8958         
8959     },
8960     filterValidation : function(e){
8961         if(!e.isNavKeyPress()){
8962             this.validationTask.delay(this.validationDelay);
8963         }
8964     },
8965      /**
8966      * Validates the field value
8967      * @return {Boolean} True if the value is valid, else false
8968      */
8969     validate : function(){
8970         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8971         if(this.disabled || this.validateValue(this.getRawValue())){
8972             this.markValid();
8973             return true;
8974         }
8975         
8976         this.markInvalid();
8977         return false;
8978     },
8979     
8980     
8981     /**
8982      * Validates a value according to the field's validation rules and marks the field as invalid
8983      * if the validation fails
8984      * @param {Mixed} value The value to validate
8985      * @return {Boolean} True if the value is valid, else false
8986      */
8987     validateValue : function(value){
8988         if(value.length < 1)  { // if it's blank
8989             if(this.allowBlank){
8990                 return true;
8991             }            
8992             return this.inputEl().hasClass('hide') ? true : false;
8993         }
8994         
8995         if(value.length < this.minLength){
8996             return false;
8997         }
8998         if(value.length > this.maxLength){
8999             return false;
9000         }
9001         if(this.vtype){
9002             var vt = Roo.form.VTypes;
9003             if(!vt[this.vtype](value, this)){
9004                 return false;
9005             }
9006         }
9007         if(typeof this.validator == "function"){
9008             var msg = this.validator(value);
9009             if(msg !== true){
9010                 return false;
9011             }
9012             if (typeof(msg) == 'string') {
9013                 this.invalidText = msg;
9014             }
9015         }
9016         
9017         if(this.regex && !this.regex.test(value)){
9018             return false;
9019         }
9020         
9021         return true;
9022     },
9023
9024     
9025     
9026      // private
9027     fireKey : function(e){
9028         //Roo.log('field ' + e.getKey());
9029         if(e.isNavKeyPress()){
9030             this.fireEvent("specialkey", this, e);
9031         }
9032     },
9033     focus : function (selectText){
9034         if(this.rendered){
9035             this.inputEl().focus();
9036             if(selectText === true){
9037                 this.inputEl().dom.select();
9038             }
9039         }
9040         return this;
9041     } ,
9042     
9043     onFocus : function(){
9044         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9045            // this.el.addClass(this.focusClass);
9046         }
9047         if(!this.hasFocus){
9048             this.hasFocus = true;
9049             this.startValue = this.getValue();
9050             this.fireEvent("focus", this);
9051         }
9052     },
9053     
9054     beforeBlur : Roo.emptyFn,
9055
9056     
9057     // private
9058     onBlur : function(){
9059         this.beforeBlur();
9060         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9061             //this.el.removeClass(this.focusClass);
9062         }
9063         this.hasFocus = false;
9064         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9065             this.validate();
9066         }
9067         var v = this.getValue();
9068         if(String(v) !== String(this.startValue)){
9069             this.fireEvent('change', this, v, this.startValue);
9070         }
9071         this.fireEvent("blur", this);
9072     },
9073     
9074     /**
9075      * Resets the current field value to the originally loaded value and clears any validation messages
9076      */
9077     reset : function(){
9078         this.setValue(this.originalValue);
9079         this.validate();
9080     },
9081      /**
9082      * Returns the name of the field
9083      * @return {Mixed} name The name field
9084      */
9085     getName: function(){
9086         return this.name;
9087     },
9088      /**
9089      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9090      * @return {Mixed} value The field value
9091      */
9092     getValue : function(){
9093         
9094         var v = this.inputEl().getValue();
9095         
9096         return v;
9097     },
9098     /**
9099      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9100      * @return {Mixed} value The field value
9101      */
9102     getRawValue : function(){
9103         var v = this.inputEl().getValue();
9104         
9105         return v;
9106     },
9107     
9108     /**
9109      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9110      * @param {Mixed} value The value to set
9111      */
9112     setRawValue : function(v){
9113         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9114     },
9115     
9116     selectText : function(start, end){
9117         var v = this.getRawValue();
9118         if(v.length > 0){
9119             start = start === undefined ? 0 : start;
9120             end = end === undefined ? v.length : end;
9121             var d = this.inputEl().dom;
9122             if(d.setSelectionRange){
9123                 d.setSelectionRange(start, end);
9124             }else if(d.createTextRange){
9125                 var range = d.createTextRange();
9126                 range.moveStart("character", start);
9127                 range.moveEnd("character", v.length-end);
9128                 range.select();
9129             }
9130         }
9131     },
9132     
9133     /**
9134      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9135      * @param {Mixed} value The value to set
9136      */
9137     setValue : function(v){
9138         this.value = v;
9139         if(this.rendered){
9140             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9141             this.validate();
9142         }
9143     },
9144     
9145     /*
9146     processValue : function(value){
9147         if(this.stripCharsRe){
9148             var newValue = value.replace(this.stripCharsRe, '');
9149             if(newValue !== value){
9150                 this.setRawValue(newValue);
9151                 return newValue;
9152             }
9153         }
9154         return value;
9155     },
9156   */
9157     preFocus : function(){
9158         
9159         if(this.selectOnFocus){
9160             this.inputEl().dom.select();
9161         }
9162     },
9163     filterKeys : function(e){
9164         var k = e.getKey();
9165         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9166             return;
9167         }
9168         var c = e.getCharCode(), cc = String.fromCharCode(c);
9169         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9170             return;
9171         }
9172         if(!this.maskRe.test(cc)){
9173             e.stopEvent();
9174         }
9175     },
9176      /**
9177      * Clear any invalid styles/messages for this field
9178      */
9179     clearInvalid : function(){
9180         
9181         if(!this.el || this.preventMark){ // not rendered
9182             return;
9183         }
9184         
9185      
9186         this.el.removeClass(this.invalidClass);
9187         
9188         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9189             
9190             var feedback = this.el.select('.form-control-feedback', true).first();
9191             
9192             if(feedback){
9193                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9194             }
9195             
9196         }
9197         
9198         this.fireEvent('valid', this);
9199     },
9200     
9201      /**
9202      * Mark this field as valid
9203      */
9204     markValid : function()
9205     {
9206         if(!this.el  || this.preventMark){ // not rendered...
9207             return;
9208         }
9209         
9210         this.el.removeClass([this.invalidClass, this.validClass]);
9211         
9212         var feedback = this.el.select('.form-control-feedback', true).first();
9213             
9214         if(feedback){
9215             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9216         }
9217
9218         if(this.disabled){
9219             return;
9220         }
9221         
9222         if(this.allowBlank && !this.getRawValue().length){
9223             return;
9224         }
9225         
9226         if(this.indicator){
9227             this.indicator.hide();
9228         }
9229         
9230         this.el.addClass(this.validClass);
9231         
9232         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9233             
9234             var feedback = this.el.select('.form-control-feedback', true).first();
9235             
9236             if(feedback){
9237                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9238                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9239             }
9240             
9241         }
9242         
9243         this.fireEvent('valid', this);
9244     },
9245     
9246      /**
9247      * Mark this field as invalid
9248      * @param {String} msg The validation message
9249      */
9250     markInvalid : function(msg)
9251     {
9252         if(!this.el  || this.preventMark){ // not rendered
9253             return;
9254         }
9255         
9256         this.el.removeClass([this.invalidClass, this.validClass]);
9257         
9258         var feedback = this.el.select('.form-control-feedback', true).first();
9259             
9260         if(feedback){
9261             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9262         }
9263
9264         if(this.disabled){
9265             return;
9266         }
9267         
9268         if(this.allowBlank && !this.getRawValue().length){
9269             return;
9270         }
9271         
9272         if(this.indicator){
9273             this.indicator.show();
9274         }
9275         
9276         this.el.addClass(this.invalidClass);
9277         
9278         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9279             
9280             var feedback = this.el.select('.form-control-feedback', true).first();
9281             
9282             if(feedback){
9283                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9284                 
9285                 if(this.getValue().length || this.forceFeedback){
9286                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9287                 }
9288                 
9289             }
9290             
9291         }
9292         
9293         this.fireEvent('invalid', this, msg);
9294     },
9295     // private
9296     SafariOnKeyDown : function(event)
9297     {
9298         // this is a workaround for a password hang bug on chrome/ webkit.
9299         if (this.inputEl().dom.type != 'password') {
9300             return;
9301         }
9302         
9303         var isSelectAll = false;
9304         
9305         if(this.inputEl().dom.selectionEnd > 0){
9306             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9307         }
9308         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9309             event.preventDefault();
9310             this.setValue('');
9311             return;
9312         }
9313         
9314         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9315             
9316             event.preventDefault();
9317             // this is very hacky as keydown always get's upper case.
9318             //
9319             var cc = String.fromCharCode(event.getCharCode());
9320             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9321             
9322         }
9323     },
9324     adjustWidth : function(tag, w){
9325         tag = tag.toLowerCase();
9326         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9327             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9328                 if(tag == 'input'){
9329                     return w + 2;
9330                 }
9331                 if(tag == 'textarea'){
9332                     return w-2;
9333                 }
9334             }else if(Roo.isOpera){
9335                 if(tag == 'input'){
9336                     return w + 2;
9337                 }
9338                 if(tag == 'textarea'){
9339                     return w-2;
9340                 }
9341             }
9342         }
9343         return w;
9344     },
9345     
9346     setFieldLabel : function(v)
9347     {
9348         this.fieldLabel = v;
9349         
9350         if(this.rendered){
9351             this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9352         }
9353     }
9354 });
9355
9356  
9357 /*
9358  * - LGPL
9359  *
9360  * Input
9361  * 
9362  */
9363
9364 /**
9365  * @class Roo.bootstrap.TextArea
9366  * @extends Roo.bootstrap.Input
9367  * Bootstrap TextArea class
9368  * @cfg {Number} cols Specifies the visible width of a text area
9369  * @cfg {Number} rows Specifies the visible number of lines in a text area
9370  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9371  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9372  * @cfg {string} html text
9373  * 
9374  * @constructor
9375  * Create a new TextArea
9376  * @param {Object} config The config object
9377  */
9378
9379 Roo.bootstrap.TextArea = function(config){
9380     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9381    
9382 };
9383
9384 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9385      
9386     cols : false,
9387     rows : 5,
9388     readOnly : false,
9389     warp : 'soft',
9390     resize : false,
9391     value: false,
9392     html: false,
9393     
9394     getAutoCreate : function(){
9395         
9396         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9397         
9398         var id = Roo.id();
9399         
9400         var cfg = {};
9401         
9402         if(this.inputType != 'hidden'){
9403             cfg.cls = 'form-group' //input-group
9404         }
9405         
9406         var input =  {
9407             tag: 'textarea',
9408             id : id,
9409             warp : this.warp,
9410             rows : this.rows,
9411             value : this.value || '',
9412             html: this.html || '',
9413             cls : 'form-control',
9414             placeholder : this.placeholder || '' 
9415             
9416         };
9417         
9418         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9419             input.maxLength = this.maxLength;
9420         }
9421         
9422         if(this.resize){
9423             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9424         }
9425         
9426         if(this.cols){
9427             input.cols = this.cols;
9428         }
9429         
9430         if (this.readOnly) {
9431             input.readonly = true;
9432         }
9433         
9434         if (this.name) {
9435             input.name = this.name;
9436         }
9437         
9438         if (this.size) {
9439             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9440         }
9441         
9442         var settings=this;
9443         ['xs','sm','md','lg'].map(function(size){
9444             if (settings[size]) {
9445                 cfg.cls += ' col-' + size + '-' + settings[size];
9446             }
9447         });
9448         
9449         var inputblock = input;
9450         
9451         if(this.hasFeedback && !this.allowBlank){
9452             
9453             var feedback = {
9454                 tag: 'span',
9455                 cls: 'glyphicon form-control-feedback'
9456             };
9457
9458             inputblock = {
9459                 cls : 'has-feedback',
9460                 cn :  [
9461                     input,
9462                     feedback
9463                 ] 
9464             };  
9465         }
9466         
9467         
9468         if (this.before || this.after) {
9469             
9470             inputblock = {
9471                 cls : 'input-group',
9472                 cn :  [] 
9473             };
9474             if (this.before) {
9475                 inputblock.cn.push({
9476                     tag :'span',
9477                     cls : 'input-group-addon',
9478                     html : this.before
9479                 });
9480             }
9481             
9482             inputblock.cn.push(input);
9483             
9484             if(this.hasFeedback && !this.allowBlank){
9485                 inputblock.cls += ' has-feedback';
9486                 inputblock.cn.push(feedback);
9487             }
9488             
9489             if (this.after) {
9490                 inputblock.cn.push({
9491                     tag :'span',
9492                     cls : 'input-group-addon',
9493                     html : this.after
9494                 });
9495             }
9496             
9497         }
9498         
9499         if (align ==='left' && this.fieldLabel.length) {
9500             cfg.cn = [
9501                 {
9502                     tag: 'label',
9503                     'for' :  id,
9504                     cls : 'control-label',
9505                     html : this.fieldLabel
9506                 },
9507                 {
9508                     cls : "",
9509                     cn: [
9510                         inputblock
9511                     ]
9512                 }
9513
9514             ];
9515             
9516             if(this.labelWidth > 12){
9517                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9518             }
9519
9520             if(this.labelWidth < 13 && this.labelmd == 0){
9521                 this.labelmd = this.labelWidth;
9522             }
9523
9524             if(this.labellg > 0){
9525                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9526                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9527             }
9528
9529             if(this.labelmd > 0){
9530                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9531                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9532             }
9533
9534             if(this.labelsm > 0){
9535                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9536                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9537             }
9538
9539             if(this.labelxs > 0){
9540                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9541                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9542             }
9543             
9544         } else if ( this.fieldLabel.length) {
9545             cfg.cn = [
9546
9547                {
9548                    tag: 'label',
9549                    //cls : 'input-group-addon',
9550                    html : this.fieldLabel
9551
9552                },
9553
9554                inputblock
9555
9556            ];
9557
9558         } else {
9559
9560             cfg.cn = [
9561
9562                 inputblock
9563
9564             ];
9565                 
9566         }
9567         
9568         if (this.disabled) {
9569             input.disabled=true;
9570         }
9571         
9572         return cfg;
9573         
9574     },
9575     /**
9576      * return the real textarea element.
9577      */
9578     inputEl: function ()
9579     {
9580         return this.el.select('textarea.form-control',true).first();
9581     },
9582     
9583     /**
9584      * Clear any invalid styles/messages for this field
9585      */
9586     clearInvalid : function()
9587     {
9588         
9589         if(!this.el || this.preventMark){ // not rendered
9590             return;
9591         }
9592         
9593         var label = this.el.select('label', true).first();
9594         var icon = this.el.select('i.fa-star', true).first();
9595         
9596         if(label && icon){
9597             icon.remove();
9598         }
9599         
9600         this.el.removeClass(this.invalidClass);
9601         
9602         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9603             
9604             var feedback = this.el.select('.form-control-feedback', true).first();
9605             
9606             if(feedback){
9607                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9608             }
9609             
9610         }
9611         
9612         this.fireEvent('valid', this);
9613     },
9614     
9615      /**
9616      * Mark this field as valid
9617      */
9618     markValid : function()
9619     {
9620         if(!this.el  || this.preventMark){ // not rendered
9621             return;
9622         }
9623         
9624         this.el.removeClass([this.invalidClass, this.validClass]);
9625         
9626         var feedback = this.el.select('.form-control-feedback', true).first();
9627             
9628         if(feedback){
9629             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9630         }
9631
9632         if(this.disabled || this.allowBlank){
9633             return;
9634         }
9635         
9636         var label = this.el.select('label', true).first();
9637         var icon = this.el.select('i.fa-star', true).first();
9638         
9639         if(label && icon){
9640             icon.remove();
9641         }
9642         
9643         this.el.addClass(this.validClass);
9644         
9645         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9646             
9647             var feedback = this.el.select('.form-control-feedback', true).first();
9648             
9649             if(feedback){
9650                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9651                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9652             }
9653             
9654         }
9655         
9656         this.fireEvent('valid', this);
9657     },
9658     
9659      /**
9660      * Mark this field as invalid
9661      * @param {String} msg The validation message
9662      */
9663     markInvalid : function(msg)
9664     {
9665         if(!this.el  || this.preventMark){ // not rendered
9666             return;
9667         }
9668         
9669         this.el.removeClass([this.invalidClass, this.validClass]);
9670         
9671         var feedback = this.el.select('.form-control-feedback', true).first();
9672             
9673         if(feedback){
9674             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9675         }
9676
9677         if(this.disabled || this.allowBlank){
9678             return;
9679         }
9680         
9681         var label = this.el.select('label', true).first();
9682         var icon = this.el.select('i.fa-star', true).first();
9683         
9684         if(!this.getValue().length && label && !icon){
9685             this.el.createChild({
9686                 tag : 'i',
9687                 cls : 'text-danger fa fa-lg fa-star',
9688                 tooltip : 'This field is required',
9689                 style : 'margin-right:5px;'
9690             }, label, true);
9691         }
9692
9693         this.el.addClass(this.invalidClass);
9694         
9695         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9696             
9697             var feedback = this.el.select('.form-control-feedback', true).first();
9698             
9699             if(feedback){
9700                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9701                 
9702                 if(this.getValue().length || this.forceFeedback){
9703                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9704                 }
9705                 
9706             }
9707             
9708         }
9709         
9710         this.fireEvent('invalid', this, msg);
9711     }
9712 });
9713
9714  
9715 /*
9716  * - LGPL
9717  *
9718  * trigger field - base class for combo..
9719  * 
9720  */
9721  
9722 /**
9723  * @class Roo.bootstrap.TriggerField
9724  * @extends Roo.bootstrap.Input
9725  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9726  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9727  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9728  * for which you can provide a custom implementation.  For example:
9729  * <pre><code>
9730 var trigger = new Roo.bootstrap.TriggerField();
9731 trigger.onTriggerClick = myTriggerFn;
9732 trigger.applyTo('my-field');
9733 </code></pre>
9734  *
9735  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9736  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9737  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9738  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9739  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9740
9741  * @constructor
9742  * Create a new TriggerField.
9743  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9744  * to the base TextField)
9745  */
9746 Roo.bootstrap.TriggerField = function(config){
9747     this.mimicing = false;
9748     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9749 };
9750
9751 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9752     /**
9753      * @cfg {String} triggerClass A CSS class to apply to the trigger
9754      */
9755      /**
9756      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9757      */
9758     hideTrigger:false,
9759
9760     /**
9761      * @cfg {Boolean} removable (true|false) special filter default false
9762      */
9763     removable : false,
9764     
9765     /** @cfg {Boolean} grow @hide */
9766     /** @cfg {Number} growMin @hide */
9767     /** @cfg {Number} growMax @hide */
9768
9769     /**
9770      * @hide 
9771      * @method
9772      */
9773     autoSize: Roo.emptyFn,
9774     // private
9775     monitorTab : true,
9776     // private
9777     deferHeight : true,
9778
9779     
9780     actionMode : 'wrap',
9781     
9782     caret : false,
9783     
9784     
9785     getAutoCreate : function(){
9786        
9787         var align = this.labelAlign || this.parentLabelAlign();
9788         
9789         var id = Roo.id();
9790         
9791         var cfg = {
9792             cls: 'form-group' //input-group
9793         };
9794         
9795         
9796         var input =  {
9797             tag: 'input',
9798             id : id,
9799             type : this.inputType,
9800             cls : 'form-control',
9801             autocomplete: 'new-password',
9802             placeholder : this.placeholder || '' 
9803             
9804         };
9805         if (this.name) {
9806             input.name = this.name;
9807         }
9808         if (this.size) {
9809             input.cls += ' input-' + this.size;
9810         }
9811         
9812         if (this.disabled) {
9813             input.disabled=true;
9814         }
9815         
9816         var inputblock = input;
9817         
9818         if(this.hasFeedback && !this.allowBlank){
9819             
9820             var feedback = {
9821                 tag: 'span',
9822                 cls: 'glyphicon form-control-feedback'
9823             };
9824             
9825             if(this.removable && !this.editable && !this.tickable){
9826                 inputblock = {
9827                     cls : 'has-feedback',
9828                     cn :  [
9829                         inputblock,
9830                         {
9831                             tag: 'button',
9832                             html : 'x',
9833                             cls : 'roo-combo-removable-btn close'
9834                         },
9835                         feedback
9836                     ] 
9837                 };
9838             } else {
9839                 inputblock = {
9840                     cls : 'has-feedback',
9841                     cn :  [
9842                         inputblock,
9843                         feedback
9844                     ] 
9845                 };
9846             }
9847
9848         } else {
9849             if(this.removable && !this.editable && !this.tickable){
9850                 inputblock = {
9851                     cls : 'roo-removable',
9852                     cn :  [
9853                         inputblock,
9854                         {
9855                             tag: 'button',
9856                             html : 'x',
9857                             cls : 'roo-combo-removable-btn close'
9858                         }
9859                     ] 
9860                 };
9861             }
9862         }
9863         
9864         if (this.before || this.after) {
9865             
9866             inputblock = {
9867                 cls : 'input-group',
9868                 cn :  [] 
9869             };
9870             if (this.before) {
9871                 inputblock.cn.push({
9872                     tag :'span',
9873                     cls : 'input-group-addon',
9874                     html : this.before
9875                 });
9876             }
9877             
9878             inputblock.cn.push(input);
9879             
9880             if(this.hasFeedback && !this.allowBlank){
9881                 inputblock.cls += ' has-feedback';
9882                 inputblock.cn.push(feedback);
9883             }
9884             
9885             if (this.after) {
9886                 inputblock.cn.push({
9887                     tag :'span',
9888                     cls : 'input-group-addon',
9889                     html : this.after
9890                 });
9891             }
9892             
9893         };
9894         
9895         var box = {
9896             tag: 'div',
9897             cn: [
9898                 {
9899                     tag: 'input',
9900                     type : 'hidden',
9901                     cls: 'form-hidden-field'
9902                 },
9903                 inputblock
9904             ]
9905             
9906         };
9907         
9908         if(this.multiple){
9909             box = {
9910                 tag: 'div',
9911                 cn: [
9912                     {
9913                         tag: 'input',
9914                         type : 'hidden',
9915                         cls: 'form-hidden-field'
9916                     },
9917                     {
9918                         tag: 'ul',
9919                         cls: 'roo-select2-choices',
9920                         cn:[
9921                             {
9922                                 tag: 'li',
9923                                 cls: 'roo-select2-search-field',
9924                                 cn: [
9925
9926                                     inputblock
9927                                 ]
9928                             }
9929                         ]
9930                     }
9931                 ]
9932             }
9933         };
9934         
9935         var combobox = {
9936             cls: 'roo-select2-container input-group',
9937             cn: [
9938                 box
9939 //                {
9940 //                    tag: 'ul',
9941 //                    cls: 'typeahead typeahead-long dropdown-menu',
9942 //                    style: 'display:none'
9943 //                }
9944             ]
9945         };
9946         
9947         if(!this.multiple && this.showToggleBtn){
9948             
9949             var caret = {
9950                         tag: 'span',
9951                         cls: 'caret'
9952              };
9953             if (this.caret != false) {
9954                 caret = {
9955                      tag: 'i',
9956                      cls: 'fa fa-' + this.caret
9957                 };
9958                 
9959             }
9960             
9961             combobox.cn.push({
9962                 tag :'span',
9963                 cls : 'input-group-addon btn dropdown-toggle',
9964                 cn : [
9965                     caret,
9966                     {
9967                         tag: 'span',
9968                         cls: 'combobox-clear',
9969                         cn  : [
9970                             {
9971                                 tag : 'i',
9972                                 cls: 'icon-remove'
9973                             }
9974                         ]
9975                     }
9976                 ]
9977
9978             })
9979         }
9980         
9981         if(this.multiple){
9982             combobox.cls += ' roo-select2-container-multi';
9983         }
9984         
9985         if (align ==='left' && this.fieldLabel.length) {
9986             
9987             cfg.cls += ' roo-form-group-label-left';
9988
9989             cfg.cn = [
9990                 {
9991                     tag : 'i',
9992                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9993                     tooltip : 'This field is required'
9994                 },
9995                 {
9996                     tag: 'label',
9997                     'for' :  id,
9998                     cls : 'control-label',
9999                     html : this.fieldLabel
10000
10001                 },
10002                 {
10003                     cls : "", 
10004                     cn: [
10005                         combobox
10006                     ]
10007                 }
10008
10009             ];
10010             
10011             var labelCfg = cfg.cn[1];
10012             var contentCfg = cfg.cn[2];
10013             
10014             if(this.indicatorpos == 'right'){
10015                 cfg.cn = [
10016                     {
10017                         tag: 'label',
10018                         'for' :  id,
10019                         cls : 'control-label',
10020                         cn : [
10021                             {
10022                                 tag : 'span',
10023                                 html : this.fieldLabel
10024                             },
10025                             {
10026                                 tag : 'i',
10027                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10028                                 tooltip : 'This field is required'
10029                             }
10030                         ]
10031                     },
10032                     {
10033                         cls : "", 
10034                         cn: [
10035                             combobox
10036                         ]
10037                     }
10038
10039                 ];
10040                 
10041                 labelCfg = cfg.cn[0];
10042                 contentCfg = cfg.cn[1];
10043             }
10044             
10045             if(this.labelWidth > 12){
10046                 labelCfg.style = "width: " + this.labelWidth + 'px';
10047             }
10048             
10049             if(this.labelWidth < 13 && this.labelmd == 0){
10050                 this.labelmd = this.labelWidth;
10051             }
10052             
10053             if(this.labellg > 0){
10054                 labelCfg.cls += ' col-lg-' + this.labellg;
10055                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10056             }
10057             
10058             if(this.labelmd > 0){
10059                 labelCfg.cls += ' col-md-' + this.labelmd;
10060                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10061             }
10062             
10063             if(this.labelsm > 0){
10064                 labelCfg.cls += ' col-sm-' + this.labelsm;
10065                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10066             }
10067             
10068             if(this.labelxs > 0){
10069                 labelCfg.cls += ' col-xs-' + this.labelxs;
10070                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10071             }
10072             
10073         } else if ( this.fieldLabel.length) {
10074 //                Roo.log(" label");
10075             cfg.cn = [
10076                 {
10077                    tag : 'i',
10078                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10079                    tooltip : 'This field is required'
10080                },
10081                {
10082                    tag: 'label',
10083                    //cls : 'input-group-addon',
10084                    html : this.fieldLabel
10085
10086                },
10087
10088                combobox
10089
10090             ];
10091             
10092             if(this.indicatorpos == 'right'){
10093                 
10094                 cfg.cn = [
10095                     {
10096                        tag: 'label',
10097                        cn : [
10098                            {
10099                                tag : 'span',
10100                                html : this.fieldLabel
10101                            },
10102                            {
10103                               tag : 'i',
10104                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10105                               tooltip : 'This field is required'
10106                            }
10107                        ]
10108
10109                     },
10110                     combobox
10111
10112                 ];
10113
10114             }
10115
10116         } else {
10117             
10118 //                Roo.log(" no label && no align");
10119                 cfg = combobox
10120                      
10121                 
10122         }
10123         
10124         var settings=this;
10125         ['xs','sm','md','lg'].map(function(size){
10126             if (settings[size]) {
10127                 cfg.cls += ' col-' + size + '-' + settings[size];
10128             }
10129         });
10130         
10131         return cfg;
10132         
10133     },
10134     
10135     
10136     
10137     // private
10138     onResize : function(w, h){
10139 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10140 //        if(typeof w == 'number'){
10141 //            var x = w - this.trigger.getWidth();
10142 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10143 //            this.trigger.setStyle('left', x+'px');
10144 //        }
10145     },
10146
10147     // private
10148     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10149
10150     // private
10151     getResizeEl : function(){
10152         return this.inputEl();
10153     },
10154
10155     // private
10156     getPositionEl : function(){
10157         return this.inputEl();
10158     },
10159
10160     // private
10161     alignErrorIcon : function(){
10162         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10163     },
10164
10165     // private
10166     initEvents : function(){
10167         
10168         this.createList();
10169         
10170         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10171         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10172         if(!this.multiple && this.showToggleBtn){
10173             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10174             if(this.hideTrigger){
10175                 this.trigger.setDisplayed(false);
10176             }
10177             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10178         }
10179         
10180         if(this.multiple){
10181             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10182         }
10183         
10184         if(this.removable && !this.editable && !this.tickable){
10185             var close = this.closeTriggerEl();
10186             
10187             if(close){
10188                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10189                 close.on('click', this.removeBtnClick, this, close);
10190             }
10191         }
10192         
10193         //this.trigger.addClassOnOver('x-form-trigger-over');
10194         //this.trigger.addClassOnClick('x-form-trigger-click');
10195         
10196         //if(!this.width){
10197         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10198         //}
10199     },
10200     
10201     closeTriggerEl : function()
10202     {
10203         var close = this.el.select('.roo-combo-removable-btn', true).first();
10204         return close ? close : false;
10205     },
10206     
10207     removeBtnClick : function(e, h, el)
10208     {
10209         e.preventDefault();
10210         
10211         if(this.fireEvent("remove", this) !== false){
10212             this.reset();
10213             this.fireEvent("afterremove", this)
10214         }
10215     },
10216     
10217     createList : function()
10218     {
10219         this.list = Roo.get(document.body).createChild({
10220             tag: 'ul',
10221             cls: 'typeahead typeahead-long dropdown-menu',
10222             style: 'display:none'
10223         });
10224         
10225         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10226         
10227     },
10228
10229     // private
10230     initTrigger : function(){
10231        
10232     },
10233
10234     // private
10235     onDestroy : function(){
10236         if(this.trigger){
10237             this.trigger.removeAllListeners();
10238           //  this.trigger.remove();
10239         }
10240         //if(this.wrap){
10241         //    this.wrap.remove();
10242         //}
10243         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10244     },
10245
10246     // private
10247     onFocus : function(){
10248         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10249         /*
10250         if(!this.mimicing){
10251             this.wrap.addClass('x-trigger-wrap-focus');
10252             this.mimicing = true;
10253             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10254             if(this.monitorTab){
10255                 this.el.on("keydown", this.checkTab, this);
10256             }
10257         }
10258         */
10259     },
10260
10261     // private
10262     checkTab : function(e){
10263         if(e.getKey() == e.TAB){
10264             this.triggerBlur();
10265         }
10266     },
10267
10268     // private
10269     onBlur : function(){
10270         // do nothing
10271     },
10272
10273     // private
10274     mimicBlur : function(e, t){
10275         /*
10276         if(!this.wrap.contains(t) && this.validateBlur()){
10277             this.triggerBlur();
10278         }
10279         */
10280     },
10281
10282     // private
10283     triggerBlur : function(){
10284         this.mimicing = false;
10285         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10286         if(this.monitorTab){
10287             this.el.un("keydown", this.checkTab, this);
10288         }
10289         //this.wrap.removeClass('x-trigger-wrap-focus');
10290         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10291     },
10292
10293     // private
10294     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10295     validateBlur : function(e, t){
10296         return true;
10297     },
10298
10299     // private
10300     onDisable : function(){
10301         this.inputEl().dom.disabled = true;
10302         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10303         //if(this.wrap){
10304         //    this.wrap.addClass('x-item-disabled');
10305         //}
10306     },
10307
10308     // private
10309     onEnable : function(){
10310         this.inputEl().dom.disabled = false;
10311         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10312         //if(this.wrap){
10313         //    this.el.removeClass('x-item-disabled');
10314         //}
10315     },
10316
10317     // private
10318     onShow : function(){
10319         var ae = this.getActionEl();
10320         
10321         if(ae){
10322             ae.dom.style.display = '';
10323             ae.dom.style.visibility = 'visible';
10324         }
10325     },
10326
10327     // private
10328     
10329     onHide : function(){
10330         var ae = this.getActionEl();
10331         ae.dom.style.display = 'none';
10332     },
10333
10334     /**
10335      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10336      * by an implementing function.
10337      * @method
10338      * @param {EventObject} e
10339      */
10340     onTriggerClick : Roo.emptyFn
10341 });
10342  /*
10343  * Based on:
10344  * Ext JS Library 1.1.1
10345  * Copyright(c) 2006-2007, Ext JS, LLC.
10346  *
10347  * Originally Released Under LGPL - original licence link has changed is not relivant.
10348  *
10349  * Fork - LGPL
10350  * <script type="text/javascript">
10351  */
10352
10353
10354 /**
10355  * @class Roo.data.SortTypes
10356  * @singleton
10357  * Defines the default sorting (casting?) comparison functions used when sorting data.
10358  */
10359 Roo.data.SortTypes = {
10360     /**
10361      * Default sort that does nothing
10362      * @param {Mixed} s The value being converted
10363      * @return {Mixed} The comparison value
10364      */
10365     none : function(s){
10366         return s;
10367     },
10368     
10369     /**
10370      * The regular expression used to strip tags
10371      * @type {RegExp}
10372      * @property
10373      */
10374     stripTagsRE : /<\/?[^>]+>/gi,
10375     
10376     /**
10377      * Strips all HTML tags to sort on text only
10378      * @param {Mixed} s The value being converted
10379      * @return {String} The comparison value
10380      */
10381     asText : function(s){
10382         return String(s).replace(this.stripTagsRE, "");
10383     },
10384     
10385     /**
10386      * Strips all HTML tags to sort on text only - Case insensitive
10387      * @param {Mixed} s The value being converted
10388      * @return {String} The comparison value
10389      */
10390     asUCText : function(s){
10391         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10392     },
10393     
10394     /**
10395      * Case insensitive string
10396      * @param {Mixed} s The value being converted
10397      * @return {String} The comparison value
10398      */
10399     asUCString : function(s) {
10400         return String(s).toUpperCase();
10401     },
10402     
10403     /**
10404      * Date sorting
10405      * @param {Mixed} s The value being converted
10406      * @return {Number} The comparison value
10407      */
10408     asDate : function(s) {
10409         if(!s){
10410             return 0;
10411         }
10412         if(s instanceof Date){
10413             return s.getTime();
10414         }
10415         return Date.parse(String(s));
10416     },
10417     
10418     /**
10419      * Float sorting
10420      * @param {Mixed} s The value being converted
10421      * @return {Float} The comparison value
10422      */
10423     asFloat : function(s) {
10424         var val = parseFloat(String(s).replace(/,/g, ""));
10425         if(isNaN(val)) {
10426             val = 0;
10427         }
10428         return val;
10429     },
10430     
10431     /**
10432      * Integer sorting
10433      * @param {Mixed} s The value being converted
10434      * @return {Number} The comparison value
10435      */
10436     asInt : function(s) {
10437         var val = parseInt(String(s).replace(/,/g, ""));
10438         if(isNaN(val)) {
10439             val = 0;
10440         }
10441         return val;
10442     }
10443 };/*
10444  * Based on:
10445  * Ext JS Library 1.1.1
10446  * Copyright(c) 2006-2007, Ext JS, LLC.
10447  *
10448  * Originally Released Under LGPL - original licence link has changed is not relivant.
10449  *
10450  * Fork - LGPL
10451  * <script type="text/javascript">
10452  */
10453
10454 /**
10455 * @class Roo.data.Record
10456  * Instances of this class encapsulate both record <em>definition</em> information, and record
10457  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10458  * to access Records cached in an {@link Roo.data.Store} object.<br>
10459  * <p>
10460  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10461  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10462  * objects.<br>
10463  * <p>
10464  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10465  * @constructor
10466  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10467  * {@link #create}. The parameters are the same.
10468  * @param {Array} data An associative Array of data values keyed by the field name.
10469  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10470  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10471  * not specified an integer id is generated.
10472  */
10473 Roo.data.Record = function(data, id){
10474     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10475     this.data = data;
10476 };
10477
10478 /**
10479  * Generate a constructor for a specific record layout.
10480  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10481  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10482  * Each field definition object may contain the following properties: <ul>
10483  * <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,
10484  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10485  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10486  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10487  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10488  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10489  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10490  * this may be omitted.</p></li>
10491  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10492  * <ul><li>auto (Default, implies no conversion)</li>
10493  * <li>string</li>
10494  * <li>int</li>
10495  * <li>float</li>
10496  * <li>boolean</li>
10497  * <li>date</li></ul></p></li>
10498  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10499  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10500  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10501  * by the Reader into an object that will be stored in the Record. It is passed the
10502  * following parameters:<ul>
10503  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10504  * </ul></p></li>
10505  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10506  * </ul>
10507  * <br>usage:<br><pre><code>
10508 var TopicRecord = Roo.data.Record.create(
10509     {name: 'title', mapping: 'topic_title'},
10510     {name: 'author', mapping: 'username'},
10511     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10512     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10513     {name: 'lastPoster', mapping: 'user2'},
10514     {name: 'excerpt', mapping: 'post_text'}
10515 );
10516
10517 var myNewRecord = new TopicRecord({
10518     title: 'Do my job please',
10519     author: 'noobie',
10520     totalPosts: 1,
10521     lastPost: new Date(),
10522     lastPoster: 'Animal',
10523     excerpt: 'No way dude!'
10524 });
10525 myStore.add(myNewRecord);
10526 </code></pre>
10527  * @method create
10528  * @static
10529  */
10530 Roo.data.Record.create = function(o){
10531     var f = function(){
10532         f.superclass.constructor.apply(this, arguments);
10533     };
10534     Roo.extend(f, Roo.data.Record);
10535     var p = f.prototype;
10536     p.fields = new Roo.util.MixedCollection(false, function(field){
10537         return field.name;
10538     });
10539     for(var i = 0, len = o.length; i < len; i++){
10540         p.fields.add(new Roo.data.Field(o[i]));
10541     }
10542     f.getField = function(name){
10543         return p.fields.get(name);  
10544     };
10545     return f;
10546 };
10547
10548 Roo.data.Record.AUTO_ID = 1000;
10549 Roo.data.Record.EDIT = 'edit';
10550 Roo.data.Record.REJECT = 'reject';
10551 Roo.data.Record.COMMIT = 'commit';
10552
10553 Roo.data.Record.prototype = {
10554     /**
10555      * Readonly flag - true if this record has been modified.
10556      * @type Boolean
10557      */
10558     dirty : false,
10559     editing : false,
10560     error: null,
10561     modified: null,
10562
10563     // private
10564     join : function(store){
10565         this.store = store;
10566     },
10567
10568     /**
10569      * Set the named field to the specified value.
10570      * @param {String} name The name of the field to set.
10571      * @param {Object} value The value to set the field to.
10572      */
10573     set : function(name, value){
10574         if(this.data[name] == value){
10575             return;
10576         }
10577         this.dirty = true;
10578         if(!this.modified){
10579             this.modified = {};
10580         }
10581         if(typeof this.modified[name] == 'undefined'){
10582             this.modified[name] = this.data[name];
10583         }
10584         this.data[name] = value;
10585         if(!this.editing && this.store){
10586             this.store.afterEdit(this);
10587         }       
10588     },
10589
10590     /**
10591      * Get the value of the named field.
10592      * @param {String} name The name of the field to get the value of.
10593      * @return {Object} The value of the field.
10594      */
10595     get : function(name){
10596         return this.data[name]; 
10597     },
10598
10599     // private
10600     beginEdit : function(){
10601         this.editing = true;
10602         this.modified = {}; 
10603     },
10604
10605     // private
10606     cancelEdit : function(){
10607         this.editing = false;
10608         delete this.modified;
10609     },
10610
10611     // private
10612     endEdit : function(){
10613         this.editing = false;
10614         if(this.dirty && this.store){
10615             this.store.afterEdit(this);
10616         }
10617     },
10618
10619     /**
10620      * Usually called by the {@link Roo.data.Store} which owns the Record.
10621      * Rejects all changes made to the Record since either creation, or the last commit operation.
10622      * Modified fields are reverted to their original values.
10623      * <p>
10624      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10625      * of reject operations.
10626      */
10627     reject : function(){
10628         var m = this.modified;
10629         for(var n in m){
10630             if(typeof m[n] != "function"){
10631                 this.data[n] = m[n];
10632             }
10633         }
10634         this.dirty = false;
10635         delete this.modified;
10636         this.editing = false;
10637         if(this.store){
10638             this.store.afterReject(this);
10639         }
10640     },
10641
10642     /**
10643      * Usually called by the {@link Roo.data.Store} which owns the Record.
10644      * Commits all changes made to the Record since either creation, or the last commit operation.
10645      * <p>
10646      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10647      * of commit operations.
10648      */
10649     commit : function(){
10650         this.dirty = false;
10651         delete this.modified;
10652         this.editing = false;
10653         if(this.store){
10654             this.store.afterCommit(this);
10655         }
10656     },
10657
10658     // private
10659     hasError : function(){
10660         return this.error != null;
10661     },
10662
10663     // private
10664     clearError : function(){
10665         this.error = null;
10666     },
10667
10668     /**
10669      * Creates a copy of this record.
10670      * @param {String} id (optional) A new record id if you don't want to use this record's id
10671      * @return {Record}
10672      */
10673     copy : function(newId) {
10674         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10675     }
10676 };/*
10677  * Based on:
10678  * Ext JS Library 1.1.1
10679  * Copyright(c) 2006-2007, Ext JS, LLC.
10680  *
10681  * Originally Released Under LGPL - original licence link has changed is not relivant.
10682  *
10683  * Fork - LGPL
10684  * <script type="text/javascript">
10685  */
10686
10687
10688
10689 /**
10690  * @class Roo.data.Store
10691  * @extends Roo.util.Observable
10692  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10693  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10694  * <p>
10695  * 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
10696  * has no knowledge of the format of the data returned by the Proxy.<br>
10697  * <p>
10698  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10699  * instances from the data object. These records are cached and made available through accessor functions.
10700  * @constructor
10701  * Creates a new Store.
10702  * @param {Object} config A config object containing the objects needed for the Store to access data,
10703  * and read the data into Records.
10704  */
10705 Roo.data.Store = function(config){
10706     this.data = new Roo.util.MixedCollection(false);
10707     this.data.getKey = function(o){
10708         return o.id;
10709     };
10710     this.baseParams = {};
10711     // private
10712     this.paramNames = {
10713         "start" : "start",
10714         "limit" : "limit",
10715         "sort" : "sort",
10716         "dir" : "dir",
10717         "multisort" : "_multisort"
10718     };
10719
10720     if(config && config.data){
10721         this.inlineData = config.data;
10722         delete config.data;
10723     }
10724
10725     Roo.apply(this, config);
10726     
10727     if(this.reader){ // reader passed
10728         this.reader = Roo.factory(this.reader, Roo.data);
10729         this.reader.xmodule = this.xmodule || false;
10730         if(!this.recordType){
10731             this.recordType = this.reader.recordType;
10732         }
10733         if(this.reader.onMetaChange){
10734             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10735         }
10736     }
10737
10738     if(this.recordType){
10739         this.fields = this.recordType.prototype.fields;
10740     }
10741     this.modified = [];
10742
10743     this.addEvents({
10744         /**
10745          * @event datachanged
10746          * Fires when the data cache has changed, and a widget which is using this Store
10747          * as a Record cache should refresh its view.
10748          * @param {Store} this
10749          */
10750         datachanged : true,
10751         /**
10752          * @event metachange
10753          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10754          * @param {Store} this
10755          * @param {Object} meta The JSON metadata
10756          */
10757         metachange : true,
10758         /**
10759          * @event add
10760          * Fires when Records have been added to the Store
10761          * @param {Store} this
10762          * @param {Roo.data.Record[]} records The array of Records added
10763          * @param {Number} index The index at which the record(s) were added
10764          */
10765         add : true,
10766         /**
10767          * @event remove
10768          * Fires when a Record has been removed from the Store
10769          * @param {Store} this
10770          * @param {Roo.data.Record} record The Record that was removed
10771          * @param {Number} index The index at which the record was removed
10772          */
10773         remove : true,
10774         /**
10775          * @event update
10776          * Fires when a Record has been updated
10777          * @param {Store} this
10778          * @param {Roo.data.Record} record The Record that was updated
10779          * @param {String} operation The update operation being performed.  Value may be one of:
10780          * <pre><code>
10781  Roo.data.Record.EDIT
10782  Roo.data.Record.REJECT
10783  Roo.data.Record.COMMIT
10784          * </code></pre>
10785          */
10786         update : true,
10787         /**
10788          * @event clear
10789          * Fires when the data cache has been cleared.
10790          * @param {Store} this
10791          */
10792         clear : true,
10793         /**
10794          * @event beforeload
10795          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10796          * the load action will be canceled.
10797          * @param {Store} this
10798          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10799          */
10800         beforeload : true,
10801         /**
10802          * @event beforeloadadd
10803          * Fires after a new set of Records has been loaded.
10804          * @param {Store} this
10805          * @param {Roo.data.Record[]} records The Records that were loaded
10806          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10807          */
10808         beforeloadadd : true,
10809         /**
10810          * @event load
10811          * Fires after a new set of Records has been loaded, before they are added to the store.
10812          * @param {Store} this
10813          * @param {Roo.data.Record[]} records The Records that were loaded
10814          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10815          * @params {Object} return from reader
10816          */
10817         load : true,
10818         /**
10819          * @event loadexception
10820          * Fires if an exception occurs in the Proxy during loading.
10821          * Called with the signature of the Proxy's "loadexception" event.
10822          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10823          * 
10824          * @param {Proxy} 
10825          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10826          * @param {Object} load options 
10827          * @param {Object} jsonData from your request (normally this contains the Exception)
10828          */
10829         loadexception : true
10830     });
10831     
10832     if(this.proxy){
10833         this.proxy = Roo.factory(this.proxy, Roo.data);
10834         this.proxy.xmodule = this.xmodule || false;
10835         this.relayEvents(this.proxy,  ["loadexception"]);
10836     }
10837     this.sortToggle = {};
10838     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10839
10840     Roo.data.Store.superclass.constructor.call(this);
10841
10842     if(this.inlineData){
10843         this.loadData(this.inlineData);
10844         delete this.inlineData;
10845     }
10846 };
10847
10848 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10849      /**
10850     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10851     * without a remote query - used by combo/forms at present.
10852     */
10853     
10854     /**
10855     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10856     */
10857     /**
10858     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10859     */
10860     /**
10861     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10862     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10863     */
10864     /**
10865     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10866     * on any HTTP request
10867     */
10868     /**
10869     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10870     */
10871     /**
10872     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10873     */
10874     multiSort: false,
10875     /**
10876     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10877     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10878     */
10879     remoteSort : false,
10880
10881     /**
10882     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10883      * loaded or when a record is removed. (defaults to false).
10884     */
10885     pruneModifiedRecords : false,
10886
10887     // private
10888     lastOptions : null,
10889
10890     /**
10891      * Add Records to the Store and fires the add event.
10892      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10893      */
10894     add : function(records){
10895         records = [].concat(records);
10896         for(var i = 0, len = records.length; i < len; i++){
10897             records[i].join(this);
10898         }
10899         var index = this.data.length;
10900         this.data.addAll(records);
10901         this.fireEvent("add", this, records, index);
10902     },
10903
10904     /**
10905      * Remove a Record from the Store and fires the remove event.
10906      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10907      */
10908     remove : function(record){
10909         var index = this.data.indexOf(record);
10910         this.data.removeAt(index);
10911         if(this.pruneModifiedRecords){
10912             this.modified.remove(record);
10913         }
10914         this.fireEvent("remove", this, record, index);
10915     },
10916
10917     /**
10918      * Remove all Records from the Store and fires the clear event.
10919      */
10920     removeAll : function(){
10921         this.data.clear();
10922         if(this.pruneModifiedRecords){
10923             this.modified = [];
10924         }
10925         this.fireEvent("clear", this);
10926     },
10927
10928     /**
10929      * Inserts Records to the Store at the given index and fires the add event.
10930      * @param {Number} index The start index at which to insert the passed Records.
10931      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10932      */
10933     insert : function(index, records){
10934         records = [].concat(records);
10935         for(var i = 0, len = records.length; i < len; i++){
10936             this.data.insert(index, records[i]);
10937             records[i].join(this);
10938         }
10939         this.fireEvent("add", this, records, index);
10940     },
10941
10942     /**
10943      * Get the index within the cache of the passed Record.
10944      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10945      * @return {Number} The index of the passed Record. Returns -1 if not found.
10946      */
10947     indexOf : function(record){
10948         return this.data.indexOf(record);
10949     },
10950
10951     /**
10952      * Get the index within the cache of the Record with the passed id.
10953      * @param {String} id The id of the Record to find.
10954      * @return {Number} The index of the Record. Returns -1 if not found.
10955      */
10956     indexOfId : function(id){
10957         return this.data.indexOfKey(id);
10958     },
10959
10960     /**
10961      * Get the Record with the specified id.
10962      * @param {String} id The id of the Record to find.
10963      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10964      */
10965     getById : function(id){
10966         return this.data.key(id);
10967     },
10968
10969     /**
10970      * Get the Record at the specified index.
10971      * @param {Number} index The index of the Record to find.
10972      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10973      */
10974     getAt : function(index){
10975         return this.data.itemAt(index);
10976     },
10977
10978     /**
10979      * Returns a range of Records between specified indices.
10980      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10981      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10982      * @return {Roo.data.Record[]} An array of Records
10983      */
10984     getRange : function(start, end){
10985         return this.data.getRange(start, end);
10986     },
10987
10988     // private
10989     storeOptions : function(o){
10990         o = Roo.apply({}, o);
10991         delete o.callback;
10992         delete o.scope;
10993         this.lastOptions = o;
10994     },
10995
10996     /**
10997      * Loads the Record cache from the configured Proxy using the configured Reader.
10998      * <p>
10999      * If using remote paging, then the first load call must specify the <em>start</em>
11000      * and <em>limit</em> properties in the options.params property to establish the initial
11001      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11002      * <p>
11003      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11004      * and this call will return before the new data has been loaded. Perform any post-processing
11005      * in a callback function, or in a "load" event handler.</strong>
11006      * <p>
11007      * @param {Object} options An object containing properties which control loading options:<ul>
11008      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11009      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11010      * passed the following arguments:<ul>
11011      * <li>r : Roo.data.Record[]</li>
11012      * <li>options: Options object from the load call</li>
11013      * <li>success: Boolean success indicator</li></ul></li>
11014      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11015      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11016      * </ul>
11017      */
11018     load : function(options){
11019         options = options || {};
11020         if(this.fireEvent("beforeload", this, options) !== false){
11021             this.storeOptions(options);
11022             var p = Roo.apply(options.params || {}, this.baseParams);
11023             // if meta was not loaded from remote source.. try requesting it.
11024             if (!this.reader.metaFromRemote) {
11025                 p._requestMeta = 1;
11026             }
11027             if(this.sortInfo && this.remoteSort){
11028                 var pn = this.paramNames;
11029                 p[pn["sort"]] = this.sortInfo.field;
11030                 p[pn["dir"]] = this.sortInfo.direction;
11031             }
11032             if (this.multiSort) {
11033                 var pn = this.paramNames;
11034                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11035             }
11036             
11037             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11038         }
11039     },
11040
11041     /**
11042      * Reloads the Record cache from the configured Proxy using the configured Reader and
11043      * the options from the last load operation performed.
11044      * @param {Object} options (optional) An object containing properties which may override the options
11045      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11046      * the most recently used options are reused).
11047      */
11048     reload : function(options){
11049         this.load(Roo.applyIf(options||{}, this.lastOptions));
11050     },
11051
11052     // private
11053     // Called as a callback by the Reader during a load operation.
11054     loadRecords : function(o, options, success){
11055         if(!o || success === false){
11056             if(success !== false){
11057                 this.fireEvent("load", this, [], options, o);
11058             }
11059             if(options.callback){
11060                 options.callback.call(options.scope || this, [], options, false);
11061             }
11062             return;
11063         }
11064         // if data returned failure - throw an exception.
11065         if (o.success === false) {
11066             // show a message if no listener is registered.
11067             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11068                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11069             }
11070             // loadmask wil be hooked into this..
11071             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11072             return;
11073         }
11074         var r = o.records, t = o.totalRecords || r.length;
11075         
11076         this.fireEvent("beforeloadadd", this, r, options, o);
11077         
11078         if(!options || options.add !== true){
11079             if(this.pruneModifiedRecords){
11080                 this.modified = [];
11081             }
11082             for(var i = 0, len = r.length; i < len; i++){
11083                 r[i].join(this);
11084             }
11085             if(this.snapshot){
11086                 this.data = this.snapshot;
11087                 delete this.snapshot;
11088             }
11089             this.data.clear();
11090             this.data.addAll(r);
11091             this.totalLength = t;
11092             this.applySort();
11093             this.fireEvent("datachanged", this);
11094         }else{
11095             this.totalLength = Math.max(t, this.data.length+r.length);
11096             this.add(r);
11097         }
11098         
11099         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11100                 
11101             var e = new Roo.data.Record({});
11102
11103             e.set(this.parent.displayField, this.parent.emptyTitle);
11104             e.set(this.parent.valueField, '');
11105
11106             this.insert(0, e);
11107         }
11108             
11109         this.fireEvent("load", this, r, options, o);
11110         if(options.callback){
11111             options.callback.call(options.scope || this, r, options, true);
11112         }
11113     },
11114
11115
11116     /**
11117      * Loads data from a passed data block. A Reader which understands the format of the data
11118      * must have been configured in the constructor.
11119      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11120      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11121      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11122      */
11123     loadData : function(o, append){
11124         var r = this.reader.readRecords(o);
11125         this.loadRecords(r, {add: append}, true);
11126     },
11127
11128     /**
11129      * Gets the number of cached records.
11130      * <p>
11131      * <em>If using paging, this may not be the total size of the dataset. If the data object
11132      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11133      * the data set size</em>
11134      */
11135     getCount : function(){
11136         return this.data.length || 0;
11137     },
11138
11139     /**
11140      * Gets the total number of records in the dataset as returned by the server.
11141      * <p>
11142      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11143      * the dataset size</em>
11144      */
11145     getTotalCount : function(){
11146         return this.totalLength || 0;
11147     },
11148
11149     /**
11150      * Returns the sort state of the Store as an object with two properties:
11151      * <pre><code>
11152  field {String} The name of the field by which the Records are sorted
11153  direction {String} The sort order, "ASC" or "DESC"
11154      * </code></pre>
11155      */
11156     getSortState : function(){
11157         return this.sortInfo;
11158     },
11159
11160     // private
11161     applySort : function(){
11162         if(this.sortInfo && !this.remoteSort){
11163             var s = this.sortInfo, f = s.field;
11164             var st = this.fields.get(f).sortType;
11165             var fn = function(r1, r2){
11166                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11167                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11168             };
11169             this.data.sort(s.direction, fn);
11170             if(this.snapshot && this.snapshot != this.data){
11171                 this.snapshot.sort(s.direction, fn);
11172             }
11173         }
11174     },
11175
11176     /**
11177      * Sets the default sort column and order to be used by the next load operation.
11178      * @param {String} fieldName The name of the field to sort by.
11179      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11180      */
11181     setDefaultSort : function(field, dir){
11182         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11183     },
11184
11185     /**
11186      * Sort the Records.
11187      * If remote sorting is used, the sort is performed on the server, and the cache is
11188      * reloaded. If local sorting is used, the cache is sorted internally.
11189      * @param {String} fieldName The name of the field to sort by.
11190      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11191      */
11192     sort : function(fieldName, dir){
11193         var f = this.fields.get(fieldName);
11194         if(!dir){
11195             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11196             
11197             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11198                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11199             }else{
11200                 dir = f.sortDir;
11201             }
11202         }
11203         this.sortToggle[f.name] = dir;
11204         this.sortInfo = {field: f.name, direction: dir};
11205         if(!this.remoteSort){
11206             this.applySort();
11207             this.fireEvent("datachanged", this);
11208         }else{
11209             this.load(this.lastOptions);
11210         }
11211     },
11212
11213     /**
11214      * Calls the specified function for each of the Records in the cache.
11215      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11216      * Returning <em>false</em> aborts and exits the iteration.
11217      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11218      */
11219     each : function(fn, scope){
11220         this.data.each(fn, scope);
11221     },
11222
11223     /**
11224      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11225      * (e.g., during paging).
11226      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11227      */
11228     getModifiedRecords : function(){
11229         return this.modified;
11230     },
11231
11232     // private
11233     createFilterFn : function(property, value, anyMatch){
11234         if(!value.exec){ // not a regex
11235             value = String(value);
11236             if(value.length == 0){
11237                 return false;
11238             }
11239             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11240         }
11241         return function(r){
11242             return value.test(r.data[property]);
11243         };
11244     },
11245
11246     /**
11247      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11248      * @param {String} property A field on your records
11249      * @param {Number} start The record index to start at (defaults to 0)
11250      * @param {Number} end The last record index to include (defaults to length - 1)
11251      * @return {Number} The sum
11252      */
11253     sum : function(property, start, end){
11254         var rs = this.data.items, v = 0;
11255         start = start || 0;
11256         end = (end || end === 0) ? end : rs.length-1;
11257
11258         for(var i = start; i <= end; i++){
11259             v += (rs[i].data[property] || 0);
11260         }
11261         return v;
11262     },
11263
11264     /**
11265      * Filter the records by a specified property.
11266      * @param {String} field A field on your records
11267      * @param {String/RegExp} value Either a string that the field
11268      * should start with or a RegExp to test against the field
11269      * @param {Boolean} anyMatch True to match any part not just the beginning
11270      */
11271     filter : function(property, value, anyMatch){
11272         var fn = this.createFilterFn(property, value, anyMatch);
11273         return fn ? this.filterBy(fn) : this.clearFilter();
11274     },
11275
11276     /**
11277      * Filter by a function. The specified function will be called with each
11278      * record in this data source. If the function returns true the record is included,
11279      * otherwise it is filtered.
11280      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11281      * @param {Object} scope (optional) The scope of the function (defaults to this)
11282      */
11283     filterBy : function(fn, scope){
11284         this.snapshot = this.snapshot || this.data;
11285         this.data = this.queryBy(fn, scope||this);
11286         this.fireEvent("datachanged", this);
11287     },
11288
11289     /**
11290      * Query the records by a specified property.
11291      * @param {String} field A field on your records
11292      * @param {String/RegExp} value Either a string that the field
11293      * should start with or a RegExp to test against the field
11294      * @param {Boolean} anyMatch True to match any part not just the beginning
11295      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11296      */
11297     query : function(property, value, anyMatch){
11298         var fn = this.createFilterFn(property, value, anyMatch);
11299         return fn ? this.queryBy(fn) : this.data.clone();
11300     },
11301
11302     /**
11303      * Query by a function. The specified function will be called with each
11304      * record in this data source. If the function returns true the record is included
11305      * in the results.
11306      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11307      * @param {Object} scope (optional) The scope of the function (defaults to this)
11308       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11309      **/
11310     queryBy : function(fn, scope){
11311         var data = this.snapshot || this.data;
11312         return data.filterBy(fn, scope||this);
11313     },
11314
11315     /**
11316      * Collects unique values for a particular dataIndex from this store.
11317      * @param {String} dataIndex The property to collect
11318      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11319      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11320      * @return {Array} An array of the unique values
11321      **/
11322     collect : function(dataIndex, allowNull, bypassFilter){
11323         var d = (bypassFilter === true && this.snapshot) ?
11324                 this.snapshot.items : this.data.items;
11325         var v, sv, r = [], l = {};
11326         for(var i = 0, len = d.length; i < len; i++){
11327             v = d[i].data[dataIndex];
11328             sv = String(v);
11329             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11330                 l[sv] = true;
11331                 r[r.length] = v;
11332             }
11333         }
11334         return r;
11335     },
11336
11337     /**
11338      * Revert to a view of the Record cache with no filtering applied.
11339      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11340      */
11341     clearFilter : function(suppressEvent){
11342         if(this.snapshot && this.snapshot != this.data){
11343             this.data = this.snapshot;
11344             delete this.snapshot;
11345             if(suppressEvent !== true){
11346                 this.fireEvent("datachanged", this);
11347             }
11348         }
11349     },
11350
11351     // private
11352     afterEdit : function(record){
11353         if(this.modified.indexOf(record) == -1){
11354             this.modified.push(record);
11355         }
11356         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11357     },
11358     
11359     // private
11360     afterReject : function(record){
11361         this.modified.remove(record);
11362         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11363     },
11364
11365     // private
11366     afterCommit : function(record){
11367         this.modified.remove(record);
11368         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11369     },
11370
11371     /**
11372      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11373      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11374      */
11375     commitChanges : function(){
11376         var m = this.modified.slice(0);
11377         this.modified = [];
11378         for(var i = 0, len = m.length; i < len; i++){
11379             m[i].commit();
11380         }
11381     },
11382
11383     /**
11384      * Cancel outstanding changes on all changed records.
11385      */
11386     rejectChanges : function(){
11387         var m = this.modified.slice(0);
11388         this.modified = [];
11389         for(var i = 0, len = m.length; i < len; i++){
11390             m[i].reject();
11391         }
11392     },
11393
11394     onMetaChange : function(meta, rtype, o){
11395         this.recordType = rtype;
11396         this.fields = rtype.prototype.fields;
11397         delete this.snapshot;
11398         this.sortInfo = meta.sortInfo || this.sortInfo;
11399         this.modified = [];
11400         this.fireEvent('metachange', this, this.reader.meta);
11401     },
11402     
11403     moveIndex : function(data, type)
11404     {
11405         var index = this.indexOf(data);
11406         
11407         var newIndex = index + type;
11408         
11409         this.remove(data);
11410         
11411         this.insert(newIndex, data);
11412         
11413     }
11414 });/*
11415  * Based on:
11416  * Ext JS Library 1.1.1
11417  * Copyright(c) 2006-2007, Ext JS, LLC.
11418  *
11419  * Originally Released Under LGPL - original licence link has changed is not relivant.
11420  *
11421  * Fork - LGPL
11422  * <script type="text/javascript">
11423  */
11424
11425 /**
11426  * @class Roo.data.SimpleStore
11427  * @extends Roo.data.Store
11428  * Small helper class to make creating Stores from Array data easier.
11429  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11430  * @cfg {Array} fields An array of field definition objects, or field name strings.
11431  * @cfg {Array} data The multi-dimensional array of data
11432  * @constructor
11433  * @param {Object} config
11434  */
11435 Roo.data.SimpleStore = function(config){
11436     Roo.data.SimpleStore.superclass.constructor.call(this, {
11437         isLocal : true,
11438         reader: new Roo.data.ArrayReader({
11439                 id: config.id
11440             },
11441             Roo.data.Record.create(config.fields)
11442         ),
11443         proxy : new Roo.data.MemoryProxy(config.data)
11444     });
11445     this.load();
11446 };
11447 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11448  * Based on:
11449  * Ext JS Library 1.1.1
11450  * Copyright(c) 2006-2007, Ext JS, LLC.
11451  *
11452  * Originally Released Under LGPL - original licence link has changed is not relivant.
11453  *
11454  * Fork - LGPL
11455  * <script type="text/javascript">
11456  */
11457
11458 /**
11459 /**
11460  * @extends Roo.data.Store
11461  * @class Roo.data.JsonStore
11462  * Small helper class to make creating Stores for JSON data easier. <br/>
11463 <pre><code>
11464 var store = new Roo.data.JsonStore({
11465     url: 'get-images.php',
11466     root: 'images',
11467     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11468 });
11469 </code></pre>
11470  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11471  * JsonReader and HttpProxy (unless inline data is provided).</b>
11472  * @cfg {Array} fields An array of field definition objects, or field name strings.
11473  * @constructor
11474  * @param {Object} config
11475  */
11476 Roo.data.JsonStore = function(c){
11477     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11478         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11479         reader: new Roo.data.JsonReader(c, c.fields)
11480     }));
11481 };
11482 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11483  * Based on:
11484  * Ext JS Library 1.1.1
11485  * Copyright(c) 2006-2007, Ext JS, LLC.
11486  *
11487  * Originally Released Under LGPL - original licence link has changed is not relivant.
11488  *
11489  * Fork - LGPL
11490  * <script type="text/javascript">
11491  */
11492
11493  
11494 Roo.data.Field = function(config){
11495     if(typeof config == "string"){
11496         config = {name: config};
11497     }
11498     Roo.apply(this, config);
11499     
11500     if(!this.type){
11501         this.type = "auto";
11502     }
11503     
11504     var st = Roo.data.SortTypes;
11505     // named sortTypes are supported, here we look them up
11506     if(typeof this.sortType == "string"){
11507         this.sortType = st[this.sortType];
11508     }
11509     
11510     // set default sortType for strings and dates
11511     if(!this.sortType){
11512         switch(this.type){
11513             case "string":
11514                 this.sortType = st.asUCString;
11515                 break;
11516             case "date":
11517                 this.sortType = st.asDate;
11518                 break;
11519             default:
11520                 this.sortType = st.none;
11521         }
11522     }
11523
11524     // define once
11525     var stripRe = /[\$,%]/g;
11526
11527     // prebuilt conversion function for this field, instead of
11528     // switching every time we're reading a value
11529     if(!this.convert){
11530         var cv, dateFormat = this.dateFormat;
11531         switch(this.type){
11532             case "":
11533             case "auto":
11534             case undefined:
11535                 cv = function(v){ return v; };
11536                 break;
11537             case "string":
11538                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11539                 break;
11540             case "int":
11541                 cv = function(v){
11542                     return v !== undefined && v !== null && v !== '' ?
11543                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11544                     };
11545                 break;
11546             case "float":
11547                 cv = function(v){
11548                     return v !== undefined && v !== null && v !== '' ?
11549                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11550                     };
11551                 break;
11552             case "bool":
11553             case "boolean":
11554                 cv = function(v){ return v === true || v === "true" || v == 1; };
11555                 break;
11556             case "date":
11557                 cv = function(v){
11558                     if(!v){
11559                         return '';
11560                     }
11561                     if(v instanceof Date){
11562                         return v;
11563                     }
11564                     if(dateFormat){
11565                         if(dateFormat == "timestamp"){
11566                             return new Date(v*1000);
11567                         }
11568                         return Date.parseDate(v, dateFormat);
11569                     }
11570                     var parsed = Date.parse(v);
11571                     return parsed ? new Date(parsed) : null;
11572                 };
11573              break;
11574             
11575         }
11576         this.convert = cv;
11577     }
11578 };
11579
11580 Roo.data.Field.prototype = {
11581     dateFormat: null,
11582     defaultValue: "",
11583     mapping: null,
11584     sortType : null,
11585     sortDir : "ASC"
11586 };/*
11587  * Based on:
11588  * Ext JS Library 1.1.1
11589  * Copyright(c) 2006-2007, Ext JS, LLC.
11590  *
11591  * Originally Released Under LGPL - original licence link has changed is not relivant.
11592  *
11593  * Fork - LGPL
11594  * <script type="text/javascript">
11595  */
11596  
11597 // Base class for reading structured data from a data source.  This class is intended to be
11598 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11599
11600 /**
11601  * @class Roo.data.DataReader
11602  * Base class for reading structured data from a data source.  This class is intended to be
11603  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11604  */
11605
11606 Roo.data.DataReader = function(meta, recordType){
11607     
11608     this.meta = meta;
11609     
11610     this.recordType = recordType instanceof Array ? 
11611         Roo.data.Record.create(recordType) : recordType;
11612 };
11613
11614 Roo.data.DataReader.prototype = {
11615      /**
11616      * Create an empty record
11617      * @param {Object} data (optional) - overlay some values
11618      * @return {Roo.data.Record} record created.
11619      */
11620     newRow :  function(d) {
11621         var da =  {};
11622         this.recordType.prototype.fields.each(function(c) {
11623             switch( c.type) {
11624                 case 'int' : da[c.name] = 0; break;
11625                 case 'date' : da[c.name] = new Date(); break;
11626                 case 'float' : da[c.name] = 0.0; break;
11627                 case 'boolean' : da[c.name] = false; break;
11628                 default : da[c.name] = ""; break;
11629             }
11630             
11631         });
11632         return new this.recordType(Roo.apply(da, d));
11633     }
11634     
11635 };/*
11636  * Based on:
11637  * Ext JS Library 1.1.1
11638  * Copyright(c) 2006-2007, Ext JS, LLC.
11639  *
11640  * Originally Released Under LGPL - original licence link has changed is not relivant.
11641  *
11642  * Fork - LGPL
11643  * <script type="text/javascript">
11644  */
11645
11646 /**
11647  * @class Roo.data.DataProxy
11648  * @extends Roo.data.Observable
11649  * This class is an abstract base class for implementations which provide retrieval of
11650  * unformatted data objects.<br>
11651  * <p>
11652  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11653  * (of the appropriate type which knows how to parse the data object) to provide a block of
11654  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11655  * <p>
11656  * Custom implementations must implement the load method as described in
11657  * {@link Roo.data.HttpProxy#load}.
11658  */
11659 Roo.data.DataProxy = function(){
11660     this.addEvents({
11661         /**
11662          * @event beforeload
11663          * Fires before a network request is made to retrieve a data object.
11664          * @param {Object} This DataProxy object.
11665          * @param {Object} params The params parameter to the load function.
11666          */
11667         beforeload : true,
11668         /**
11669          * @event load
11670          * Fires before the load method's callback is called.
11671          * @param {Object} This DataProxy object.
11672          * @param {Object} o The data object.
11673          * @param {Object} arg The callback argument object passed to the load function.
11674          */
11675         load : true,
11676         /**
11677          * @event loadexception
11678          * Fires if an Exception occurs during data retrieval.
11679          * @param {Object} This DataProxy object.
11680          * @param {Object} o The data object.
11681          * @param {Object} arg The callback argument object passed to the load function.
11682          * @param {Object} e The Exception.
11683          */
11684         loadexception : true
11685     });
11686     Roo.data.DataProxy.superclass.constructor.call(this);
11687 };
11688
11689 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11690
11691     /**
11692      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11693      */
11694 /*
11695  * Based on:
11696  * Ext JS Library 1.1.1
11697  * Copyright(c) 2006-2007, Ext JS, LLC.
11698  *
11699  * Originally Released Under LGPL - original licence link has changed is not relivant.
11700  *
11701  * Fork - LGPL
11702  * <script type="text/javascript">
11703  */
11704 /**
11705  * @class Roo.data.MemoryProxy
11706  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11707  * to the Reader when its load method is called.
11708  * @constructor
11709  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11710  */
11711 Roo.data.MemoryProxy = function(data){
11712     if (data.data) {
11713         data = data.data;
11714     }
11715     Roo.data.MemoryProxy.superclass.constructor.call(this);
11716     this.data = data;
11717 };
11718
11719 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11720     
11721     /**
11722      * Load data from the requested source (in this case an in-memory
11723      * data object passed to the constructor), read the data object into
11724      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11725      * process that block using the passed callback.
11726      * @param {Object} params This parameter is not used by the MemoryProxy class.
11727      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11728      * object into a block of Roo.data.Records.
11729      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11730      * The function must be passed <ul>
11731      * <li>The Record block object</li>
11732      * <li>The "arg" argument from the load function</li>
11733      * <li>A boolean success indicator</li>
11734      * </ul>
11735      * @param {Object} scope The scope in which to call the callback
11736      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11737      */
11738     load : function(params, reader, callback, scope, arg){
11739         params = params || {};
11740         var result;
11741         try {
11742             result = reader.readRecords(this.data);
11743         }catch(e){
11744             this.fireEvent("loadexception", this, arg, null, e);
11745             callback.call(scope, null, arg, false);
11746             return;
11747         }
11748         callback.call(scope, result, arg, true);
11749     },
11750     
11751     // private
11752     update : function(params, records){
11753         
11754     }
11755 });/*
11756  * Based on:
11757  * Ext JS Library 1.1.1
11758  * Copyright(c) 2006-2007, Ext JS, LLC.
11759  *
11760  * Originally Released Under LGPL - original licence link has changed is not relivant.
11761  *
11762  * Fork - LGPL
11763  * <script type="text/javascript">
11764  */
11765 /**
11766  * @class Roo.data.HttpProxy
11767  * @extends Roo.data.DataProxy
11768  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11769  * configured to reference a certain URL.<br><br>
11770  * <p>
11771  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11772  * from which the running page was served.<br><br>
11773  * <p>
11774  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11775  * <p>
11776  * Be aware that to enable the browser to parse an XML document, the server must set
11777  * the Content-Type header in the HTTP response to "text/xml".
11778  * @constructor
11779  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11780  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11781  * will be used to make the request.
11782  */
11783 Roo.data.HttpProxy = function(conn){
11784     Roo.data.HttpProxy.superclass.constructor.call(this);
11785     // is conn a conn config or a real conn?
11786     this.conn = conn;
11787     this.useAjax = !conn || !conn.events;
11788   
11789 };
11790
11791 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11792     // thse are take from connection...
11793     
11794     /**
11795      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11796      */
11797     /**
11798      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11799      * extra parameters to each request made by this object. (defaults to undefined)
11800      */
11801     /**
11802      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11803      *  to each request made by this object. (defaults to undefined)
11804      */
11805     /**
11806      * @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)
11807      */
11808     /**
11809      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11810      */
11811      /**
11812      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11813      * @type Boolean
11814      */
11815   
11816
11817     /**
11818      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11819      * @type Boolean
11820      */
11821     /**
11822      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11823      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11824      * a finer-grained basis than the DataProxy events.
11825      */
11826     getConnection : function(){
11827         return this.useAjax ? Roo.Ajax : this.conn;
11828     },
11829
11830     /**
11831      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11832      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11833      * process that block using the passed callback.
11834      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11835      * for the request to the remote server.
11836      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11837      * object into a block of Roo.data.Records.
11838      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11839      * The function must be passed <ul>
11840      * <li>The Record block object</li>
11841      * <li>The "arg" argument from the load function</li>
11842      * <li>A boolean success indicator</li>
11843      * </ul>
11844      * @param {Object} scope The scope in which to call the callback
11845      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11846      */
11847     load : function(params, reader, callback, scope, arg){
11848         if(this.fireEvent("beforeload", this, params) !== false){
11849             var  o = {
11850                 params : params || {},
11851                 request: {
11852                     callback : callback,
11853                     scope : scope,
11854                     arg : arg
11855                 },
11856                 reader: reader,
11857                 callback : this.loadResponse,
11858                 scope: this
11859             };
11860             if(this.useAjax){
11861                 Roo.applyIf(o, this.conn);
11862                 if(this.activeRequest){
11863                     Roo.Ajax.abort(this.activeRequest);
11864                 }
11865                 this.activeRequest = Roo.Ajax.request(o);
11866             }else{
11867                 this.conn.request(o);
11868             }
11869         }else{
11870             callback.call(scope||this, null, arg, false);
11871         }
11872     },
11873
11874     // private
11875     loadResponse : function(o, success, response){
11876         delete this.activeRequest;
11877         if(!success){
11878             this.fireEvent("loadexception", this, o, response);
11879             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11880             return;
11881         }
11882         var result;
11883         try {
11884             result = o.reader.read(response);
11885         }catch(e){
11886             this.fireEvent("loadexception", this, o, response, e);
11887             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11888             return;
11889         }
11890         
11891         this.fireEvent("load", this, o, o.request.arg);
11892         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11893     },
11894
11895     // private
11896     update : function(dataSet){
11897
11898     },
11899
11900     // private
11901     updateResponse : function(dataSet){
11902
11903     }
11904 });/*
11905  * Based on:
11906  * Ext JS Library 1.1.1
11907  * Copyright(c) 2006-2007, Ext JS, LLC.
11908  *
11909  * Originally Released Under LGPL - original licence link has changed is not relivant.
11910  *
11911  * Fork - LGPL
11912  * <script type="text/javascript">
11913  */
11914
11915 /**
11916  * @class Roo.data.ScriptTagProxy
11917  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11918  * other than the originating domain of the running page.<br><br>
11919  * <p>
11920  * <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
11921  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11922  * <p>
11923  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11924  * source code that is used as the source inside a &lt;script> tag.<br><br>
11925  * <p>
11926  * In order for the browser to process the returned data, the server must wrap the data object
11927  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11928  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11929  * depending on whether the callback name was passed:
11930  * <p>
11931  * <pre><code>
11932 boolean scriptTag = false;
11933 String cb = request.getParameter("callback");
11934 if (cb != null) {
11935     scriptTag = true;
11936     response.setContentType("text/javascript");
11937 } else {
11938     response.setContentType("application/x-json");
11939 }
11940 Writer out = response.getWriter();
11941 if (scriptTag) {
11942     out.write(cb + "(");
11943 }
11944 out.print(dataBlock.toJsonString());
11945 if (scriptTag) {
11946     out.write(");");
11947 }
11948 </pre></code>
11949  *
11950  * @constructor
11951  * @param {Object} config A configuration object.
11952  */
11953 Roo.data.ScriptTagProxy = function(config){
11954     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11955     Roo.apply(this, config);
11956     this.head = document.getElementsByTagName("head")[0];
11957 };
11958
11959 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11960
11961 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11962     /**
11963      * @cfg {String} url The URL from which to request the data object.
11964      */
11965     /**
11966      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11967      */
11968     timeout : 30000,
11969     /**
11970      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11971      * the server the name of the callback function set up by the load call to process the returned data object.
11972      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11973      * javascript output which calls this named function passing the data object as its only parameter.
11974      */
11975     callbackParam : "callback",
11976     /**
11977      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11978      * name to the request.
11979      */
11980     nocache : true,
11981
11982     /**
11983      * Load data from the configured URL, read the data object into
11984      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11985      * process that block using the passed callback.
11986      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11987      * for the request to the remote server.
11988      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11989      * object into a block of Roo.data.Records.
11990      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11991      * The function must be passed <ul>
11992      * <li>The Record block object</li>
11993      * <li>The "arg" argument from the load function</li>
11994      * <li>A boolean success indicator</li>
11995      * </ul>
11996      * @param {Object} scope The scope in which to call the callback
11997      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11998      */
11999     load : function(params, reader, callback, scope, arg){
12000         if(this.fireEvent("beforeload", this, params) !== false){
12001
12002             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12003
12004             var url = this.url;
12005             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12006             if(this.nocache){
12007                 url += "&_dc=" + (new Date().getTime());
12008             }
12009             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12010             var trans = {
12011                 id : transId,
12012                 cb : "stcCallback"+transId,
12013                 scriptId : "stcScript"+transId,
12014                 params : params,
12015                 arg : arg,
12016                 url : url,
12017                 callback : callback,
12018                 scope : scope,
12019                 reader : reader
12020             };
12021             var conn = this;
12022
12023             window[trans.cb] = function(o){
12024                 conn.handleResponse(o, trans);
12025             };
12026
12027             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12028
12029             if(this.autoAbort !== false){
12030                 this.abort();
12031             }
12032
12033             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12034
12035             var script = document.createElement("script");
12036             script.setAttribute("src", url);
12037             script.setAttribute("type", "text/javascript");
12038             script.setAttribute("id", trans.scriptId);
12039             this.head.appendChild(script);
12040
12041             this.trans = trans;
12042         }else{
12043             callback.call(scope||this, null, arg, false);
12044         }
12045     },
12046
12047     // private
12048     isLoading : function(){
12049         return this.trans ? true : false;
12050     },
12051
12052     /**
12053      * Abort the current server request.
12054      */
12055     abort : function(){
12056         if(this.isLoading()){
12057             this.destroyTrans(this.trans);
12058         }
12059     },
12060
12061     // private
12062     destroyTrans : function(trans, isLoaded){
12063         this.head.removeChild(document.getElementById(trans.scriptId));
12064         clearTimeout(trans.timeoutId);
12065         if(isLoaded){
12066             window[trans.cb] = undefined;
12067             try{
12068                 delete window[trans.cb];
12069             }catch(e){}
12070         }else{
12071             // if hasn't been loaded, wait for load to remove it to prevent script error
12072             window[trans.cb] = function(){
12073                 window[trans.cb] = undefined;
12074                 try{
12075                     delete window[trans.cb];
12076                 }catch(e){}
12077             };
12078         }
12079     },
12080
12081     // private
12082     handleResponse : function(o, trans){
12083         this.trans = false;
12084         this.destroyTrans(trans, true);
12085         var result;
12086         try {
12087             result = trans.reader.readRecords(o);
12088         }catch(e){
12089             this.fireEvent("loadexception", this, o, trans.arg, e);
12090             trans.callback.call(trans.scope||window, null, trans.arg, false);
12091             return;
12092         }
12093         this.fireEvent("load", this, o, trans.arg);
12094         trans.callback.call(trans.scope||window, result, trans.arg, true);
12095     },
12096
12097     // private
12098     handleFailure : function(trans){
12099         this.trans = false;
12100         this.destroyTrans(trans, false);
12101         this.fireEvent("loadexception", this, null, trans.arg);
12102         trans.callback.call(trans.scope||window, null, trans.arg, false);
12103     }
12104 });/*
12105  * Based on:
12106  * Ext JS Library 1.1.1
12107  * Copyright(c) 2006-2007, Ext JS, LLC.
12108  *
12109  * Originally Released Under LGPL - original licence link has changed is not relivant.
12110  *
12111  * Fork - LGPL
12112  * <script type="text/javascript">
12113  */
12114
12115 /**
12116  * @class Roo.data.JsonReader
12117  * @extends Roo.data.DataReader
12118  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12119  * based on mappings in a provided Roo.data.Record constructor.
12120  * 
12121  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12122  * in the reply previously. 
12123  * 
12124  * <p>
12125  * Example code:
12126  * <pre><code>
12127 var RecordDef = Roo.data.Record.create([
12128     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12129     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12130 ]);
12131 var myReader = new Roo.data.JsonReader({
12132     totalProperty: "results",    // The property which contains the total dataset size (optional)
12133     root: "rows",                // The property which contains an Array of row objects
12134     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12135 }, RecordDef);
12136 </code></pre>
12137  * <p>
12138  * This would consume a JSON file like this:
12139  * <pre><code>
12140 { 'results': 2, 'rows': [
12141     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12142     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12143 }
12144 </code></pre>
12145  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12146  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12147  * paged from the remote server.
12148  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12149  * @cfg {String} root name of the property which contains the Array of row objects.
12150  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12151  * @cfg {Array} fields Array of field definition objects
12152  * @constructor
12153  * Create a new JsonReader
12154  * @param {Object} meta Metadata configuration options
12155  * @param {Object} recordType Either an Array of field definition objects,
12156  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12157  */
12158 Roo.data.JsonReader = function(meta, recordType){
12159     
12160     meta = meta || {};
12161     // set some defaults:
12162     Roo.applyIf(meta, {
12163         totalProperty: 'total',
12164         successProperty : 'success',
12165         root : 'data',
12166         id : 'id'
12167     });
12168     
12169     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12170 };
12171 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12172     
12173     /**
12174      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12175      * Used by Store query builder to append _requestMeta to params.
12176      * 
12177      */
12178     metaFromRemote : false,
12179     /**
12180      * This method is only used by a DataProxy which has retrieved data from a remote server.
12181      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12182      * @return {Object} data A data block which is used by an Roo.data.Store object as
12183      * a cache of Roo.data.Records.
12184      */
12185     read : function(response){
12186         var json = response.responseText;
12187        
12188         var o = /* eval:var:o */ eval("("+json+")");
12189         if(!o) {
12190             throw {message: "JsonReader.read: Json object not found"};
12191         }
12192         
12193         if(o.metaData){
12194             
12195             delete this.ef;
12196             this.metaFromRemote = true;
12197             this.meta = o.metaData;
12198             this.recordType = Roo.data.Record.create(o.metaData.fields);
12199             this.onMetaChange(this.meta, this.recordType, o);
12200         }
12201         return this.readRecords(o);
12202     },
12203
12204     // private function a store will implement
12205     onMetaChange : function(meta, recordType, o){
12206
12207     },
12208
12209     /**
12210          * @ignore
12211          */
12212     simpleAccess: function(obj, subsc) {
12213         return obj[subsc];
12214     },
12215
12216         /**
12217          * @ignore
12218          */
12219     getJsonAccessor: function(){
12220         var re = /[\[\.]/;
12221         return function(expr) {
12222             try {
12223                 return(re.test(expr))
12224                     ? new Function("obj", "return obj." + expr)
12225                     : function(obj){
12226                         return obj[expr];
12227                     };
12228             } catch(e){}
12229             return Roo.emptyFn;
12230         };
12231     }(),
12232
12233     /**
12234      * Create a data block containing Roo.data.Records from an XML document.
12235      * @param {Object} o An object which contains an Array of row objects in the property specified
12236      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12237      * which contains the total size of the dataset.
12238      * @return {Object} data A data block which is used by an Roo.data.Store object as
12239      * a cache of Roo.data.Records.
12240      */
12241     readRecords : function(o){
12242         /**
12243          * After any data loads, the raw JSON data is available for further custom processing.
12244          * @type Object
12245          */
12246         this.o = o;
12247         var s = this.meta, Record = this.recordType,
12248             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12249
12250 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12251         if (!this.ef) {
12252             if(s.totalProperty) {
12253                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12254                 }
12255                 if(s.successProperty) {
12256                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12257                 }
12258                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12259                 if (s.id) {
12260                         var g = this.getJsonAccessor(s.id);
12261                         this.getId = function(rec) {
12262                                 var r = g(rec);  
12263                                 return (r === undefined || r === "") ? null : r;
12264                         };
12265                 } else {
12266                         this.getId = function(){return null;};
12267                 }
12268             this.ef = [];
12269             for(var jj = 0; jj < fl; jj++){
12270                 f = fi[jj];
12271                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12272                 this.ef[jj] = this.getJsonAccessor(map);
12273             }
12274         }
12275
12276         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12277         if(s.totalProperty){
12278             var vt = parseInt(this.getTotal(o), 10);
12279             if(!isNaN(vt)){
12280                 totalRecords = vt;
12281             }
12282         }
12283         if(s.successProperty){
12284             var vs = this.getSuccess(o);
12285             if(vs === false || vs === 'false'){
12286                 success = false;
12287             }
12288         }
12289         var records = [];
12290         for(var i = 0; i < c; i++){
12291                 var n = root[i];
12292             var values = {};
12293             var id = this.getId(n);
12294             for(var j = 0; j < fl; j++){
12295                 f = fi[j];
12296             var v = this.ef[j](n);
12297             if (!f.convert) {
12298                 Roo.log('missing convert for ' + f.name);
12299                 Roo.log(f);
12300                 continue;
12301             }
12302             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12303             }
12304             var record = new Record(values, id);
12305             record.json = n;
12306             records[i] = record;
12307         }
12308         return {
12309             raw : o,
12310             success : success,
12311             records : records,
12312             totalRecords : totalRecords
12313         };
12314     }
12315 });/*
12316  * Based on:
12317  * Ext JS Library 1.1.1
12318  * Copyright(c) 2006-2007, Ext JS, LLC.
12319  *
12320  * Originally Released Under LGPL - original licence link has changed is not relivant.
12321  *
12322  * Fork - LGPL
12323  * <script type="text/javascript">
12324  */
12325
12326 /**
12327  * @class Roo.data.ArrayReader
12328  * @extends Roo.data.DataReader
12329  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12330  * Each element of that Array represents a row of data fields. The
12331  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12332  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12333  * <p>
12334  * Example code:.
12335  * <pre><code>
12336 var RecordDef = Roo.data.Record.create([
12337     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12338     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12339 ]);
12340 var myReader = new Roo.data.ArrayReader({
12341     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12342 }, RecordDef);
12343 </code></pre>
12344  * <p>
12345  * This would consume an Array like this:
12346  * <pre><code>
12347 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12348   </code></pre>
12349  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12350  * @constructor
12351  * Create a new JsonReader
12352  * @param {Object} meta Metadata configuration options.
12353  * @param {Object} recordType Either an Array of field definition objects
12354  * as specified to {@link Roo.data.Record#create},
12355  * or an {@link Roo.data.Record} object
12356  * created using {@link Roo.data.Record#create}.
12357  */
12358 Roo.data.ArrayReader = function(meta, recordType){
12359     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12360 };
12361
12362 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12363     /**
12364      * Create a data block containing Roo.data.Records from an XML document.
12365      * @param {Object} o An Array of row objects which represents the dataset.
12366      * @return {Object} data A data block which is used by an Roo.data.Store object as
12367      * a cache of Roo.data.Records.
12368      */
12369     readRecords : function(o){
12370         var sid = this.meta ? this.meta.id : null;
12371         var recordType = this.recordType, fields = recordType.prototype.fields;
12372         var records = [];
12373         var root = o;
12374             for(var i = 0; i < root.length; i++){
12375                     var n = root[i];
12376                 var values = {};
12377                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12378                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12379                 var f = fields.items[j];
12380                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12381                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12382                 v = f.convert(v);
12383                 values[f.name] = v;
12384             }
12385                 var record = new recordType(values, id);
12386                 record.json = n;
12387                 records[records.length] = record;
12388             }
12389             return {
12390                 records : records,
12391                 totalRecords : records.length
12392             };
12393     }
12394 });/*
12395  * - LGPL
12396  * * 
12397  */
12398
12399 /**
12400  * @class Roo.bootstrap.ComboBox
12401  * @extends Roo.bootstrap.TriggerField
12402  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12403  * @cfg {Boolean} append (true|false) default false
12404  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12405  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12406  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12407  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12408  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12409  * @cfg {Boolean} animate default true
12410  * @cfg {Boolean} emptyResultText only for touch device
12411  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12412  * @cfg {String} emptyTitle default ''
12413  * @constructor
12414  * Create a new ComboBox.
12415  * @param {Object} config Configuration options
12416  */
12417 Roo.bootstrap.ComboBox = function(config){
12418     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12419     this.addEvents({
12420         /**
12421          * @event expand
12422          * Fires when the dropdown list is expanded
12423         * @param {Roo.bootstrap.ComboBox} combo This combo box
12424         */
12425         'expand' : true,
12426         /**
12427          * @event collapse
12428          * Fires when the dropdown list is collapsed
12429         * @param {Roo.bootstrap.ComboBox} combo This combo box
12430         */
12431         'collapse' : true,
12432         /**
12433          * @event beforeselect
12434          * Fires before a list item is selected. Return false to cancel the selection.
12435         * @param {Roo.bootstrap.ComboBox} combo This combo box
12436         * @param {Roo.data.Record} record The data record returned from the underlying store
12437         * @param {Number} index The index of the selected item in the dropdown list
12438         */
12439         'beforeselect' : true,
12440         /**
12441          * @event select
12442          * Fires when a list item is selected
12443         * @param {Roo.bootstrap.ComboBox} combo This combo box
12444         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12445         * @param {Number} index The index of the selected item in the dropdown list
12446         */
12447         'select' : true,
12448         /**
12449          * @event beforequery
12450          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12451          * The event object passed has these properties:
12452         * @param {Roo.bootstrap.ComboBox} combo This combo box
12453         * @param {String} query The query
12454         * @param {Boolean} forceAll true to force "all" query
12455         * @param {Boolean} cancel true to cancel the query
12456         * @param {Object} e The query event object
12457         */
12458         'beforequery': true,
12459          /**
12460          * @event add
12461          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12462         * @param {Roo.bootstrap.ComboBox} combo This combo box
12463         */
12464         'add' : true,
12465         /**
12466          * @event edit
12467          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12468         * @param {Roo.bootstrap.ComboBox} combo This combo box
12469         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12470         */
12471         'edit' : true,
12472         /**
12473          * @event remove
12474          * Fires when the remove value from the combobox array
12475         * @param {Roo.bootstrap.ComboBox} combo This combo box
12476         */
12477         'remove' : true,
12478         /**
12479          * @event afterremove
12480          * Fires when the remove value from the combobox array
12481         * @param {Roo.bootstrap.ComboBox} combo This combo box
12482         */
12483         'afterremove' : true,
12484         /**
12485          * @event specialfilter
12486          * Fires when specialfilter
12487             * @param {Roo.bootstrap.ComboBox} combo This combo box
12488             */
12489         'specialfilter' : true,
12490         /**
12491          * @event tick
12492          * Fires when tick the element
12493             * @param {Roo.bootstrap.ComboBox} combo This combo box
12494             */
12495         'tick' : true,
12496         /**
12497          * @event touchviewdisplay
12498          * Fires when touch view require special display (default is using displayField)
12499             * @param {Roo.bootstrap.ComboBox} combo This combo box
12500             * @param {Object} cfg set html .
12501             */
12502         'touchviewdisplay' : true
12503         
12504     });
12505     
12506     this.item = [];
12507     this.tickItems = [];
12508     
12509     this.selectedIndex = -1;
12510     if(this.mode == 'local'){
12511         if(config.queryDelay === undefined){
12512             this.queryDelay = 10;
12513         }
12514         if(config.minChars === undefined){
12515             this.minChars = 0;
12516         }
12517     }
12518 };
12519
12520 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12521      
12522     /**
12523      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12524      * rendering into an Roo.Editor, defaults to false)
12525      */
12526     /**
12527      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12528      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12529      */
12530     /**
12531      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12532      */
12533     /**
12534      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12535      * the dropdown list (defaults to undefined, with no header element)
12536      */
12537
12538      /**
12539      * @cfg {String/Roo.Template} tpl The template to use to render the output
12540      */
12541      
12542      /**
12543      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12544      */
12545     listWidth: undefined,
12546     /**
12547      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12548      * mode = 'remote' or 'text' if mode = 'local')
12549      */
12550     displayField: undefined,
12551     
12552     /**
12553      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12554      * mode = 'remote' or 'value' if mode = 'local'). 
12555      * Note: use of a valueField requires the user make a selection
12556      * in order for a value to be mapped.
12557      */
12558     valueField: undefined,
12559     /**
12560      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12561      */
12562     modalTitle : '',
12563     
12564     /**
12565      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12566      * field's data value (defaults to the underlying DOM element's name)
12567      */
12568     hiddenName: undefined,
12569     /**
12570      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12571      */
12572     listClass: '',
12573     /**
12574      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12575      */
12576     selectedClass: 'active',
12577     
12578     /**
12579      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12580      */
12581     shadow:'sides',
12582     /**
12583      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12584      * anchor positions (defaults to 'tl-bl')
12585      */
12586     listAlign: 'tl-bl?',
12587     /**
12588      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12589      */
12590     maxHeight: 300,
12591     /**
12592      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12593      * query specified by the allQuery config option (defaults to 'query')
12594      */
12595     triggerAction: 'query',
12596     /**
12597      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12598      * (defaults to 4, does not apply if editable = false)
12599      */
12600     minChars : 4,
12601     /**
12602      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12603      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12604      */
12605     typeAhead: false,
12606     /**
12607      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12608      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12609      */
12610     queryDelay: 500,
12611     /**
12612      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12613      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12614      */
12615     pageSize: 0,
12616     /**
12617      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12618      * when editable = true (defaults to false)
12619      */
12620     selectOnFocus:false,
12621     /**
12622      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12623      */
12624     queryParam: 'query',
12625     /**
12626      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12627      * when mode = 'remote' (defaults to 'Loading...')
12628      */
12629     loadingText: 'Loading...',
12630     /**
12631      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12632      */
12633     resizable: false,
12634     /**
12635      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12636      */
12637     handleHeight : 8,
12638     /**
12639      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12640      * traditional select (defaults to true)
12641      */
12642     editable: true,
12643     /**
12644      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12645      */
12646     allQuery: '',
12647     /**
12648      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12649      */
12650     mode: 'remote',
12651     /**
12652      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12653      * listWidth has a higher value)
12654      */
12655     minListWidth : 70,
12656     /**
12657      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12658      * allow the user to set arbitrary text into the field (defaults to false)
12659      */
12660     forceSelection:false,
12661     /**
12662      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12663      * if typeAhead = true (defaults to 250)
12664      */
12665     typeAheadDelay : 250,
12666     /**
12667      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12668      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12669      */
12670     valueNotFoundText : undefined,
12671     /**
12672      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12673      */
12674     blockFocus : false,
12675     
12676     /**
12677      * @cfg {Boolean} disableClear Disable showing of clear button.
12678      */
12679     disableClear : false,
12680     /**
12681      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12682      */
12683     alwaysQuery : false,
12684     
12685     /**
12686      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12687      */
12688     multiple : false,
12689     
12690     /**
12691      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12692      */
12693     invalidClass : "has-warning",
12694     
12695     /**
12696      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12697      */
12698     validClass : "has-success",
12699     
12700     /**
12701      * @cfg {Boolean} specialFilter (true|false) special filter default false
12702      */
12703     specialFilter : false,
12704     
12705     /**
12706      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12707      */
12708     mobileTouchView : true,
12709     
12710     /**
12711      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12712      */
12713     useNativeIOS : false,
12714     
12715     ios_options : false,
12716     
12717     //private
12718     addicon : false,
12719     editicon: false,
12720     
12721     page: 0,
12722     hasQuery: false,
12723     append: false,
12724     loadNext: false,
12725     autoFocus : true,
12726     tickable : false,
12727     btnPosition : 'right',
12728     triggerList : true,
12729     showToggleBtn : true,
12730     animate : true,
12731     emptyResultText: 'Empty',
12732     triggerText : 'Select',
12733     emptyTitle : '',
12734     
12735     // element that contains real text value.. (when hidden is used..)
12736     
12737     getAutoCreate : function()
12738     {   
12739         var cfg = false;
12740         //render
12741         /*
12742          * Render classic select for iso
12743          */
12744         
12745         if(Roo.isIOS && this.useNativeIOS){
12746             cfg = this.getAutoCreateNativeIOS();
12747             return cfg;
12748         }
12749         
12750         /*
12751          * Touch Devices
12752          */
12753         
12754         if(Roo.isTouch && this.mobileTouchView){
12755             cfg = this.getAutoCreateTouchView();
12756             return cfg;;
12757         }
12758         
12759         /*
12760          *  Normal ComboBox
12761          */
12762         if(!this.tickable){
12763             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12764             return cfg;
12765         }
12766         
12767         /*
12768          *  ComboBox with tickable selections
12769          */
12770              
12771         var align = this.labelAlign || this.parentLabelAlign();
12772         
12773         cfg = {
12774             cls : 'form-group roo-combobox-tickable' //input-group
12775         };
12776         
12777         var btn_text_select = '';
12778         var btn_text_done = '';
12779         var btn_text_cancel = '';
12780         
12781         if (this.btn_text_show) {
12782             btn_text_select = 'Select';
12783             btn_text_done = 'Done';
12784             btn_text_cancel = 'Cancel'; 
12785         }
12786         
12787         var buttons = {
12788             tag : 'div',
12789             cls : 'tickable-buttons',
12790             cn : [
12791                 {
12792                     tag : 'button',
12793                     type : 'button',
12794                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12795                     //html : this.triggerText
12796                     html: btn_text_select
12797                 },
12798                 {
12799                     tag : 'button',
12800                     type : 'button',
12801                     name : 'ok',
12802                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12803                     //html : 'Done'
12804                     html: btn_text_done
12805                 },
12806                 {
12807                     tag : 'button',
12808                     type : 'button',
12809                     name : 'cancel',
12810                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12811                     //html : 'Cancel'
12812                     html: btn_text_cancel
12813                 }
12814             ]
12815         };
12816         
12817         if(this.editable){
12818             buttons.cn.unshift({
12819                 tag: 'input',
12820                 cls: 'roo-select2-search-field-input'
12821             });
12822         }
12823         
12824         var _this = this;
12825         
12826         Roo.each(buttons.cn, function(c){
12827             if (_this.size) {
12828                 c.cls += ' btn-' + _this.size;
12829             }
12830
12831             if (_this.disabled) {
12832                 c.disabled = true;
12833             }
12834         });
12835         
12836         var box = {
12837             tag: 'div',
12838             cn: [
12839                 {
12840                     tag: 'input',
12841                     type : 'hidden',
12842                     cls: 'form-hidden-field'
12843                 },
12844                 {
12845                     tag: 'ul',
12846                     cls: 'roo-select2-choices',
12847                     cn:[
12848                         {
12849                             tag: 'li',
12850                             cls: 'roo-select2-search-field',
12851                             cn: [
12852                                 buttons
12853                             ]
12854                         }
12855                     ]
12856                 }
12857             ]
12858         };
12859         
12860         var combobox = {
12861             cls: 'roo-select2-container input-group roo-select2-container-multi',
12862             cn: [
12863                 box
12864 //                {
12865 //                    tag: 'ul',
12866 //                    cls: 'typeahead typeahead-long dropdown-menu',
12867 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12868 //                }
12869             ]
12870         };
12871         
12872         if(this.hasFeedback && !this.allowBlank){
12873             
12874             var feedback = {
12875                 tag: 'span',
12876                 cls: 'glyphicon form-control-feedback'
12877             };
12878
12879             combobox.cn.push(feedback);
12880         }
12881         
12882         
12883         if (align ==='left' && this.fieldLabel.length) {
12884             
12885             cfg.cls += ' roo-form-group-label-left';
12886             
12887             cfg.cn = [
12888                 {
12889                     tag : 'i',
12890                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12891                     tooltip : 'This field is required'
12892                 },
12893                 {
12894                     tag: 'label',
12895                     'for' :  id,
12896                     cls : 'control-label',
12897                     html : this.fieldLabel
12898
12899                 },
12900                 {
12901                     cls : "", 
12902                     cn: [
12903                         combobox
12904                     ]
12905                 }
12906
12907             ];
12908             
12909             var labelCfg = cfg.cn[1];
12910             var contentCfg = cfg.cn[2];
12911             
12912
12913             if(this.indicatorpos == 'right'){
12914                 
12915                 cfg.cn = [
12916                     {
12917                         tag: 'label',
12918                         'for' :  id,
12919                         cls : 'control-label',
12920                         cn : [
12921                             {
12922                                 tag : 'span',
12923                                 html : this.fieldLabel
12924                             },
12925                             {
12926                                 tag : 'i',
12927                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12928                                 tooltip : 'This field is required'
12929                             }
12930                         ]
12931                     },
12932                     {
12933                         cls : "",
12934                         cn: [
12935                             combobox
12936                         ]
12937                     }
12938
12939                 ];
12940                 
12941                 
12942                 
12943                 labelCfg = cfg.cn[0];
12944                 contentCfg = cfg.cn[1];
12945             
12946             }
12947             
12948             if(this.labelWidth > 12){
12949                 labelCfg.style = "width: " + this.labelWidth + 'px';
12950             }
12951             
12952             if(this.labelWidth < 13 && this.labelmd == 0){
12953                 this.labelmd = this.labelWidth;
12954             }
12955             
12956             if(this.labellg > 0){
12957                 labelCfg.cls += ' col-lg-' + this.labellg;
12958                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12959             }
12960             
12961             if(this.labelmd > 0){
12962                 labelCfg.cls += ' col-md-' + this.labelmd;
12963                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12964             }
12965             
12966             if(this.labelsm > 0){
12967                 labelCfg.cls += ' col-sm-' + this.labelsm;
12968                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12969             }
12970             
12971             if(this.labelxs > 0){
12972                 labelCfg.cls += ' col-xs-' + this.labelxs;
12973                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12974             }
12975                 
12976                 
12977         } else if ( this.fieldLabel.length) {
12978 //                Roo.log(" label");
12979                  cfg.cn = [
12980                     {
12981                         tag : 'i',
12982                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12983                         tooltip : 'This field is required'
12984                     },
12985                     {
12986                         tag: 'label',
12987                         //cls : 'input-group-addon',
12988                         html : this.fieldLabel
12989                     },
12990                     combobox
12991                 ];
12992                 
12993                 if(this.indicatorpos == 'right'){
12994                     cfg.cn = [
12995                         {
12996                             tag: 'label',
12997                             //cls : 'input-group-addon',
12998                             html : this.fieldLabel
12999                         },
13000                         {
13001                             tag : 'i',
13002                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13003                             tooltip : 'This field is required'
13004                         },
13005                         combobox
13006                     ];
13007                     
13008                 }
13009
13010         } else {
13011             
13012 //                Roo.log(" no label && no align");
13013                 cfg = combobox
13014                      
13015                 
13016         }
13017          
13018         var settings=this;
13019         ['xs','sm','md','lg'].map(function(size){
13020             if (settings[size]) {
13021                 cfg.cls += ' col-' + size + '-' + settings[size];
13022             }
13023         });
13024         
13025         return cfg;
13026         
13027     },
13028     
13029     _initEventsCalled : false,
13030     
13031     // private
13032     initEvents: function()
13033     {   
13034         if (this._initEventsCalled) { // as we call render... prevent looping...
13035             return;
13036         }
13037         this._initEventsCalled = true;
13038         
13039         if (!this.store) {
13040             throw "can not find store for combo";
13041         }
13042         
13043         this.indicator = this.indicatorEl();
13044         
13045         this.store = Roo.factory(this.store, Roo.data);
13046         this.store.parent = this;
13047         
13048         // if we are building from html. then this element is so complex, that we can not really
13049         // use the rendered HTML.
13050         // so we have to trash and replace the previous code.
13051         if (Roo.XComponent.build_from_html) {
13052             // remove this element....
13053             var e = this.el.dom, k=0;
13054             while (e ) { e = e.previousSibling;  ++k;}
13055
13056             this.el.remove();
13057             
13058             this.el=false;
13059             this.rendered = false;
13060             
13061             this.render(this.parent().getChildContainer(true), k);
13062         }
13063         
13064         if(Roo.isIOS && this.useNativeIOS){
13065             this.initIOSView();
13066             return;
13067         }
13068         
13069         /*
13070          * Touch Devices
13071          */
13072         
13073         if(Roo.isTouch && this.mobileTouchView){
13074             this.initTouchView();
13075             return;
13076         }
13077         
13078         if(this.tickable){
13079             this.initTickableEvents();
13080             return;
13081         }
13082         
13083         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13084         
13085         if(this.hiddenName){
13086             
13087             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13088             
13089             this.hiddenField.dom.value =
13090                 this.hiddenValue !== undefined ? this.hiddenValue :
13091                 this.value !== undefined ? this.value : '';
13092
13093             // prevent input submission
13094             this.el.dom.removeAttribute('name');
13095             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13096              
13097              
13098         }
13099         //if(Roo.isGecko){
13100         //    this.el.dom.setAttribute('autocomplete', 'off');
13101         //}
13102         
13103         var cls = 'x-combo-list';
13104         
13105         //this.list = new Roo.Layer({
13106         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13107         //});
13108         
13109         var _this = this;
13110         
13111         (function(){
13112             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13113             _this.list.setWidth(lw);
13114         }).defer(100);
13115         
13116         this.list.on('mouseover', this.onViewOver, this);
13117         this.list.on('mousemove', this.onViewMove, this);
13118         this.list.on('scroll', this.onViewScroll, this);
13119         
13120         /*
13121         this.list.swallowEvent('mousewheel');
13122         this.assetHeight = 0;
13123
13124         if(this.title){
13125             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13126             this.assetHeight += this.header.getHeight();
13127         }
13128
13129         this.innerList = this.list.createChild({cls:cls+'-inner'});
13130         this.innerList.on('mouseover', this.onViewOver, this);
13131         this.innerList.on('mousemove', this.onViewMove, this);
13132         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13133         
13134         if(this.allowBlank && !this.pageSize && !this.disableClear){
13135             this.footer = this.list.createChild({cls:cls+'-ft'});
13136             this.pageTb = new Roo.Toolbar(this.footer);
13137            
13138         }
13139         if(this.pageSize){
13140             this.footer = this.list.createChild({cls:cls+'-ft'});
13141             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13142                     {pageSize: this.pageSize});
13143             
13144         }
13145         
13146         if (this.pageTb && this.allowBlank && !this.disableClear) {
13147             var _this = this;
13148             this.pageTb.add(new Roo.Toolbar.Fill(), {
13149                 cls: 'x-btn-icon x-btn-clear',
13150                 text: '&#160;',
13151                 handler: function()
13152                 {
13153                     _this.collapse();
13154                     _this.clearValue();
13155                     _this.onSelect(false, -1);
13156                 }
13157             });
13158         }
13159         if (this.footer) {
13160             this.assetHeight += this.footer.getHeight();
13161         }
13162         */
13163             
13164         if(!this.tpl){
13165             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13166         }
13167
13168         this.view = new Roo.View(this.list, this.tpl, {
13169             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13170         });
13171         //this.view.wrapEl.setDisplayed(false);
13172         this.view.on('click', this.onViewClick, this);
13173         
13174         
13175         this.store.on('beforeload', this.onBeforeLoad, this);
13176         this.store.on('load', this.onLoad, this);
13177         this.store.on('loadexception', this.onLoadException, this);
13178         /*
13179         if(this.resizable){
13180             this.resizer = new Roo.Resizable(this.list,  {
13181                pinned:true, handles:'se'
13182             });
13183             this.resizer.on('resize', function(r, w, h){
13184                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13185                 this.listWidth = w;
13186                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13187                 this.restrictHeight();
13188             }, this);
13189             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13190         }
13191         */
13192         if(!this.editable){
13193             this.editable = true;
13194             this.setEditable(false);
13195         }
13196         
13197         /*
13198         
13199         if (typeof(this.events.add.listeners) != 'undefined') {
13200             
13201             this.addicon = this.wrap.createChild(
13202                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13203        
13204             this.addicon.on('click', function(e) {
13205                 this.fireEvent('add', this);
13206             }, this);
13207         }
13208         if (typeof(this.events.edit.listeners) != 'undefined') {
13209             
13210             this.editicon = this.wrap.createChild(
13211                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13212             if (this.addicon) {
13213                 this.editicon.setStyle('margin-left', '40px');
13214             }
13215             this.editicon.on('click', function(e) {
13216                 
13217                 // we fire even  if inothing is selected..
13218                 this.fireEvent('edit', this, this.lastData );
13219                 
13220             }, this);
13221         }
13222         */
13223         
13224         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13225             "up" : function(e){
13226                 this.inKeyMode = true;
13227                 this.selectPrev();
13228             },
13229
13230             "down" : function(e){
13231                 if(!this.isExpanded()){
13232                     this.onTriggerClick();
13233                 }else{
13234                     this.inKeyMode = true;
13235                     this.selectNext();
13236                 }
13237             },
13238
13239             "enter" : function(e){
13240 //                this.onViewClick();
13241                 //return true;
13242                 this.collapse();
13243                 
13244                 if(this.fireEvent("specialkey", this, e)){
13245                     this.onViewClick(false);
13246                 }
13247                 
13248                 return true;
13249             },
13250
13251             "esc" : function(e){
13252                 this.collapse();
13253             },
13254
13255             "tab" : function(e){
13256                 this.collapse();
13257                 
13258                 if(this.fireEvent("specialkey", this, e)){
13259                     this.onViewClick(false);
13260                 }
13261                 
13262                 return true;
13263             },
13264
13265             scope : this,
13266
13267             doRelay : function(foo, bar, hname){
13268                 if(hname == 'down' || this.scope.isExpanded()){
13269                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13270                 }
13271                 return true;
13272             },
13273
13274             forceKeyDown: true
13275         });
13276         
13277         
13278         this.queryDelay = Math.max(this.queryDelay || 10,
13279                 this.mode == 'local' ? 10 : 250);
13280         
13281         
13282         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13283         
13284         if(this.typeAhead){
13285             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13286         }
13287         if(this.editable !== false){
13288             this.inputEl().on("keyup", this.onKeyUp, this);
13289         }
13290         if(this.forceSelection){
13291             this.inputEl().on('blur', this.doForce, this);
13292         }
13293         
13294         if(this.multiple){
13295             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13296             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13297         }
13298     },
13299     
13300     initTickableEvents: function()
13301     {   
13302         this.createList();
13303         
13304         if(this.hiddenName){
13305             
13306             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13307             
13308             this.hiddenField.dom.value =
13309                 this.hiddenValue !== undefined ? this.hiddenValue :
13310                 this.value !== undefined ? this.value : '';
13311
13312             // prevent input submission
13313             this.el.dom.removeAttribute('name');
13314             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13315              
13316              
13317         }
13318         
13319 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13320         
13321         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13322         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13323         if(this.triggerList){
13324             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13325         }
13326          
13327         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13328         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13329         
13330         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13331         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13332         
13333         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13334         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13335         
13336         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13337         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13338         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13339         
13340         this.okBtn.hide();
13341         this.cancelBtn.hide();
13342         
13343         var _this = this;
13344         
13345         (function(){
13346             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13347             _this.list.setWidth(lw);
13348         }).defer(100);
13349         
13350         this.list.on('mouseover', this.onViewOver, this);
13351         this.list.on('mousemove', this.onViewMove, this);
13352         
13353         this.list.on('scroll', this.onViewScroll, this);
13354         
13355         if(!this.tpl){
13356             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>';
13357         }
13358
13359         this.view = new Roo.View(this.list, this.tpl, {
13360             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13361         });
13362         
13363         //this.view.wrapEl.setDisplayed(false);
13364         this.view.on('click', this.onViewClick, this);
13365         
13366         
13367         
13368         this.store.on('beforeload', this.onBeforeLoad, this);
13369         this.store.on('load', this.onLoad, this);
13370         this.store.on('loadexception', this.onLoadException, this);
13371         
13372         if(this.editable){
13373             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13374                 "up" : function(e){
13375                     this.inKeyMode = true;
13376                     this.selectPrev();
13377                 },
13378
13379                 "down" : function(e){
13380                     this.inKeyMode = true;
13381                     this.selectNext();
13382                 },
13383
13384                 "enter" : function(e){
13385                     if(this.fireEvent("specialkey", this, e)){
13386                         this.onViewClick(false);
13387                     }
13388                     
13389                     return true;
13390                 },
13391
13392                 "esc" : function(e){
13393                     this.onTickableFooterButtonClick(e, false, false);
13394                 },
13395
13396                 "tab" : function(e){
13397                     this.fireEvent("specialkey", this, e);
13398                     
13399                     this.onTickableFooterButtonClick(e, false, false);
13400                     
13401                     return true;
13402                 },
13403
13404                 scope : this,
13405
13406                 doRelay : function(e, fn, key){
13407                     if(this.scope.isExpanded()){
13408                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13409                     }
13410                     return true;
13411                 },
13412
13413                 forceKeyDown: true
13414             });
13415         }
13416         
13417         this.queryDelay = Math.max(this.queryDelay || 10,
13418                 this.mode == 'local' ? 10 : 250);
13419         
13420         
13421         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13422         
13423         if(this.typeAhead){
13424             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13425         }
13426         
13427         if(this.editable !== false){
13428             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13429         }
13430         
13431         this.indicator = this.indicatorEl();
13432         
13433         if(this.indicator){
13434             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13435             this.indicator.hide();
13436         }
13437         
13438     },
13439
13440     onDestroy : function(){
13441         if(this.view){
13442             this.view.setStore(null);
13443             this.view.el.removeAllListeners();
13444             this.view.el.remove();
13445             this.view.purgeListeners();
13446         }
13447         if(this.list){
13448             this.list.dom.innerHTML  = '';
13449         }
13450         
13451         if(this.store){
13452             this.store.un('beforeload', this.onBeforeLoad, this);
13453             this.store.un('load', this.onLoad, this);
13454             this.store.un('loadexception', this.onLoadException, this);
13455         }
13456         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13457     },
13458
13459     // private
13460     fireKey : function(e){
13461         if(e.isNavKeyPress() && !this.list.isVisible()){
13462             this.fireEvent("specialkey", this, e);
13463         }
13464     },
13465
13466     // private
13467     onResize: function(w, h){
13468 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13469 //        
13470 //        if(typeof w != 'number'){
13471 //            // we do not handle it!?!?
13472 //            return;
13473 //        }
13474 //        var tw = this.trigger.getWidth();
13475 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13476 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13477 //        var x = w - tw;
13478 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13479 //            
13480 //        //this.trigger.setStyle('left', x+'px');
13481 //        
13482 //        if(this.list && this.listWidth === undefined){
13483 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13484 //            this.list.setWidth(lw);
13485 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13486 //        }
13487         
13488     
13489         
13490     },
13491
13492     /**
13493      * Allow or prevent the user from directly editing the field text.  If false is passed,
13494      * the user will only be able to select from the items defined in the dropdown list.  This method
13495      * is the runtime equivalent of setting the 'editable' config option at config time.
13496      * @param {Boolean} value True to allow the user to directly edit the field text
13497      */
13498     setEditable : function(value){
13499         if(value == this.editable){
13500             return;
13501         }
13502         this.editable = value;
13503         if(!value){
13504             this.inputEl().dom.setAttribute('readOnly', true);
13505             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13506             this.inputEl().addClass('x-combo-noedit');
13507         }else{
13508             this.inputEl().dom.setAttribute('readOnly', false);
13509             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13510             this.inputEl().removeClass('x-combo-noedit');
13511         }
13512     },
13513
13514     // private
13515     
13516     onBeforeLoad : function(combo,opts){
13517         if(!this.hasFocus){
13518             return;
13519         }
13520          if (!opts.add) {
13521             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13522          }
13523         this.restrictHeight();
13524         this.selectedIndex = -1;
13525     },
13526
13527     // private
13528     onLoad : function(){
13529         
13530         this.hasQuery = false;
13531         
13532         if(!this.hasFocus){
13533             return;
13534         }
13535         
13536         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13537             this.loading.hide();
13538         }
13539         
13540         if(this.store.getCount() > 0){
13541             
13542             this.expand();
13543             this.restrictHeight();
13544             if(this.lastQuery == this.allQuery){
13545                 if(this.editable && !this.tickable){
13546                     this.inputEl().dom.select();
13547                 }
13548                 
13549                 if(
13550                     !this.selectByValue(this.value, true) &&
13551                     this.autoFocus && 
13552                     (
13553                         !this.store.lastOptions ||
13554                         typeof(this.store.lastOptions.add) == 'undefined' || 
13555                         this.store.lastOptions.add != true
13556                     )
13557                 ){
13558                     this.select(0, true);
13559                 }
13560             }else{
13561                 if(this.autoFocus){
13562                     this.selectNext();
13563                 }
13564                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13565                     this.taTask.delay(this.typeAheadDelay);
13566                 }
13567             }
13568         }else{
13569             this.onEmptyResults();
13570         }
13571         
13572         //this.el.focus();
13573     },
13574     // private
13575     onLoadException : function()
13576     {
13577         this.hasQuery = false;
13578         
13579         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13580             this.loading.hide();
13581         }
13582         
13583         if(this.tickable && this.editable){
13584             return;
13585         }
13586         
13587         this.collapse();
13588         // only causes errors at present
13589         //Roo.log(this.store.reader.jsonData);
13590         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13591             // fixme
13592             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13593         //}
13594         
13595         
13596     },
13597     // private
13598     onTypeAhead : function(){
13599         if(this.store.getCount() > 0){
13600             var r = this.store.getAt(0);
13601             var newValue = r.data[this.displayField];
13602             var len = newValue.length;
13603             var selStart = this.getRawValue().length;
13604             
13605             if(selStart != len){
13606                 this.setRawValue(newValue);
13607                 this.selectText(selStart, newValue.length);
13608             }
13609         }
13610     },
13611
13612     // private
13613     onSelect : function(record, index){
13614         
13615         if(this.fireEvent('beforeselect', this, record, index) !== false){
13616         
13617             this.setFromData(index > -1 ? record.data : false);
13618             
13619             this.collapse();
13620             this.fireEvent('select', this, record, index);
13621         }
13622     },
13623
13624     /**
13625      * Returns the currently selected field value or empty string if no value is set.
13626      * @return {String} value The selected value
13627      */
13628     getValue : function()
13629     {
13630         if(Roo.isIOS && this.useNativeIOS){
13631             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13632         }
13633         
13634         if(this.multiple){
13635             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13636         }
13637         
13638         if(this.valueField){
13639             return typeof this.value != 'undefined' ? this.value : '';
13640         }else{
13641             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13642         }
13643     },
13644     
13645     getRawValue : function()
13646     {
13647         if(Roo.isIOS && this.useNativeIOS){
13648             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13649         }
13650         
13651         var v = this.inputEl().getValue();
13652         
13653         return v;
13654     },
13655
13656     /**
13657      * Clears any text/value currently set in the field
13658      */
13659     clearValue : function(){
13660         
13661         if(this.hiddenField){
13662             this.hiddenField.dom.value = '';
13663         }
13664         this.value = '';
13665         this.setRawValue('');
13666         this.lastSelectionText = '';
13667         this.lastData = false;
13668         
13669         var close = this.closeTriggerEl();
13670         
13671         if(close){
13672             close.hide();
13673         }
13674         
13675         this.validate();
13676         
13677     },
13678
13679     /**
13680      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13681      * will be displayed in the field.  If the value does not match the data value of an existing item,
13682      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13683      * Otherwise the field will be blank (although the value will still be set).
13684      * @param {String} value The value to match
13685      */
13686     setValue : function(v)
13687     {
13688         if(Roo.isIOS && this.useNativeIOS){
13689             this.setIOSValue(v);
13690             return;
13691         }
13692         
13693         if(this.multiple){
13694             this.syncValue();
13695             return;
13696         }
13697         
13698         var text = v;
13699         if(this.valueField){
13700             var r = this.findRecord(this.valueField, v);
13701             if(r){
13702                 text = r.data[this.displayField];
13703             }else if(this.valueNotFoundText !== undefined){
13704                 text = this.valueNotFoundText;
13705             }
13706         }
13707         this.lastSelectionText = text;
13708         if(this.hiddenField){
13709             this.hiddenField.dom.value = v;
13710         }
13711         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13712         this.value = v;
13713         
13714         var close = this.closeTriggerEl();
13715         
13716         if(close){
13717             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13718         }
13719         
13720         this.validate();
13721     },
13722     /**
13723      * @property {Object} the last set data for the element
13724      */
13725     
13726     lastData : false,
13727     /**
13728      * Sets the value of the field based on a object which is related to the record format for the store.
13729      * @param {Object} value the value to set as. or false on reset?
13730      */
13731     setFromData : function(o){
13732         
13733         if(this.multiple){
13734             this.addItem(o);
13735             return;
13736         }
13737             
13738         var dv = ''; // display value
13739         var vv = ''; // value value..
13740         this.lastData = o;
13741         if (this.displayField) {
13742             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13743         } else {
13744             // this is an error condition!!!
13745             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13746         }
13747         
13748         if(this.valueField){
13749             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13750         }
13751         
13752         var close = this.closeTriggerEl();
13753         
13754         if(close){
13755             if(dv.length || vv * 1 > 0){
13756                 close.show() ;
13757                 this.blockFocus=true;
13758             } else {
13759                 close.hide();
13760             }             
13761         }
13762         
13763         if(this.hiddenField){
13764             this.hiddenField.dom.value = vv;
13765             
13766             this.lastSelectionText = dv;
13767             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13768             this.value = vv;
13769             return;
13770         }
13771         // no hidden field.. - we store the value in 'value', but still display
13772         // display field!!!!
13773         this.lastSelectionText = dv;
13774         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13775         this.value = vv;
13776         
13777         
13778         
13779     },
13780     // private
13781     reset : function(){
13782         // overridden so that last data is reset..
13783         
13784         if(this.multiple){
13785             this.clearItem();
13786             return;
13787         }
13788         
13789         this.setValue(this.originalValue);
13790         //this.clearInvalid();
13791         this.lastData = false;
13792         if (this.view) {
13793             this.view.clearSelections();
13794         }
13795         
13796         this.validate();
13797     },
13798     // private
13799     findRecord : function(prop, value){
13800         var record;
13801         if(this.store.getCount() > 0){
13802             this.store.each(function(r){
13803                 if(r.data[prop] == value){
13804                     record = r;
13805                     return false;
13806                 }
13807                 return true;
13808             });
13809         }
13810         return record;
13811     },
13812     
13813     getName: function()
13814     {
13815         // returns hidden if it's set..
13816         if (!this.rendered) {return ''};
13817         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13818         
13819     },
13820     // private
13821     onViewMove : function(e, t){
13822         this.inKeyMode = false;
13823     },
13824
13825     // private
13826     onViewOver : function(e, t){
13827         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13828             return;
13829         }
13830         var item = this.view.findItemFromChild(t);
13831         
13832         if(item){
13833             var index = this.view.indexOf(item);
13834             this.select(index, false);
13835         }
13836     },
13837
13838     // private
13839     onViewClick : function(view, doFocus, el, e)
13840     {
13841         var index = this.view.getSelectedIndexes()[0];
13842         
13843         var r = this.store.getAt(index);
13844         
13845         if(this.tickable){
13846             
13847             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13848                 return;
13849             }
13850             
13851             var rm = false;
13852             var _this = this;
13853             
13854             Roo.each(this.tickItems, function(v,k){
13855                 
13856                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13857                     Roo.log(v);
13858                     _this.tickItems.splice(k, 1);
13859                     
13860                     if(typeof(e) == 'undefined' && view == false){
13861                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13862                     }
13863                     
13864                     rm = true;
13865                     return;
13866                 }
13867             });
13868             
13869             if(rm){
13870                 return;
13871             }
13872             
13873             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13874                 this.tickItems.push(r.data);
13875             }
13876             
13877             if(typeof(e) == 'undefined' && view == false){
13878                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13879             }
13880                     
13881             return;
13882         }
13883         
13884         if(r){
13885             this.onSelect(r, index);
13886         }
13887         if(doFocus !== false && !this.blockFocus){
13888             this.inputEl().focus();
13889         }
13890     },
13891
13892     // private
13893     restrictHeight : function(){
13894         //this.innerList.dom.style.height = '';
13895         //var inner = this.innerList.dom;
13896         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13897         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13898         //this.list.beginUpdate();
13899         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13900         this.list.alignTo(this.inputEl(), this.listAlign);
13901         this.list.alignTo(this.inputEl(), this.listAlign);
13902         //this.list.endUpdate();
13903     },
13904
13905     // private
13906     onEmptyResults : function(){
13907         
13908         if(this.tickable && this.editable){
13909             this.restrictHeight();
13910             return;
13911         }
13912         
13913         this.collapse();
13914     },
13915
13916     /**
13917      * Returns true if the dropdown list is expanded, else false.
13918      */
13919     isExpanded : function(){
13920         return this.list.isVisible();
13921     },
13922
13923     /**
13924      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13925      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13926      * @param {String} value The data value of the item to select
13927      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13928      * selected item if it is not currently in view (defaults to true)
13929      * @return {Boolean} True if the value matched an item in the list, else false
13930      */
13931     selectByValue : function(v, scrollIntoView){
13932         if(v !== undefined && v !== null){
13933             var r = this.findRecord(this.valueField || this.displayField, v);
13934             if(r){
13935                 this.select(this.store.indexOf(r), scrollIntoView);
13936                 return true;
13937             }
13938         }
13939         return false;
13940     },
13941
13942     /**
13943      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13944      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13945      * @param {Number} index The zero-based index of the list item to select
13946      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13947      * selected item if it is not currently in view (defaults to true)
13948      */
13949     select : function(index, scrollIntoView){
13950         this.selectedIndex = index;
13951         this.view.select(index);
13952         if(scrollIntoView !== false){
13953             var el = this.view.getNode(index);
13954             /*
13955              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13956              */
13957             if(el){
13958                 this.list.scrollChildIntoView(el, false);
13959             }
13960         }
13961     },
13962
13963     // private
13964     selectNext : function(){
13965         var ct = this.store.getCount();
13966         if(ct > 0){
13967             if(this.selectedIndex == -1){
13968                 this.select(0);
13969             }else if(this.selectedIndex < ct-1){
13970                 this.select(this.selectedIndex+1);
13971             }
13972         }
13973     },
13974
13975     // private
13976     selectPrev : function(){
13977         var ct = this.store.getCount();
13978         if(ct > 0){
13979             if(this.selectedIndex == -1){
13980                 this.select(0);
13981             }else if(this.selectedIndex != 0){
13982                 this.select(this.selectedIndex-1);
13983             }
13984         }
13985     },
13986
13987     // private
13988     onKeyUp : function(e){
13989         if(this.editable !== false && !e.isSpecialKey()){
13990             this.lastKey = e.getKey();
13991             this.dqTask.delay(this.queryDelay);
13992         }
13993     },
13994
13995     // private
13996     validateBlur : function(){
13997         return !this.list || !this.list.isVisible();   
13998     },
13999
14000     // private
14001     initQuery : function(){
14002         
14003         var v = this.getRawValue();
14004         
14005         if(this.tickable && this.editable){
14006             v = this.tickableInputEl().getValue();
14007         }
14008         
14009         this.doQuery(v);
14010     },
14011
14012     // private
14013     doForce : function(){
14014         if(this.inputEl().dom.value.length > 0){
14015             this.inputEl().dom.value =
14016                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14017              
14018         }
14019     },
14020
14021     /**
14022      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14023      * query allowing the query action to be canceled if needed.
14024      * @param {String} query The SQL query to execute
14025      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14026      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14027      * saved in the current store (defaults to false)
14028      */
14029     doQuery : function(q, forceAll){
14030         
14031         if(q === undefined || q === null){
14032             q = '';
14033         }
14034         var qe = {
14035             query: q,
14036             forceAll: forceAll,
14037             combo: this,
14038             cancel:false
14039         };
14040         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14041             return false;
14042         }
14043         q = qe.query;
14044         
14045         forceAll = qe.forceAll;
14046         if(forceAll === true || (q.length >= this.minChars)){
14047             
14048             this.hasQuery = true;
14049             
14050             if(this.lastQuery != q || this.alwaysQuery){
14051                 this.lastQuery = q;
14052                 if(this.mode == 'local'){
14053                     this.selectedIndex = -1;
14054                     if(forceAll){
14055                         this.store.clearFilter();
14056                     }else{
14057                         
14058                         if(this.specialFilter){
14059                             this.fireEvent('specialfilter', this);
14060                             this.onLoad();
14061                             return;
14062                         }
14063                         
14064                         this.store.filter(this.displayField, q);
14065                     }
14066                     
14067                     this.store.fireEvent("datachanged", this.store);
14068                     
14069                     this.onLoad();
14070                     
14071                     
14072                 }else{
14073                     
14074                     this.store.baseParams[this.queryParam] = q;
14075                     
14076                     var options = {params : this.getParams(q)};
14077                     
14078                     if(this.loadNext){
14079                         options.add = true;
14080                         options.params.start = this.page * this.pageSize;
14081                     }
14082                     
14083                     this.store.load(options);
14084                     
14085                     /*
14086                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14087                      *  we should expand the list on onLoad
14088                      *  so command out it
14089                      */
14090 //                    this.expand();
14091                 }
14092             }else{
14093                 this.selectedIndex = -1;
14094                 this.onLoad();   
14095             }
14096         }
14097         
14098         this.loadNext = false;
14099     },
14100     
14101     // private
14102     getParams : function(q){
14103         var p = {};
14104         //p[this.queryParam] = q;
14105         
14106         if(this.pageSize){
14107             p.start = 0;
14108             p.limit = this.pageSize;
14109         }
14110         return p;
14111     },
14112
14113     /**
14114      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14115      */
14116     collapse : function(){
14117         if(!this.isExpanded()){
14118             return;
14119         }
14120         
14121         this.list.hide();
14122         
14123         this.hasFocus = false;
14124         
14125         if(this.tickable){
14126             this.okBtn.hide();
14127             this.cancelBtn.hide();
14128             this.trigger.show();
14129             
14130             if(this.editable){
14131                 this.tickableInputEl().dom.value = '';
14132                 this.tickableInputEl().blur();
14133             }
14134             
14135         }
14136         
14137         Roo.get(document).un('mousedown', this.collapseIf, this);
14138         Roo.get(document).un('mousewheel', this.collapseIf, this);
14139         if (!this.editable) {
14140             Roo.get(document).un('keydown', this.listKeyPress, this);
14141         }
14142         this.fireEvent('collapse', this);
14143         
14144         this.validate();
14145     },
14146
14147     // private
14148     collapseIf : function(e){
14149         var in_combo  = e.within(this.el);
14150         var in_list =  e.within(this.list);
14151         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14152         
14153         if (in_combo || in_list || is_list) {
14154             //e.stopPropagation();
14155             return;
14156         }
14157         
14158         if(this.tickable){
14159             this.onTickableFooterButtonClick(e, false, false);
14160         }
14161
14162         this.collapse();
14163         
14164     },
14165
14166     /**
14167      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14168      */
14169     expand : function(){
14170        
14171         if(this.isExpanded() || !this.hasFocus){
14172             return;
14173         }
14174         
14175         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14176         this.list.setWidth(lw);
14177         
14178         Roo.log('expand');
14179         
14180         this.list.show();
14181         
14182         this.restrictHeight();
14183         
14184         if(this.tickable){
14185             
14186             this.tickItems = Roo.apply([], this.item);
14187             
14188             this.okBtn.show();
14189             this.cancelBtn.show();
14190             this.trigger.hide();
14191             
14192             if(this.editable){
14193                 this.tickableInputEl().focus();
14194             }
14195             
14196         }
14197         
14198         Roo.get(document).on('mousedown', this.collapseIf, this);
14199         Roo.get(document).on('mousewheel', this.collapseIf, this);
14200         if (!this.editable) {
14201             Roo.get(document).on('keydown', this.listKeyPress, this);
14202         }
14203         
14204         this.fireEvent('expand', this);
14205     },
14206
14207     // private
14208     // Implements the default empty TriggerField.onTriggerClick function
14209     onTriggerClick : function(e)
14210     {
14211         Roo.log('trigger click');
14212         
14213         if(this.disabled || !this.triggerList){
14214             return;
14215         }
14216         
14217         this.page = 0;
14218         this.loadNext = false;
14219         
14220         if(this.isExpanded()){
14221             this.collapse();
14222             if (!this.blockFocus) {
14223                 this.inputEl().focus();
14224             }
14225             
14226         }else {
14227             this.hasFocus = true;
14228             if(this.triggerAction == 'all') {
14229                 this.doQuery(this.allQuery, true);
14230             } else {
14231                 this.doQuery(this.getRawValue());
14232             }
14233             if (!this.blockFocus) {
14234                 this.inputEl().focus();
14235             }
14236         }
14237     },
14238     
14239     onTickableTriggerClick : function(e)
14240     {
14241         if(this.disabled){
14242             return;
14243         }
14244         
14245         this.page = 0;
14246         this.loadNext = false;
14247         this.hasFocus = true;
14248         
14249         if(this.triggerAction == 'all') {
14250             this.doQuery(this.allQuery, true);
14251         } else {
14252             this.doQuery(this.getRawValue());
14253         }
14254     },
14255     
14256     onSearchFieldClick : function(e)
14257     {
14258         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14259             this.onTickableFooterButtonClick(e, false, false);
14260             return;
14261         }
14262         
14263         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14264             return;
14265         }
14266         
14267         this.page = 0;
14268         this.loadNext = false;
14269         this.hasFocus = true;
14270         
14271         if(this.triggerAction == 'all') {
14272             this.doQuery(this.allQuery, true);
14273         } else {
14274             this.doQuery(this.getRawValue());
14275         }
14276     },
14277     
14278     listKeyPress : function(e)
14279     {
14280         //Roo.log('listkeypress');
14281         // scroll to first matching element based on key pres..
14282         if (e.isSpecialKey()) {
14283             return false;
14284         }
14285         var k = String.fromCharCode(e.getKey()).toUpperCase();
14286         //Roo.log(k);
14287         var match  = false;
14288         var csel = this.view.getSelectedNodes();
14289         var cselitem = false;
14290         if (csel.length) {
14291             var ix = this.view.indexOf(csel[0]);
14292             cselitem  = this.store.getAt(ix);
14293             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14294                 cselitem = false;
14295             }
14296             
14297         }
14298         
14299         this.store.each(function(v) { 
14300             if (cselitem) {
14301                 // start at existing selection.
14302                 if (cselitem.id == v.id) {
14303                     cselitem = false;
14304                 }
14305                 return true;
14306             }
14307                 
14308             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14309                 match = this.store.indexOf(v);
14310                 return false;
14311             }
14312             return true;
14313         }, this);
14314         
14315         if (match === false) {
14316             return true; // no more action?
14317         }
14318         // scroll to?
14319         this.view.select(match);
14320         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14321         sn.scrollIntoView(sn.dom.parentNode, false);
14322     },
14323     
14324     onViewScroll : function(e, t){
14325         
14326         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){
14327             return;
14328         }
14329         
14330         this.hasQuery = true;
14331         
14332         this.loading = this.list.select('.loading', true).first();
14333         
14334         if(this.loading === null){
14335             this.list.createChild({
14336                 tag: 'div',
14337                 cls: 'loading roo-select2-more-results roo-select2-active',
14338                 html: 'Loading more results...'
14339             });
14340             
14341             this.loading = this.list.select('.loading', true).first();
14342             
14343             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14344             
14345             this.loading.hide();
14346         }
14347         
14348         this.loading.show();
14349         
14350         var _combo = this;
14351         
14352         this.page++;
14353         this.loadNext = true;
14354         
14355         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14356         
14357         return;
14358     },
14359     
14360     addItem : function(o)
14361     {   
14362         var dv = ''; // display value
14363         
14364         if (this.displayField) {
14365             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14366         } else {
14367             // this is an error condition!!!
14368             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14369         }
14370         
14371         if(!dv.length){
14372             return;
14373         }
14374         
14375         var choice = this.choices.createChild({
14376             tag: 'li',
14377             cls: 'roo-select2-search-choice',
14378             cn: [
14379                 {
14380                     tag: 'div',
14381                     html: dv
14382                 },
14383                 {
14384                     tag: 'a',
14385                     href: '#',
14386                     cls: 'roo-select2-search-choice-close fa fa-times',
14387                     tabindex: '-1'
14388                 }
14389             ]
14390             
14391         }, this.searchField);
14392         
14393         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14394         
14395         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14396         
14397         this.item.push(o);
14398         
14399         this.lastData = o;
14400         
14401         this.syncValue();
14402         
14403         this.inputEl().dom.value = '';
14404         
14405         this.validate();
14406     },
14407     
14408     onRemoveItem : function(e, _self, o)
14409     {
14410         e.preventDefault();
14411         
14412         this.lastItem = Roo.apply([], this.item);
14413         
14414         var index = this.item.indexOf(o.data) * 1;
14415         
14416         if( index < 0){
14417             Roo.log('not this item?!');
14418             return;
14419         }
14420         
14421         this.item.splice(index, 1);
14422         o.item.remove();
14423         
14424         this.syncValue();
14425         
14426         this.fireEvent('remove', this, e);
14427         
14428         this.validate();
14429         
14430     },
14431     
14432     syncValue : function()
14433     {
14434         if(!this.item.length){
14435             this.clearValue();
14436             return;
14437         }
14438             
14439         var value = [];
14440         var _this = this;
14441         Roo.each(this.item, function(i){
14442             if(_this.valueField){
14443                 value.push(i[_this.valueField]);
14444                 return;
14445             }
14446
14447             value.push(i);
14448         });
14449
14450         this.value = value.join(',');
14451
14452         if(this.hiddenField){
14453             this.hiddenField.dom.value = this.value;
14454         }
14455         
14456         this.store.fireEvent("datachanged", this.store);
14457         
14458         this.validate();
14459     },
14460     
14461     clearItem : function()
14462     {
14463         if(!this.multiple){
14464             return;
14465         }
14466         
14467         this.item = [];
14468         
14469         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14470            c.remove();
14471         });
14472         
14473         this.syncValue();
14474         
14475         this.validate();
14476         
14477         if(this.tickable && !Roo.isTouch){
14478             this.view.refresh();
14479         }
14480     },
14481     
14482     inputEl: function ()
14483     {
14484         if(Roo.isIOS && this.useNativeIOS){
14485             return this.el.select('select.roo-ios-select', true).first();
14486         }
14487         
14488         if(Roo.isTouch && this.mobileTouchView){
14489             return this.el.select('input.form-control',true).first();
14490         }
14491         
14492         if(this.tickable){
14493             return this.searchField;
14494         }
14495         
14496         return this.el.select('input.form-control',true).first();
14497     },
14498     
14499     onTickableFooterButtonClick : function(e, btn, el)
14500     {
14501         e.preventDefault();
14502         
14503         this.lastItem = Roo.apply([], this.item);
14504         
14505         if(btn && btn.name == 'cancel'){
14506             this.tickItems = Roo.apply([], this.item);
14507             this.collapse();
14508             return;
14509         }
14510         
14511         this.clearItem();
14512         
14513         var _this = this;
14514         
14515         Roo.each(this.tickItems, function(o){
14516             _this.addItem(o);
14517         });
14518         
14519         this.collapse();
14520         
14521     },
14522     
14523     validate : function()
14524     {
14525         var v = this.getRawValue();
14526         
14527         if(this.multiple){
14528             v = this.getValue();
14529         }
14530         
14531         if(this.disabled || this.allowBlank || v.length){
14532             this.markValid();
14533             return true;
14534         }
14535         
14536         this.markInvalid();
14537         return false;
14538     },
14539     
14540     tickableInputEl : function()
14541     {
14542         if(!this.tickable || !this.editable){
14543             return this.inputEl();
14544         }
14545         
14546         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14547     },
14548     
14549     
14550     getAutoCreateTouchView : function()
14551     {
14552         var id = Roo.id();
14553         
14554         var cfg = {
14555             cls: 'form-group' //input-group
14556         };
14557         
14558         var input =  {
14559             tag: 'input',
14560             id : id,
14561             type : this.inputType,
14562             cls : 'form-control x-combo-noedit',
14563             autocomplete: 'new-password',
14564             placeholder : this.placeholder || '',
14565             readonly : true
14566         };
14567         
14568         if (this.name) {
14569             input.name = this.name;
14570         }
14571         
14572         if (this.size) {
14573             input.cls += ' input-' + this.size;
14574         }
14575         
14576         if (this.disabled) {
14577             input.disabled = true;
14578         }
14579         
14580         var inputblock = {
14581             cls : '',
14582             cn : [
14583                 input
14584             ]
14585         };
14586         
14587         if(this.before){
14588             inputblock.cls += ' input-group';
14589             
14590             inputblock.cn.unshift({
14591                 tag :'span',
14592                 cls : 'input-group-addon',
14593                 html : this.before
14594             });
14595         }
14596         
14597         if(this.removable && !this.multiple){
14598             inputblock.cls += ' roo-removable';
14599             
14600             inputblock.cn.push({
14601                 tag: 'button',
14602                 html : 'x',
14603                 cls : 'roo-combo-removable-btn close'
14604             });
14605         }
14606
14607         if(this.hasFeedback && !this.allowBlank){
14608             
14609             inputblock.cls += ' has-feedback';
14610             
14611             inputblock.cn.push({
14612                 tag: 'span',
14613                 cls: 'glyphicon form-control-feedback'
14614             });
14615             
14616         }
14617         
14618         if (this.after) {
14619             
14620             inputblock.cls += (this.before) ? '' : ' input-group';
14621             
14622             inputblock.cn.push({
14623                 tag :'span',
14624                 cls : 'input-group-addon',
14625                 html : this.after
14626             });
14627         }
14628
14629         var box = {
14630             tag: 'div',
14631             cn: [
14632                 {
14633                     tag: 'input',
14634                     type : 'hidden',
14635                     cls: 'form-hidden-field'
14636                 },
14637                 inputblock
14638             ]
14639             
14640         };
14641         
14642         if(this.multiple){
14643             box = {
14644                 tag: 'div',
14645                 cn: [
14646                     {
14647                         tag: 'input',
14648                         type : 'hidden',
14649                         cls: 'form-hidden-field'
14650                     },
14651                     {
14652                         tag: 'ul',
14653                         cls: 'roo-select2-choices',
14654                         cn:[
14655                             {
14656                                 tag: 'li',
14657                                 cls: 'roo-select2-search-field',
14658                                 cn: [
14659
14660                                     inputblock
14661                                 ]
14662                             }
14663                         ]
14664                     }
14665                 ]
14666             }
14667         };
14668         
14669         var combobox = {
14670             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14671             cn: [
14672                 box
14673             ]
14674         };
14675         
14676         if(!this.multiple && this.showToggleBtn){
14677             
14678             var caret = {
14679                         tag: 'span',
14680                         cls: 'caret'
14681             };
14682             
14683             if (this.caret != false) {
14684                 caret = {
14685                      tag: 'i',
14686                      cls: 'fa fa-' + this.caret
14687                 };
14688                 
14689             }
14690             
14691             combobox.cn.push({
14692                 tag :'span',
14693                 cls : 'input-group-addon btn dropdown-toggle',
14694                 cn : [
14695                     caret,
14696                     {
14697                         tag: 'span',
14698                         cls: 'combobox-clear',
14699                         cn  : [
14700                             {
14701                                 tag : 'i',
14702                                 cls: 'icon-remove'
14703                             }
14704                         ]
14705                     }
14706                 ]
14707
14708             })
14709         }
14710         
14711         if(this.multiple){
14712             combobox.cls += ' roo-select2-container-multi';
14713         }
14714         
14715         var align = this.labelAlign || this.parentLabelAlign();
14716         
14717         if (align ==='left' && this.fieldLabel.length) {
14718
14719             cfg.cn = [
14720                 {
14721                    tag : 'i',
14722                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14723                    tooltip : 'This field is required'
14724                 },
14725                 {
14726                     tag: 'label',
14727                     cls : 'control-label',
14728                     html : this.fieldLabel
14729
14730                 },
14731                 {
14732                     cls : '', 
14733                     cn: [
14734                         combobox
14735                     ]
14736                 }
14737             ];
14738             
14739             var labelCfg = cfg.cn[1];
14740             var contentCfg = cfg.cn[2];
14741             
14742
14743             if(this.indicatorpos == 'right'){
14744                 cfg.cn = [
14745                     {
14746                         tag: 'label',
14747                         'for' :  id,
14748                         cls : 'control-label',
14749                         cn : [
14750                             {
14751                                 tag : 'span',
14752                                 html : this.fieldLabel
14753                             },
14754                             {
14755                                 tag : 'i',
14756                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14757                                 tooltip : 'This field is required'
14758                             }
14759                         ]
14760                     },
14761                     {
14762                         cls : "",
14763                         cn: [
14764                             combobox
14765                         ]
14766                     }
14767
14768                 ];
14769                 
14770                 labelCfg = cfg.cn[0];
14771                 contentCfg = cfg.cn[1];
14772             }
14773             
14774            
14775             
14776             if(this.labelWidth > 12){
14777                 labelCfg.style = "width: " + this.labelWidth + 'px';
14778             }
14779             
14780             if(this.labelWidth < 13 && this.labelmd == 0){
14781                 this.labelmd = this.labelWidth;
14782             }
14783             
14784             if(this.labellg > 0){
14785                 labelCfg.cls += ' col-lg-' + this.labellg;
14786                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14787             }
14788             
14789             if(this.labelmd > 0){
14790                 labelCfg.cls += ' col-md-' + this.labelmd;
14791                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14792             }
14793             
14794             if(this.labelsm > 0){
14795                 labelCfg.cls += ' col-sm-' + this.labelsm;
14796                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14797             }
14798             
14799             if(this.labelxs > 0){
14800                 labelCfg.cls += ' col-xs-' + this.labelxs;
14801                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14802             }
14803                 
14804                 
14805         } else if ( this.fieldLabel.length) {
14806             cfg.cn = [
14807                 {
14808                    tag : 'i',
14809                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14810                    tooltip : 'This field is required'
14811                 },
14812                 {
14813                     tag: 'label',
14814                     cls : 'control-label',
14815                     html : this.fieldLabel
14816
14817                 },
14818                 {
14819                     cls : '', 
14820                     cn: [
14821                         combobox
14822                     ]
14823                 }
14824             ];
14825             
14826             if(this.indicatorpos == 'right'){
14827                 cfg.cn = [
14828                     {
14829                         tag: 'label',
14830                         cls : 'control-label',
14831                         html : this.fieldLabel,
14832                         cn : [
14833                             {
14834                                tag : 'i',
14835                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14836                                tooltip : 'This field is required'
14837                             }
14838                         ]
14839                     },
14840                     {
14841                         cls : '', 
14842                         cn: [
14843                             combobox
14844                         ]
14845                     }
14846                 ];
14847             }
14848         } else {
14849             cfg.cn = combobox;    
14850         }
14851         
14852         
14853         var settings = this;
14854         
14855         ['xs','sm','md','lg'].map(function(size){
14856             if (settings[size]) {
14857                 cfg.cls += ' col-' + size + '-' + settings[size];
14858             }
14859         });
14860         
14861         return cfg;
14862     },
14863     
14864     initTouchView : function()
14865     {
14866         this.renderTouchView();
14867         
14868         this.touchViewEl.on('scroll', function(){
14869             this.el.dom.scrollTop = 0;
14870         }, this);
14871         
14872         this.originalValue = this.getValue();
14873         
14874         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14875         
14876         this.inputEl().on("click", this.showTouchView, this);
14877         if (this.triggerEl) {
14878             this.triggerEl.on("click", this.showTouchView, this);
14879         }
14880         
14881         
14882         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14883         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14884         
14885         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14886         
14887         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14888         this.store.on('load', this.onTouchViewLoad, this);
14889         this.store.on('loadexception', this.onTouchViewLoadException, this);
14890         
14891         if(this.hiddenName){
14892             
14893             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14894             
14895             this.hiddenField.dom.value =
14896                 this.hiddenValue !== undefined ? this.hiddenValue :
14897                 this.value !== undefined ? this.value : '';
14898         
14899             this.el.dom.removeAttribute('name');
14900             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14901         }
14902         
14903         if(this.multiple){
14904             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14905             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14906         }
14907         
14908         if(this.removable && !this.multiple){
14909             var close = this.closeTriggerEl();
14910             if(close){
14911                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14912                 close.on('click', this.removeBtnClick, this, close);
14913             }
14914         }
14915         /*
14916          * fix the bug in Safari iOS8
14917          */
14918         this.inputEl().on("focus", function(e){
14919             document.activeElement.blur();
14920         }, this);
14921         
14922         return;
14923         
14924         
14925     },
14926     
14927     renderTouchView : function()
14928     {
14929         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14930         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14931         
14932         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14933         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14934         
14935         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14936         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14937         this.touchViewBodyEl.setStyle('overflow', 'auto');
14938         
14939         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14940         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14941         
14942         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14943         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14944         
14945     },
14946     
14947     showTouchView : function()
14948     {
14949         if(this.disabled){
14950             return;
14951         }
14952         
14953         this.touchViewHeaderEl.hide();
14954
14955         if(this.modalTitle.length){
14956             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14957             this.touchViewHeaderEl.show();
14958         }
14959
14960         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14961         this.touchViewEl.show();
14962
14963         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14964         
14965         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14966         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14967
14968         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14969
14970         if(this.modalTitle.length){
14971             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14972         }
14973         
14974         this.touchViewBodyEl.setHeight(bodyHeight);
14975
14976         if(this.animate){
14977             var _this = this;
14978             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14979         }else{
14980             this.touchViewEl.addClass('in');
14981         }
14982
14983         this.doTouchViewQuery();
14984         
14985     },
14986     
14987     hideTouchView : function()
14988     {
14989         this.touchViewEl.removeClass('in');
14990
14991         if(this.animate){
14992             var _this = this;
14993             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14994         }else{
14995             this.touchViewEl.setStyle('display', 'none');
14996         }
14997         
14998     },
14999     
15000     setTouchViewValue : function()
15001     {
15002         if(this.multiple){
15003             this.clearItem();
15004         
15005             var _this = this;
15006
15007             Roo.each(this.tickItems, function(o){
15008                 this.addItem(o);
15009             }, this);
15010         }
15011         
15012         this.hideTouchView();
15013     },
15014     
15015     doTouchViewQuery : function()
15016     {
15017         var qe = {
15018             query: '',
15019             forceAll: true,
15020             combo: this,
15021             cancel:false
15022         };
15023         
15024         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15025             return false;
15026         }
15027         
15028         if(!this.alwaysQuery || this.mode == 'local'){
15029             this.onTouchViewLoad();
15030             return;
15031         }
15032         
15033         this.store.load();
15034     },
15035     
15036     onTouchViewBeforeLoad : function(combo,opts)
15037     {
15038         return;
15039     },
15040
15041     // private
15042     onTouchViewLoad : function()
15043     {
15044         if(this.store.getCount() < 1){
15045             this.onTouchViewEmptyResults();
15046             return;
15047         }
15048         
15049         this.clearTouchView();
15050         
15051         var rawValue = this.getRawValue();
15052         
15053         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15054         
15055         this.tickItems = [];
15056         
15057         this.store.data.each(function(d, rowIndex){
15058             var row = this.touchViewListGroup.createChild(template);
15059             
15060             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15061                 row.addClass(d.data.cls);
15062             }
15063             
15064             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15065                 var cfg = {
15066                     data : d.data,
15067                     html : d.data[this.displayField]
15068                 };
15069                 
15070                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15071                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15072                 }
15073             }
15074             row.removeClass('selected');
15075             if(!this.multiple && this.valueField &&
15076                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15077             {
15078                 // radio buttons..
15079                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15080                 row.addClass('selected');
15081             }
15082             
15083             if(this.multiple && this.valueField &&
15084                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15085             {
15086                 
15087                 // checkboxes...
15088                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15089                 this.tickItems.push(d.data);
15090             }
15091             
15092             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15093             
15094         }, this);
15095         
15096         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15097         
15098         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15099
15100         if(this.modalTitle.length){
15101             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15102         }
15103
15104         var listHeight = this.touchViewListGroup.getHeight();
15105         
15106         var _this = this;
15107         
15108         if(firstChecked && listHeight > bodyHeight){
15109             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15110         }
15111         
15112     },
15113     
15114     onTouchViewLoadException : function()
15115     {
15116         this.hideTouchView();
15117     },
15118     
15119     onTouchViewEmptyResults : function()
15120     {
15121         this.clearTouchView();
15122         
15123         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15124         
15125         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15126         
15127     },
15128     
15129     clearTouchView : function()
15130     {
15131         this.touchViewListGroup.dom.innerHTML = '';
15132     },
15133     
15134     onTouchViewClick : function(e, el, o)
15135     {
15136         e.preventDefault();
15137         
15138         var row = o.row;
15139         var rowIndex = o.rowIndex;
15140         
15141         var r = this.store.getAt(rowIndex);
15142         
15143         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15144             
15145             if(!this.multiple){
15146                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15147                     c.dom.removeAttribute('checked');
15148                 }, this);
15149
15150                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15151
15152                 this.setFromData(r.data);
15153
15154                 var close = this.closeTriggerEl();
15155
15156                 if(close){
15157                     close.show();
15158                 }
15159
15160                 this.hideTouchView();
15161
15162                 this.fireEvent('select', this, r, rowIndex);
15163
15164                 return;
15165             }
15166
15167             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15168                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15169                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15170                 return;
15171             }
15172
15173             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15174             this.addItem(r.data);
15175             this.tickItems.push(r.data);
15176         }
15177     },
15178     
15179     getAutoCreateNativeIOS : function()
15180     {
15181         var cfg = {
15182             cls: 'form-group' //input-group,
15183         };
15184         
15185         var combobox =  {
15186             tag: 'select',
15187             cls : 'roo-ios-select'
15188         };
15189         
15190         if (this.name) {
15191             combobox.name = this.name;
15192         }
15193         
15194         if (this.disabled) {
15195             combobox.disabled = true;
15196         }
15197         
15198         var settings = this;
15199         
15200         ['xs','sm','md','lg'].map(function(size){
15201             if (settings[size]) {
15202                 cfg.cls += ' col-' + size + '-' + settings[size];
15203             }
15204         });
15205         
15206         cfg.cn = combobox;
15207         
15208         return cfg;
15209         
15210     },
15211     
15212     initIOSView : function()
15213     {
15214         this.store.on('load', this.onIOSViewLoad, this);
15215         
15216         return;
15217     },
15218     
15219     onIOSViewLoad : function()
15220     {
15221         if(this.store.getCount() < 1){
15222             return;
15223         }
15224         
15225         this.clearIOSView();
15226         
15227         if(this.allowBlank) {
15228             
15229             var default_text = '-- SELECT --';
15230             
15231             if(this.placeholder.length){
15232                 default_text = this.placeholder;
15233             }
15234             
15235             if(this.emptyTitle.length){
15236                 default_text += ' - ' + this.emptyTitle + ' -';
15237             }
15238             
15239             var opt = this.inputEl().createChild({
15240                 tag: 'option',
15241                 value : 0,
15242                 html : default_text
15243             });
15244             
15245             var o = {};
15246             o[this.valueField] = 0;
15247             o[this.displayField] = default_text;
15248             
15249             this.ios_options.push({
15250                 data : o,
15251                 el : opt
15252             });
15253             
15254         }
15255         
15256         this.store.data.each(function(d, rowIndex){
15257             
15258             var html = '';
15259             
15260             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15261                 html = d.data[this.displayField];
15262             }
15263             
15264             var value = '';
15265             
15266             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15267                 value = d.data[this.valueField];
15268             }
15269             
15270             var option = {
15271                 tag: 'option',
15272                 value : value,
15273                 html : html
15274             };
15275             
15276             if(this.value == d.data[this.valueField]){
15277                 option['selected'] = true;
15278             }
15279             
15280             var opt = this.inputEl().createChild(option);
15281             
15282             this.ios_options.push({
15283                 data : d.data,
15284                 el : opt
15285             });
15286             
15287         }, this);
15288         
15289         this.inputEl().on('change', function(){
15290            this.fireEvent('select', this);
15291         }, this);
15292         
15293     },
15294     
15295     clearIOSView: function()
15296     {
15297         this.inputEl().dom.innerHTML = '';
15298         
15299         this.ios_options = [];
15300     },
15301     
15302     setIOSValue: function(v)
15303     {
15304         this.value = v;
15305         
15306         if(!this.ios_options){
15307             return;
15308         }
15309         
15310         Roo.each(this.ios_options, function(opts){
15311            
15312            opts.el.dom.removeAttribute('selected');
15313            
15314            if(opts.data[this.valueField] != v){
15315                return;
15316            }
15317            
15318            opts.el.dom.setAttribute('selected', true);
15319            
15320         }, this);
15321     }
15322
15323     /** 
15324     * @cfg {Boolean} grow 
15325     * @hide 
15326     */
15327     /** 
15328     * @cfg {Number} growMin 
15329     * @hide 
15330     */
15331     /** 
15332     * @cfg {Number} growMax 
15333     * @hide 
15334     */
15335     /**
15336      * @hide
15337      * @method autoSize
15338      */
15339 });
15340
15341 Roo.apply(Roo.bootstrap.ComboBox,  {
15342     
15343     header : {
15344         tag: 'div',
15345         cls: 'modal-header',
15346         cn: [
15347             {
15348                 tag: 'h4',
15349                 cls: 'modal-title'
15350             }
15351         ]
15352     },
15353     
15354     body : {
15355         tag: 'div',
15356         cls: 'modal-body',
15357         cn: [
15358             {
15359                 tag: 'ul',
15360                 cls: 'list-group'
15361             }
15362         ]
15363     },
15364     
15365     listItemRadio : {
15366         tag: 'li',
15367         cls: 'list-group-item',
15368         cn: [
15369             {
15370                 tag: 'span',
15371                 cls: 'roo-combobox-list-group-item-value'
15372             },
15373             {
15374                 tag: 'div',
15375                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15376                 cn: [
15377                     {
15378                         tag: 'input',
15379                         type: 'radio'
15380                     },
15381                     {
15382                         tag: 'label'
15383                     }
15384                 ]
15385             }
15386         ]
15387     },
15388     
15389     listItemCheckbox : {
15390         tag: 'li',
15391         cls: 'list-group-item',
15392         cn: [
15393             {
15394                 tag: 'span',
15395                 cls: 'roo-combobox-list-group-item-value'
15396             },
15397             {
15398                 tag: 'div',
15399                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15400                 cn: [
15401                     {
15402                         tag: 'input',
15403                         type: 'checkbox'
15404                     },
15405                     {
15406                         tag: 'label'
15407                     }
15408                 ]
15409             }
15410         ]
15411     },
15412     
15413     emptyResult : {
15414         tag: 'div',
15415         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15416     },
15417     
15418     footer : {
15419         tag: 'div',
15420         cls: 'modal-footer',
15421         cn: [
15422             {
15423                 tag: 'div',
15424                 cls: 'row',
15425                 cn: [
15426                     {
15427                         tag: 'div',
15428                         cls: 'col-xs-6 text-left',
15429                         cn: {
15430                             tag: 'button',
15431                             cls: 'btn btn-danger roo-touch-view-cancel',
15432                             html: 'Cancel'
15433                         }
15434                     },
15435                     {
15436                         tag: 'div',
15437                         cls: 'col-xs-6 text-right',
15438                         cn: {
15439                             tag: 'button',
15440                             cls: 'btn btn-success roo-touch-view-ok',
15441                             html: 'OK'
15442                         }
15443                     }
15444                 ]
15445             }
15446         ]
15447         
15448     }
15449 });
15450
15451 Roo.apply(Roo.bootstrap.ComboBox,  {
15452     
15453     touchViewTemplate : {
15454         tag: 'div',
15455         cls: 'modal fade roo-combobox-touch-view',
15456         cn: [
15457             {
15458                 tag: 'div',
15459                 cls: 'modal-dialog',
15460                 style : 'position:fixed', // we have to fix position....
15461                 cn: [
15462                     {
15463                         tag: 'div',
15464                         cls: 'modal-content',
15465                         cn: [
15466                             Roo.bootstrap.ComboBox.header,
15467                             Roo.bootstrap.ComboBox.body,
15468                             Roo.bootstrap.ComboBox.footer
15469                         ]
15470                     }
15471                 ]
15472             }
15473         ]
15474     }
15475 });/*
15476  * Based on:
15477  * Ext JS Library 1.1.1
15478  * Copyright(c) 2006-2007, Ext JS, LLC.
15479  *
15480  * Originally Released Under LGPL - original licence link has changed is not relivant.
15481  *
15482  * Fork - LGPL
15483  * <script type="text/javascript">
15484  */
15485
15486 /**
15487  * @class Roo.View
15488  * @extends Roo.util.Observable
15489  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15490  * This class also supports single and multi selection modes. <br>
15491  * Create a data model bound view:
15492  <pre><code>
15493  var store = new Roo.data.Store(...);
15494
15495  var view = new Roo.View({
15496     el : "my-element",
15497     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15498  
15499     singleSelect: true,
15500     selectedClass: "ydataview-selected",
15501     store: store
15502  });
15503
15504  // listen for node click?
15505  view.on("click", function(vw, index, node, e){
15506  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15507  });
15508
15509  // load XML data
15510  dataModel.load("foobar.xml");
15511  </code></pre>
15512  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15513  * <br><br>
15514  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15515  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15516  * 
15517  * Note: old style constructor is still suported (container, template, config)
15518  * 
15519  * @constructor
15520  * Create a new View
15521  * @param {Object} config The config object
15522  * 
15523  */
15524 Roo.View = function(config, depreciated_tpl, depreciated_config){
15525     
15526     this.parent = false;
15527     
15528     if (typeof(depreciated_tpl) == 'undefined') {
15529         // new way.. - universal constructor.
15530         Roo.apply(this, config);
15531         this.el  = Roo.get(this.el);
15532     } else {
15533         // old format..
15534         this.el  = Roo.get(config);
15535         this.tpl = depreciated_tpl;
15536         Roo.apply(this, depreciated_config);
15537     }
15538     this.wrapEl  = this.el.wrap().wrap();
15539     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15540     
15541     
15542     if(typeof(this.tpl) == "string"){
15543         this.tpl = new Roo.Template(this.tpl);
15544     } else {
15545         // support xtype ctors..
15546         this.tpl = new Roo.factory(this.tpl, Roo);
15547     }
15548     
15549     
15550     this.tpl.compile();
15551     
15552     /** @private */
15553     this.addEvents({
15554         /**
15555          * @event beforeclick
15556          * Fires before a click is processed. Returns false to cancel the default action.
15557          * @param {Roo.View} this
15558          * @param {Number} index The index of the target node
15559          * @param {HTMLElement} node The target node
15560          * @param {Roo.EventObject} e The raw event object
15561          */
15562             "beforeclick" : true,
15563         /**
15564          * @event click
15565          * Fires when a template node is clicked.
15566          * @param {Roo.View} this
15567          * @param {Number} index The index of the target node
15568          * @param {HTMLElement} node The target node
15569          * @param {Roo.EventObject} e The raw event object
15570          */
15571             "click" : true,
15572         /**
15573          * @event dblclick
15574          * Fires when a template node is double clicked.
15575          * @param {Roo.View} this
15576          * @param {Number} index The index of the target node
15577          * @param {HTMLElement} node The target node
15578          * @param {Roo.EventObject} e The raw event object
15579          */
15580             "dblclick" : true,
15581         /**
15582          * @event contextmenu
15583          * Fires when a template node is right clicked.
15584          * @param {Roo.View} this
15585          * @param {Number} index The index of the target node
15586          * @param {HTMLElement} node The target node
15587          * @param {Roo.EventObject} e The raw event object
15588          */
15589             "contextmenu" : true,
15590         /**
15591          * @event selectionchange
15592          * Fires when the selected nodes change.
15593          * @param {Roo.View} this
15594          * @param {Array} selections Array of the selected nodes
15595          */
15596             "selectionchange" : true,
15597     
15598         /**
15599          * @event beforeselect
15600          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15601          * @param {Roo.View} this
15602          * @param {HTMLElement} node The node to be selected
15603          * @param {Array} selections Array of currently selected nodes
15604          */
15605             "beforeselect" : true,
15606         /**
15607          * @event preparedata
15608          * Fires on every row to render, to allow you to change the data.
15609          * @param {Roo.View} this
15610          * @param {Object} data to be rendered (change this)
15611          */
15612           "preparedata" : true
15613           
15614           
15615         });
15616
15617
15618
15619     this.el.on({
15620         "click": this.onClick,
15621         "dblclick": this.onDblClick,
15622         "contextmenu": this.onContextMenu,
15623         scope:this
15624     });
15625
15626     this.selections = [];
15627     this.nodes = [];
15628     this.cmp = new Roo.CompositeElementLite([]);
15629     if(this.store){
15630         this.store = Roo.factory(this.store, Roo.data);
15631         this.setStore(this.store, true);
15632     }
15633     
15634     if ( this.footer && this.footer.xtype) {
15635            
15636          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15637         
15638         this.footer.dataSource = this.store;
15639         this.footer.container = fctr;
15640         this.footer = Roo.factory(this.footer, Roo);
15641         fctr.insertFirst(this.el);
15642         
15643         // this is a bit insane - as the paging toolbar seems to detach the el..
15644 //        dom.parentNode.parentNode.parentNode
15645          // they get detached?
15646     }
15647     
15648     
15649     Roo.View.superclass.constructor.call(this);
15650     
15651     
15652 };
15653
15654 Roo.extend(Roo.View, Roo.util.Observable, {
15655     
15656      /**
15657      * @cfg {Roo.data.Store} store Data store to load data from.
15658      */
15659     store : false,
15660     
15661     /**
15662      * @cfg {String|Roo.Element} el The container element.
15663      */
15664     el : '',
15665     
15666     /**
15667      * @cfg {String|Roo.Template} tpl The template used by this View 
15668      */
15669     tpl : false,
15670     /**
15671      * @cfg {String} dataName the named area of the template to use as the data area
15672      *                          Works with domtemplates roo-name="name"
15673      */
15674     dataName: false,
15675     /**
15676      * @cfg {String} selectedClass The css class to add to selected nodes
15677      */
15678     selectedClass : "x-view-selected",
15679      /**
15680      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15681      */
15682     emptyText : "",
15683     
15684     /**
15685      * @cfg {String} text to display on mask (default Loading)
15686      */
15687     mask : false,
15688     /**
15689      * @cfg {Boolean} multiSelect Allow multiple selection
15690      */
15691     multiSelect : false,
15692     /**
15693      * @cfg {Boolean} singleSelect Allow single selection
15694      */
15695     singleSelect:  false,
15696     
15697     /**
15698      * @cfg {Boolean} toggleSelect - selecting 
15699      */
15700     toggleSelect : false,
15701     
15702     /**
15703      * @cfg {Boolean} tickable - selecting 
15704      */
15705     tickable : false,
15706     
15707     /**
15708      * Returns the element this view is bound to.
15709      * @return {Roo.Element}
15710      */
15711     getEl : function(){
15712         return this.wrapEl;
15713     },
15714     
15715     
15716
15717     /**
15718      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15719      */
15720     refresh : function(){
15721         //Roo.log('refresh');
15722         var t = this.tpl;
15723         
15724         // if we are using something like 'domtemplate', then
15725         // the what gets used is:
15726         // t.applySubtemplate(NAME, data, wrapping data..)
15727         // the outer template then get' applied with
15728         //     the store 'extra data'
15729         // and the body get's added to the
15730         //      roo-name="data" node?
15731         //      <span class='roo-tpl-{name}'></span> ?????
15732         
15733         
15734         
15735         this.clearSelections();
15736         this.el.update("");
15737         var html = [];
15738         var records = this.store.getRange();
15739         if(records.length < 1) {
15740             
15741             // is this valid??  = should it render a template??
15742             
15743             this.el.update(this.emptyText);
15744             return;
15745         }
15746         var el = this.el;
15747         if (this.dataName) {
15748             this.el.update(t.apply(this.store.meta)); //????
15749             el = this.el.child('.roo-tpl-' + this.dataName);
15750         }
15751         
15752         for(var i = 0, len = records.length; i < len; i++){
15753             var data = this.prepareData(records[i].data, i, records[i]);
15754             this.fireEvent("preparedata", this, data, i, records[i]);
15755             
15756             var d = Roo.apply({}, data);
15757             
15758             if(this.tickable){
15759                 Roo.apply(d, {'roo-id' : Roo.id()});
15760                 
15761                 var _this = this;
15762             
15763                 Roo.each(this.parent.item, function(item){
15764                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15765                         return;
15766                     }
15767                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15768                 });
15769             }
15770             
15771             html[html.length] = Roo.util.Format.trim(
15772                 this.dataName ?
15773                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15774                     t.apply(d)
15775             );
15776         }
15777         
15778         
15779         
15780         el.update(html.join(""));
15781         this.nodes = el.dom.childNodes;
15782         this.updateIndexes(0);
15783     },
15784     
15785
15786     /**
15787      * Function to override to reformat the data that is sent to
15788      * the template for each node.
15789      * DEPRICATED - use the preparedata event handler.
15790      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15791      * a JSON object for an UpdateManager bound view).
15792      */
15793     prepareData : function(data, index, record)
15794     {
15795         this.fireEvent("preparedata", this, data, index, record);
15796         return data;
15797     },
15798
15799     onUpdate : function(ds, record){
15800         // Roo.log('on update');   
15801         this.clearSelections();
15802         var index = this.store.indexOf(record);
15803         var n = this.nodes[index];
15804         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15805         n.parentNode.removeChild(n);
15806         this.updateIndexes(index, index);
15807     },
15808
15809     
15810     
15811 // --------- FIXME     
15812     onAdd : function(ds, records, index)
15813     {
15814         //Roo.log(['on Add', ds, records, index] );        
15815         this.clearSelections();
15816         if(this.nodes.length == 0){
15817             this.refresh();
15818             return;
15819         }
15820         var n = this.nodes[index];
15821         for(var i = 0, len = records.length; i < len; i++){
15822             var d = this.prepareData(records[i].data, i, records[i]);
15823             if(n){
15824                 this.tpl.insertBefore(n, d);
15825             }else{
15826                 
15827                 this.tpl.append(this.el, d);
15828             }
15829         }
15830         this.updateIndexes(index);
15831     },
15832
15833     onRemove : function(ds, record, index){
15834        // Roo.log('onRemove');
15835         this.clearSelections();
15836         var el = this.dataName  ?
15837             this.el.child('.roo-tpl-' + this.dataName) :
15838             this.el; 
15839         
15840         el.dom.removeChild(this.nodes[index]);
15841         this.updateIndexes(index);
15842     },
15843
15844     /**
15845      * Refresh an individual node.
15846      * @param {Number} index
15847      */
15848     refreshNode : function(index){
15849         this.onUpdate(this.store, this.store.getAt(index));
15850     },
15851
15852     updateIndexes : function(startIndex, endIndex){
15853         var ns = this.nodes;
15854         startIndex = startIndex || 0;
15855         endIndex = endIndex || ns.length - 1;
15856         for(var i = startIndex; i <= endIndex; i++){
15857             ns[i].nodeIndex = i;
15858         }
15859     },
15860
15861     /**
15862      * Changes the data store this view uses and refresh the view.
15863      * @param {Store} store
15864      */
15865     setStore : function(store, initial){
15866         if(!initial && this.store){
15867             this.store.un("datachanged", this.refresh);
15868             this.store.un("add", this.onAdd);
15869             this.store.un("remove", this.onRemove);
15870             this.store.un("update", this.onUpdate);
15871             this.store.un("clear", this.refresh);
15872             this.store.un("beforeload", this.onBeforeLoad);
15873             this.store.un("load", this.onLoad);
15874             this.store.un("loadexception", this.onLoad);
15875         }
15876         if(store){
15877           
15878             store.on("datachanged", this.refresh, this);
15879             store.on("add", this.onAdd, this);
15880             store.on("remove", this.onRemove, this);
15881             store.on("update", this.onUpdate, this);
15882             store.on("clear", this.refresh, this);
15883             store.on("beforeload", this.onBeforeLoad, this);
15884             store.on("load", this.onLoad, this);
15885             store.on("loadexception", this.onLoad, this);
15886         }
15887         
15888         if(store){
15889             this.refresh();
15890         }
15891     },
15892     /**
15893      * onbeforeLoad - masks the loading area.
15894      *
15895      */
15896     onBeforeLoad : function(store,opts)
15897     {
15898          //Roo.log('onBeforeLoad');   
15899         if (!opts.add) {
15900             this.el.update("");
15901         }
15902         this.el.mask(this.mask ? this.mask : "Loading" ); 
15903     },
15904     onLoad : function ()
15905     {
15906         this.el.unmask();
15907     },
15908     
15909
15910     /**
15911      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15912      * @param {HTMLElement} node
15913      * @return {HTMLElement} The template node
15914      */
15915     findItemFromChild : function(node){
15916         var el = this.dataName  ?
15917             this.el.child('.roo-tpl-' + this.dataName,true) :
15918             this.el.dom; 
15919         
15920         if(!node || node.parentNode == el){
15921                     return node;
15922             }
15923             var p = node.parentNode;
15924             while(p && p != el){
15925             if(p.parentNode == el){
15926                 return p;
15927             }
15928             p = p.parentNode;
15929         }
15930             return null;
15931     },
15932
15933     /** @ignore */
15934     onClick : function(e){
15935         var item = this.findItemFromChild(e.getTarget());
15936         if(item){
15937             var index = this.indexOf(item);
15938             if(this.onItemClick(item, index, e) !== false){
15939                 this.fireEvent("click", this, index, item, e);
15940             }
15941         }else{
15942             this.clearSelections();
15943         }
15944     },
15945
15946     /** @ignore */
15947     onContextMenu : function(e){
15948         var item = this.findItemFromChild(e.getTarget());
15949         if(item){
15950             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15951         }
15952     },
15953
15954     /** @ignore */
15955     onDblClick : function(e){
15956         var item = this.findItemFromChild(e.getTarget());
15957         if(item){
15958             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15959         }
15960     },
15961
15962     onItemClick : function(item, index, e)
15963     {
15964         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15965             return false;
15966         }
15967         if (this.toggleSelect) {
15968             var m = this.isSelected(item) ? 'unselect' : 'select';
15969             //Roo.log(m);
15970             var _t = this;
15971             _t[m](item, true, false);
15972             return true;
15973         }
15974         if(this.multiSelect || this.singleSelect){
15975             if(this.multiSelect && e.shiftKey && this.lastSelection){
15976                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15977             }else{
15978                 this.select(item, this.multiSelect && e.ctrlKey);
15979                 this.lastSelection = item;
15980             }
15981             
15982             if(!this.tickable){
15983                 e.preventDefault();
15984             }
15985             
15986         }
15987         return true;
15988     },
15989
15990     /**
15991      * Get the number of selected nodes.
15992      * @return {Number}
15993      */
15994     getSelectionCount : function(){
15995         return this.selections.length;
15996     },
15997
15998     /**
15999      * Get the currently selected nodes.
16000      * @return {Array} An array of HTMLElements
16001      */
16002     getSelectedNodes : function(){
16003         return this.selections;
16004     },
16005
16006     /**
16007      * Get the indexes of the selected nodes.
16008      * @return {Array}
16009      */
16010     getSelectedIndexes : function(){
16011         var indexes = [], s = this.selections;
16012         for(var i = 0, len = s.length; i < len; i++){
16013             indexes.push(s[i].nodeIndex);
16014         }
16015         return indexes;
16016     },
16017
16018     /**
16019      * Clear all selections
16020      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16021      */
16022     clearSelections : function(suppressEvent){
16023         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16024             this.cmp.elements = this.selections;
16025             this.cmp.removeClass(this.selectedClass);
16026             this.selections = [];
16027             if(!suppressEvent){
16028                 this.fireEvent("selectionchange", this, this.selections);
16029             }
16030         }
16031     },
16032
16033     /**
16034      * Returns true if the passed node is selected
16035      * @param {HTMLElement/Number} node The node or node index
16036      * @return {Boolean}
16037      */
16038     isSelected : function(node){
16039         var s = this.selections;
16040         if(s.length < 1){
16041             return false;
16042         }
16043         node = this.getNode(node);
16044         return s.indexOf(node) !== -1;
16045     },
16046
16047     /**
16048      * Selects nodes.
16049      * @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
16050      * @param {Boolean} keepExisting (optional) true to keep existing selections
16051      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16052      */
16053     select : function(nodeInfo, keepExisting, suppressEvent){
16054         if(nodeInfo instanceof Array){
16055             if(!keepExisting){
16056                 this.clearSelections(true);
16057             }
16058             for(var i = 0, len = nodeInfo.length; i < len; i++){
16059                 this.select(nodeInfo[i], true, true);
16060             }
16061             return;
16062         } 
16063         var node = this.getNode(nodeInfo);
16064         if(!node || this.isSelected(node)){
16065             return; // already selected.
16066         }
16067         if(!keepExisting){
16068             this.clearSelections(true);
16069         }
16070         
16071         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16072             Roo.fly(node).addClass(this.selectedClass);
16073             this.selections.push(node);
16074             if(!suppressEvent){
16075                 this.fireEvent("selectionchange", this, this.selections);
16076             }
16077         }
16078         
16079         
16080     },
16081       /**
16082      * Unselects nodes.
16083      * @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
16084      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16085      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16086      */
16087     unselect : function(nodeInfo, keepExisting, suppressEvent)
16088     {
16089         if(nodeInfo instanceof Array){
16090             Roo.each(this.selections, function(s) {
16091                 this.unselect(s, nodeInfo);
16092             }, this);
16093             return;
16094         }
16095         var node = this.getNode(nodeInfo);
16096         if(!node || !this.isSelected(node)){
16097             //Roo.log("not selected");
16098             return; // not selected.
16099         }
16100         // fireevent???
16101         var ns = [];
16102         Roo.each(this.selections, function(s) {
16103             if (s == node ) {
16104                 Roo.fly(node).removeClass(this.selectedClass);
16105
16106                 return;
16107             }
16108             ns.push(s);
16109         },this);
16110         
16111         this.selections= ns;
16112         this.fireEvent("selectionchange", this, this.selections);
16113     },
16114
16115     /**
16116      * Gets a template node.
16117      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16118      * @return {HTMLElement} The node or null if it wasn't found
16119      */
16120     getNode : function(nodeInfo){
16121         if(typeof nodeInfo == "string"){
16122             return document.getElementById(nodeInfo);
16123         }else if(typeof nodeInfo == "number"){
16124             return this.nodes[nodeInfo];
16125         }
16126         return nodeInfo;
16127     },
16128
16129     /**
16130      * Gets a range template nodes.
16131      * @param {Number} startIndex
16132      * @param {Number} endIndex
16133      * @return {Array} An array of nodes
16134      */
16135     getNodes : function(start, end){
16136         var ns = this.nodes;
16137         start = start || 0;
16138         end = typeof end == "undefined" ? ns.length - 1 : end;
16139         var nodes = [];
16140         if(start <= end){
16141             for(var i = start; i <= end; i++){
16142                 nodes.push(ns[i]);
16143             }
16144         } else{
16145             for(var i = start; i >= end; i--){
16146                 nodes.push(ns[i]);
16147             }
16148         }
16149         return nodes;
16150     },
16151
16152     /**
16153      * Finds the index of the passed node
16154      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16155      * @return {Number} The index of the node or -1
16156      */
16157     indexOf : function(node){
16158         node = this.getNode(node);
16159         if(typeof node.nodeIndex == "number"){
16160             return node.nodeIndex;
16161         }
16162         var ns = this.nodes;
16163         for(var i = 0, len = ns.length; i < len; i++){
16164             if(ns[i] == node){
16165                 return i;
16166             }
16167         }
16168         return -1;
16169     }
16170 });
16171 /*
16172  * - LGPL
16173  *
16174  * based on jquery fullcalendar
16175  * 
16176  */
16177
16178 Roo.bootstrap = Roo.bootstrap || {};
16179 /**
16180  * @class Roo.bootstrap.Calendar
16181  * @extends Roo.bootstrap.Component
16182  * Bootstrap Calendar class
16183  * @cfg {Boolean} loadMask (true|false) default false
16184  * @cfg {Object} header generate the user specific header of the calendar, default false
16185
16186  * @constructor
16187  * Create a new Container
16188  * @param {Object} config The config object
16189  */
16190
16191
16192
16193 Roo.bootstrap.Calendar = function(config){
16194     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16195      this.addEvents({
16196         /**
16197              * @event select
16198              * Fires when a date is selected
16199              * @param {DatePicker} this
16200              * @param {Date} date The selected date
16201              */
16202         'select': true,
16203         /**
16204              * @event monthchange
16205              * Fires when the displayed month changes 
16206              * @param {DatePicker} this
16207              * @param {Date} date The selected month
16208              */
16209         'monthchange': true,
16210         /**
16211              * @event evententer
16212              * Fires when mouse over an event
16213              * @param {Calendar} this
16214              * @param {event} Event
16215              */
16216         'evententer': true,
16217         /**
16218              * @event eventleave
16219              * Fires when the mouse leaves an
16220              * @param {Calendar} this
16221              * @param {event}
16222              */
16223         'eventleave': true,
16224         /**
16225              * @event eventclick
16226              * Fires when the mouse click an
16227              * @param {Calendar} this
16228              * @param {event}
16229              */
16230         'eventclick': true
16231         
16232     });
16233
16234 };
16235
16236 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16237     
16238      /**
16239      * @cfg {Number} startDay
16240      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16241      */
16242     startDay : 0,
16243     
16244     loadMask : false,
16245     
16246     header : false,
16247       
16248     getAutoCreate : function(){
16249         
16250         
16251         var fc_button = function(name, corner, style, content ) {
16252             return Roo.apply({},{
16253                 tag : 'span',
16254                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16255                          (corner.length ?
16256                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16257                             ''
16258                         ),
16259                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16260                 unselectable: 'on'
16261             });
16262         };
16263         
16264         var header = {};
16265         
16266         if(!this.header){
16267             header = {
16268                 tag : 'table',
16269                 cls : 'fc-header',
16270                 style : 'width:100%',
16271                 cn : [
16272                     {
16273                         tag: 'tr',
16274                         cn : [
16275                             {
16276                                 tag : 'td',
16277                                 cls : 'fc-header-left',
16278                                 cn : [
16279                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16280                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16281                                     { tag: 'span', cls: 'fc-header-space' },
16282                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16283
16284
16285                                 ]
16286                             },
16287
16288                             {
16289                                 tag : 'td',
16290                                 cls : 'fc-header-center',
16291                                 cn : [
16292                                     {
16293                                         tag: 'span',
16294                                         cls: 'fc-header-title',
16295                                         cn : {
16296                                             tag: 'H2',
16297                                             html : 'month / year'
16298                                         }
16299                                     }
16300
16301                                 ]
16302                             },
16303                             {
16304                                 tag : 'td',
16305                                 cls : 'fc-header-right',
16306                                 cn : [
16307                               /*      fc_button('month', 'left', '', 'month' ),
16308                                     fc_button('week', '', '', 'week' ),
16309                                     fc_button('day', 'right', '', 'day' )
16310                                 */    
16311
16312                                 ]
16313                             }
16314
16315                         ]
16316                     }
16317                 ]
16318             };
16319         }
16320         
16321         header = this.header;
16322         
16323        
16324         var cal_heads = function() {
16325             var ret = [];
16326             // fixme - handle this.
16327             
16328             for (var i =0; i < Date.dayNames.length; i++) {
16329                 var d = Date.dayNames[i];
16330                 ret.push({
16331                     tag: 'th',
16332                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16333                     html : d.substring(0,3)
16334                 });
16335                 
16336             }
16337             ret[0].cls += ' fc-first';
16338             ret[6].cls += ' fc-last';
16339             return ret;
16340         };
16341         var cal_cell = function(n) {
16342             return  {
16343                 tag: 'td',
16344                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16345                 cn : [
16346                     {
16347                         cn : [
16348                             {
16349                                 cls: 'fc-day-number',
16350                                 html: 'D'
16351                             },
16352                             {
16353                                 cls: 'fc-day-content',
16354                              
16355                                 cn : [
16356                                      {
16357                                         style: 'position: relative;' // height: 17px;
16358                                     }
16359                                 ]
16360                             }
16361                             
16362                             
16363                         ]
16364                     }
16365                 ]
16366                 
16367             }
16368         };
16369         var cal_rows = function() {
16370             
16371             var ret = [];
16372             for (var r = 0; r < 6; r++) {
16373                 var row= {
16374                     tag : 'tr',
16375                     cls : 'fc-week',
16376                     cn : []
16377                 };
16378                 
16379                 for (var i =0; i < Date.dayNames.length; i++) {
16380                     var d = Date.dayNames[i];
16381                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16382
16383                 }
16384                 row.cn[0].cls+=' fc-first';
16385                 row.cn[0].cn[0].style = 'min-height:90px';
16386                 row.cn[6].cls+=' fc-last';
16387                 ret.push(row);
16388                 
16389             }
16390             ret[0].cls += ' fc-first';
16391             ret[4].cls += ' fc-prev-last';
16392             ret[5].cls += ' fc-last';
16393             return ret;
16394             
16395         };
16396         
16397         var cal_table = {
16398             tag: 'table',
16399             cls: 'fc-border-separate',
16400             style : 'width:100%',
16401             cellspacing  : 0,
16402             cn : [
16403                 { 
16404                     tag: 'thead',
16405                     cn : [
16406                         { 
16407                             tag: 'tr',
16408                             cls : 'fc-first fc-last',
16409                             cn : cal_heads()
16410                         }
16411                     ]
16412                 },
16413                 { 
16414                     tag: 'tbody',
16415                     cn : cal_rows()
16416                 }
16417                   
16418             ]
16419         };
16420          
16421          var cfg = {
16422             cls : 'fc fc-ltr',
16423             cn : [
16424                 header,
16425                 {
16426                     cls : 'fc-content',
16427                     style : "position: relative;",
16428                     cn : [
16429                         {
16430                             cls : 'fc-view fc-view-month fc-grid',
16431                             style : 'position: relative',
16432                             unselectable : 'on',
16433                             cn : [
16434                                 {
16435                                     cls : 'fc-event-container',
16436                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16437                                 },
16438                                 cal_table
16439                             ]
16440                         }
16441                     ]
16442     
16443                 }
16444            ] 
16445             
16446         };
16447         
16448          
16449         
16450         return cfg;
16451     },
16452     
16453     
16454     initEvents : function()
16455     {
16456         if(!this.store){
16457             throw "can not find store for calendar";
16458         }
16459         
16460         var mark = {
16461             tag: "div",
16462             cls:"x-dlg-mask",
16463             style: "text-align:center",
16464             cn: [
16465                 {
16466                     tag: "div",
16467                     style: "background-color:white;width:50%;margin:250 auto",
16468                     cn: [
16469                         {
16470                             tag: "img",
16471                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16472                         },
16473                         {
16474                             tag: "span",
16475                             html: "Loading"
16476                         }
16477                         
16478                     ]
16479                 }
16480             ]
16481         };
16482         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16483         
16484         var size = this.el.select('.fc-content', true).first().getSize();
16485         this.maskEl.setSize(size.width, size.height);
16486         this.maskEl.enableDisplayMode("block");
16487         if(!this.loadMask){
16488             this.maskEl.hide();
16489         }
16490         
16491         this.store = Roo.factory(this.store, Roo.data);
16492         this.store.on('load', this.onLoad, this);
16493         this.store.on('beforeload', this.onBeforeLoad, this);
16494         
16495         this.resize();
16496         
16497         this.cells = this.el.select('.fc-day',true);
16498         //Roo.log(this.cells);
16499         this.textNodes = this.el.query('.fc-day-number');
16500         this.cells.addClassOnOver('fc-state-hover');
16501         
16502         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16503         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16504         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16505         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16506         
16507         this.on('monthchange', this.onMonthChange, this);
16508         
16509         this.update(new Date().clearTime());
16510     },
16511     
16512     resize : function() {
16513         var sz  = this.el.getSize();
16514         
16515         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16516         this.el.select('.fc-day-content div',true).setHeight(34);
16517     },
16518     
16519     
16520     // private
16521     showPrevMonth : function(e){
16522         this.update(this.activeDate.add("mo", -1));
16523     },
16524     showToday : function(e){
16525         this.update(new Date().clearTime());
16526     },
16527     // private
16528     showNextMonth : function(e){
16529         this.update(this.activeDate.add("mo", 1));
16530     },
16531
16532     // private
16533     showPrevYear : function(){
16534         this.update(this.activeDate.add("y", -1));
16535     },
16536
16537     // private
16538     showNextYear : function(){
16539         this.update(this.activeDate.add("y", 1));
16540     },
16541
16542     
16543    // private
16544     update : function(date)
16545     {
16546         var vd = this.activeDate;
16547         this.activeDate = date;
16548 //        if(vd && this.el){
16549 //            var t = date.getTime();
16550 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16551 //                Roo.log('using add remove');
16552 //                
16553 //                this.fireEvent('monthchange', this, date);
16554 //                
16555 //                this.cells.removeClass("fc-state-highlight");
16556 //                this.cells.each(function(c){
16557 //                   if(c.dateValue == t){
16558 //                       c.addClass("fc-state-highlight");
16559 //                       setTimeout(function(){
16560 //                            try{c.dom.firstChild.focus();}catch(e){}
16561 //                       }, 50);
16562 //                       return false;
16563 //                   }
16564 //                   return true;
16565 //                });
16566 //                return;
16567 //            }
16568 //        }
16569         
16570         var days = date.getDaysInMonth();
16571         
16572         var firstOfMonth = date.getFirstDateOfMonth();
16573         var startingPos = firstOfMonth.getDay()-this.startDay;
16574         
16575         if(startingPos < this.startDay){
16576             startingPos += 7;
16577         }
16578         
16579         var pm = date.add(Date.MONTH, -1);
16580         var prevStart = pm.getDaysInMonth()-startingPos;
16581 //        
16582         this.cells = this.el.select('.fc-day',true);
16583         this.textNodes = this.el.query('.fc-day-number');
16584         this.cells.addClassOnOver('fc-state-hover');
16585         
16586         var cells = this.cells.elements;
16587         var textEls = this.textNodes;
16588         
16589         Roo.each(cells, function(cell){
16590             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16591         });
16592         
16593         days += startingPos;
16594
16595         // convert everything to numbers so it's fast
16596         var day = 86400000;
16597         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16598         //Roo.log(d);
16599         //Roo.log(pm);
16600         //Roo.log(prevStart);
16601         
16602         var today = new Date().clearTime().getTime();
16603         var sel = date.clearTime().getTime();
16604         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16605         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16606         var ddMatch = this.disabledDatesRE;
16607         var ddText = this.disabledDatesText;
16608         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16609         var ddaysText = this.disabledDaysText;
16610         var format = this.format;
16611         
16612         var setCellClass = function(cal, cell){
16613             cell.row = 0;
16614             cell.events = [];
16615             cell.more = [];
16616             //Roo.log('set Cell Class');
16617             cell.title = "";
16618             var t = d.getTime();
16619             
16620             //Roo.log(d);
16621             
16622             cell.dateValue = t;
16623             if(t == today){
16624                 cell.className += " fc-today";
16625                 cell.className += " fc-state-highlight";
16626                 cell.title = cal.todayText;
16627             }
16628             if(t == sel){
16629                 // disable highlight in other month..
16630                 //cell.className += " fc-state-highlight";
16631                 
16632             }
16633             // disabling
16634             if(t < min) {
16635                 cell.className = " fc-state-disabled";
16636                 cell.title = cal.minText;
16637                 return;
16638             }
16639             if(t > max) {
16640                 cell.className = " fc-state-disabled";
16641                 cell.title = cal.maxText;
16642                 return;
16643             }
16644             if(ddays){
16645                 if(ddays.indexOf(d.getDay()) != -1){
16646                     cell.title = ddaysText;
16647                     cell.className = " fc-state-disabled";
16648                 }
16649             }
16650             if(ddMatch && format){
16651                 var fvalue = d.dateFormat(format);
16652                 if(ddMatch.test(fvalue)){
16653                     cell.title = ddText.replace("%0", fvalue);
16654                     cell.className = " fc-state-disabled";
16655                 }
16656             }
16657             
16658             if (!cell.initialClassName) {
16659                 cell.initialClassName = cell.dom.className;
16660             }
16661             
16662             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16663         };
16664
16665         var i = 0;
16666         
16667         for(; i < startingPos; i++) {
16668             textEls[i].innerHTML = (++prevStart);
16669             d.setDate(d.getDate()+1);
16670             
16671             cells[i].className = "fc-past fc-other-month";
16672             setCellClass(this, cells[i]);
16673         }
16674         
16675         var intDay = 0;
16676         
16677         for(; i < days; i++){
16678             intDay = i - startingPos + 1;
16679             textEls[i].innerHTML = (intDay);
16680             d.setDate(d.getDate()+1);
16681             
16682             cells[i].className = ''; // "x-date-active";
16683             setCellClass(this, cells[i]);
16684         }
16685         var extraDays = 0;
16686         
16687         for(; i < 42; i++) {
16688             textEls[i].innerHTML = (++extraDays);
16689             d.setDate(d.getDate()+1);
16690             
16691             cells[i].className = "fc-future fc-other-month";
16692             setCellClass(this, cells[i]);
16693         }
16694         
16695         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16696         
16697         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16698         
16699         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16700         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16701         
16702         if(totalRows != 6){
16703             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16704             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16705         }
16706         
16707         this.fireEvent('monthchange', this, date);
16708         
16709         
16710         /*
16711         if(!this.internalRender){
16712             var main = this.el.dom.firstChild;
16713             var w = main.offsetWidth;
16714             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16715             Roo.fly(main).setWidth(w);
16716             this.internalRender = true;
16717             // opera does not respect the auto grow header center column
16718             // then, after it gets a width opera refuses to recalculate
16719             // without a second pass
16720             if(Roo.isOpera && !this.secondPass){
16721                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16722                 this.secondPass = true;
16723                 this.update.defer(10, this, [date]);
16724             }
16725         }
16726         */
16727         
16728     },
16729     
16730     findCell : function(dt) {
16731         dt = dt.clearTime().getTime();
16732         var ret = false;
16733         this.cells.each(function(c){
16734             //Roo.log("check " +c.dateValue + '?=' + dt);
16735             if(c.dateValue == dt){
16736                 ret = c;
16737                 return false;
16738             }
16739             return true;
16740         });
16741         
16742         return ret;
16743     },
16744     
16745     findCells : function(ev) {
16746         var s = ev.start.clone().clearTime().getTime();
16747        // Roo.log(s);
16748         var e= ev.end.clone().clearTime().getTime();
16749        // Roo.log(e);
16750         var ret = [];
16751         this.cells.each(function(c){
16752              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16753             
16754             if(c.dateValue > e){
16755                 return ;
16756             }
16757             if(c.dateValue < s){
16758                 return ;
16759             }
16760             ret.push(c);
16761         });
16762         
16763         return ret;    
16764     },
16765     
16766 //    findBestRow: function(cells)
16767 //    {
16768 //        var ret = 0;
16769 //        
16770 //        for (var i =0 ; i < cells.length;i++) {
16771 //            ret  = Math.max(cells[i].rows || 0,ret);
16772 //        }
16773 //        return ret;
16774 //        
16775 //    },
16776     
16777     
16778     addItem : function(ev)
16779     {
16780         // look for vertical location slot in
16781         var cells = this.findCells(ev);
16782         
16783 //        ev.row = this.findBestRow(cells);
16784         
16785         // work out the location.
16786         
16787         var crow = false;
16788         var rows = [];
16789         for(var i =0; i < cells.length; i++) {
16790             
16791             cells[i].row = cells[0].row;
16792             
16793             if(i == 0){
16794                 cells[i].row = cells[i].row + 1;
16795             }
16796             
16797             if (!crow) {
16798                 crow = {
16799                     start : cells[i],
16800                     end :  cells[i]
16801                 };
16802                 continue;
16803             }
16804             if (crow.start.getY() == cells[i].getY()) {
16805                 // on same row.
16806                 crow.end = cells[i];
16807                 continue;
16808             }
16809             // different row.
16810             rows.push(crow);
16811             crow = {
16812                 start: cells[i],
16813                 end : cells[i]
16814             };
16815             
16816         }
16817         
16818         rows.push(crow);
16819         ev.els = [];
16820         ev.rows = rows;
16821         ev.cells = cells;
16822         
16823         cells[0].events.push(ev);
16824         
16825         this.calevents.push(ev);
16826     },
16827     
16828     clearEvents: function() {
16829         
16830         if(!this.calevents){
16831             return;
16832         }
16833         
16834         Roo.each(this.cells.elements, function(c){
16835             c.row = 0;
16836             c.events = [];
16837             c.more = [];
16838         });
16839         
16840         Roo.each(this.calevents, function(e) {
16841             Roo.each(e.els, function(el) {
16842                 el.un('mouseenter' ,this.onEventEnter, this);
16843                 el.un('mouseleave' ,this.onEventLeave, this);
16844                 el.remove();
16845             },this);
16846         },this);
16847         
16848         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16849             e.remove();
16850         });
16851         
16852     },
16853     
16854     renderEvents: function()
16855     {   
16856         var _this = this;
16857         
16858         this.cells.each(function(c) {
16859             
16860             if(c.row < 5){
16861                 return;
16862             }
16863             
16864             var ev = c.events;
16865             
16866             var r = 4;
16867             if(c.row != c.events.length){
16868                 r = 4 - (4 - (c.row - c.events.length));
16869             }
16870             
16871             c.events = ev.slice(0, r);
16872             c.more = ev.slice(r);
16873             
16874             if(c.more.length && c.more.length == 1){
16875                 c.events.push(c.more.pop());
16876             }
16877             
16878             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16879             
16880         });
16881             
16882         this.cells.each(function(c) {
16883             
16884             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16885             
16886             
16887             for (var e = 0; e < c.events.length; e++){
16888                 var ev = c.events[e];
16889                 var rows = ev.rows;
16890                 
16891                 for(var i = 0; i < rows.length; i++) {
16892                 
16893                     // how many rows should it span..
16894
16895                     var  cfg = {
16896                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16897                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16898
16899                         unselectable : "on",
16900                         cn : [
16901                             {
16902                                 cls: 'fc-event-inner',
16903                                 cn : [
16904     //                                {
16905     //                                  tag:'span',
16906     //                                  cls: 'fc-event-time',
16907     //                                  html : cells.length > 1 ? '' : ev.time
16908     //                                },
16909                                     {
16910                                       tag:'span',
16911                                       cls: 'fc-event-title',
16912                                       html : String.format('{0}', ev.title)
16913                                     }
16914
16915
16916                                 ]
16917                             },
16918                             {
16919                                 cls: 'ui-resizable-handle ui-resizable-e',
16920                                 html : '&nbsp;&nbsp;&nbsp'
16921                             }
16922
16923                         ]
16924                     };
16925
16926                     if (i == 0) {
16927                         cfg.cls += ' fc-event-start';
16928                     }
16929                     if ((i+1) == rows.length) {
16930                         cfg.cls += ' fc-event-end';
16931                     }
16932
16933                     var ctr = _this.el.select('.fc-event-container',true).first();
16934                     var cg = ctr.createChild(cfg);
16935
16936                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16937                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16938
16939                     var r = (c.more.length) ? 1 : 0;
16940                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16941                     cg.setWidth(ebox.right - sbox.x -2);
16942
16943                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16944                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16945                     cg.on('click', _this.onEventClick, _this, ev);
16946
16947                     ev.els.push(cg);
16948                     
16949                 }
16950                 
16951             }
16952             
16953             
16954             if(c.more.length){
16955                 var  cfg = {
16956                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16957                     style : 'position: absolute',
16958                     unselectable : "on",
16959                     cn : [
16960                         {
16961                             cls: 'fc-event-inner',
16962                             cn : [
16963                                 {
16964                                   tag:'span',
16965                                   cls: 'fc-event-title',
16966                                   html : 'More'
16967                                 }
16968
16969
16970                             ]
16971                         },
16972                         {
16973                             cls: 'ui-resizable-handle ui-resizable-e',
16974                             html : '&nbsp;&nbsp;&nbsp'
16975                         }
16976
16977                     ]
16978                 };
16979
16980                 var ctr = _this.el.select('.fc-event-container',true).first();
16981                 var cg = ctr.createChild(cfg);
16982
16983                 var sbox = c.select('.fc-day-content',true).first().getBox();
16984                 var ebox = c.select('.fc-day-content',true).first().getBox();
16985                 //Roo.log(cg);
16986                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16987                 cg.setWidth(ebox.right - sbox.x -2);
16988
16989                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16990                 
16991             }
16992             
16993         });
16994         
16995         
16996         
16997     },
16998     
16999     onEventEnter: function (e, el,event,d) {
17000         this.fireEvent('evententer', this, el, event);
17001     },
17002     
17003     onEventLeave: function (e, el,event,d) {
17004         this.fireEvent('eventleave', this, el, event);
17005     },
17006     
17007     onEventClick: function (e, el,event,d) {
17008         this.fireEvent('eventclick', this, el, event);
17009     },
17010     
17011     onMonthChange: function () {
17012         this.store.load();
17013     },
17014     
17015     onMoreEventClick: function(e, el, more)
17016     {
17017         var _this = this;
17018         
17019         this.calpopover.placement = 'right';
17020         this.calpopover.setTitle('More');
17021         
17022         this.calpopover.setContent('');
17023         
17024         var ctr = this.calpopover.el.select('.popover-content', true).first();
17025         
17026         Roo.each(more, function(m){
17027             var cfg = {
17028                 cls : 'fc-event-hori fc-event-draggable',
17029                 html : m.title
17030             };
17031             var cg = ctr.createChild(cfg);
17032             
17033             cg.on('click', _this.onEventClick, _this, m);
17034         });
17035         
17036         this.calpopover.show(el);
17037         
17038         
17039     },
17040     
17041     onLoad: function () 
17042     {   
17043         this.calevents = [];
17044         var cal = this;
17045         
17046         if(this.store.getCount() > 0){
17047             this.store.data.each(function(d){
17048                cal.addItem({
17049                     id : d.data.id,
17050                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17051                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17052                     time : d.data.start_time,
17053                     title : d.data.title,
17054                     description : d.data.description,
17055                     venue : d.data.venue
17056                 });
17057             });
17058         }
17059         
17060         this.renderEvents();
17061         
17062         if(this.calevents.length && this.loadMask){
17063             this.maskEl.hide();
17064         }
17065     },
17066     
17067     onBeforeLoad: function()
17068     {
17069         this.clearEvents();
17070         if(this.loadMask){
17071             this.maskEl.show();
17072         }
17073     }
17074 });
17075
17076  
17077  /*
17078  * - LGPL
17079  *
17080  * element
17081  * 
17082  */
17083
17084 /**
17085  * @class Roo.bootstrap.Popover
17086  * @extends Roo.bootstrap.Component
17087  * Bootstrap Popover class
17088  * @cfg {String} html contents of the popover   (or false to use children..)
17089  * @cfg {String} title of popover (or false to hide)
17090  * @cfg {String} placement how it is placed
17091  * @cfg {String} trigger click || hover (or false to trigger manually)
17092  * @cfg {String} over what (parent or false to trigger manually.)
17093  * @cfg {Number} delay - delay before showing
17094  
17095  * @constructor
17096  * Create a new Popover
17097  * @param {Object} config The config object
17098  */
17099
17100 Roo.bootstrap.Popover = function(config){
17101     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17102     
17103     this.addEvents({
17104         // raw events
17105          /**
17106          * @event show
17107          * After the popover show
17108          * 
17109          * @param {Roo.bootstrap.Popover} this
17110          */
17111         "show" : true,
17112         /**
17113          * @event hide
17114          * After the popover hide
17115          * 
17116          * @param {Roo.bootstrap.Popover} this
17117          */
17118         "hide" : true
17119     });
17120 };
17121
17122 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17123     
17124     title: 'Fill in a title',
17125     html: false,
17126     
17127     placement : 'right',
17128     trigger : 'hover', // hover
17129     
17130     delay : 0,
17131     
17132     over: 'parent',
17133     
17134     can_build_overlaid : false,
17135     
17136     getChildContainer : function()
17137     {
17138         return this.el.select('.popover-content',true).first();
17139     },
17140     
17141     getAutoCreate : function(){
17142          
17143         var cfg = {
17144            cls : 'popover roo-dynamic',
17145            style: 'display:block',
17146            cn : [
17147                 {
17148                     cls : 'arrow'
17149                 },
17150                 {
17151                     cls : 'popover-inner',
17152                     cn : [
17153                         {
17154                             tag: 'h3',
17155                             cls: 'popover-title',
17156                             html : this.title
17157                         },
17158                         {
17159                             cls : 'popover-content',
17160                             html : this.html
17161                         }
17162                     ]
17163                     
17164                 }
17165            ]
17166         };
17167         
17168         return cfg;
17169     },
17170     setTitle: function(str)
17171     {
17172         this.title = str;
17173         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17174     },
17175     setContent: function(str)
17176     {
17177         this.html = str;
17178         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17179     },
17180     // as it get's added to the bottom of the page.
17181     onRender : function(ct, position)
17182     {
17183         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17184         if(!this.el){
17185             var cfg = Roo.apply({},  this.getAutoCreate());
17186             cfg.id = Roo.id();
17187             
17188             if (this.cls) {
17189                 cfg.cls += ' ' + this.cls;
17190             }
17191             if (this.style) {
17192                 cfg.style = this.style;
17193             }
17194             //Roo.log("adding to ");
17195             this.el = Roo.get(document.body).createChild(cfg, position);
17196 //            Roo.log(this.el);
17197         }
17198         this.initEvents();
17199     },
17200     
17201     initEvents : function()
17202     {
17203         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17204         this.el.enableDisplayMode('block');
17205         this.el.hide();
17206         if (this.over === false) {
17207             return; 
17208         }
17209         if (this.triggers === false) {
17210             return;
17211         }
17212         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17213         var triggers = this.trigger ? this.trigger.split(' ') : [];
17214         Roo.each(triggers, function(trigger) {
17215         
17216             if (trigger == 'click') {
17217                 on_el.on('click', this.toggle, this);
17218             } else if (trigger != 'manual') {
17219                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17220                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17221       
17222                 on_el.on(eventIn  ,this.enter, this);
17223                 on_el.on(eventOut, this.leave, this);
17224             }
17225         }, this);
17226         
17227     },
17228     
17229     
17230     // private
17231     timeout : null,
17232     hoverState : null,
17233     
17234     toggle : function () {
17235         this.hoverState == 'in' ? this.leave() : this.enter();
17236     },
17237     
17238     enter : function () {
17239         
17240         clearTimeout(this.timeout);
17241     
17242         this.hoverState = 'in';
17243     
17244         if (!this.delay || !this.delay.show) {
17245             this.show();
17246             return;
17247         }
17248         var _t = this;
17249         this.timeout = setTimeout(function () {
17250             if (_t.hoverState == 'in') {
17251                 _t.show();
17252             }
17253         }, this.delay.show)
17254     },
17255     
17256     leave : function() {
17257         clearTimeout(this.timeout);
17258     
17259         this.hoverState = 'out';
17260     
17261         if (!this.delay || !this.delay.hide) {
17262             this.hide();
17263             return;
17264         }
17265         var _t = this;
17266         this.timeout = setTimeout(function () {
17267             if (_t.hoverState == 'out') {
17268                 _t.hide();
17269             }
17270         }, this.delay.hide)
17271     },
17272     
17273     show : function (on_el)
17274     {
17275         if (!on_el) {
17276             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17277         }
17278         
17279         // set content.
17280         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17281         if (this.html !== false) {
17282             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17283         }
17284         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17285         if (!this.title.length) {
17286             this.el.select('.popover-title',true).hide();
17287         }
17288         
17289         var placement = typeof this.placement == 'function' ?
17290             this.placement.call(this, this.el, on_el) :
17291             this.placement;
17292             
17293         var autoToken = /\s?auto?\s?/i;
17294         var autoPlace = autoToken.test(placement);
17295         if (autoPlace) {
17296             placement = placement.replace(autoToken, '') || 'top';
17297         }
17298         
17299         //this.el.detach()
17300         //this.el.setXY([0,0]);
17301         this.el.show();
17302         this.el.dom.style.display='block';
17303         this.el.addClass(placement);
17304         
17305         //this.el.appendTo(on_el);
17306         
17307         var p = this.getPosition();
17308         var box = this.el.getBox();
17309         
17310         if (autoPlace) {
17311             // fixme..
17312         }
17313         var align = Roo.bootstrap.Popover.alignment[placement];
17314         this.el.alignTo(on_el, align[0],align[1]);
17315         //var arrow = this.el.select('.arrow',true).first();
17316         //arrow.set(align[2], 
17317         
17318         this.el.addClass('in');
17319         
17320         
17321         if (this.el.hasClass('fade')) {
17322             // fade it?
17323         }
17324         
17325         this.hoverState = 'in';
17326         
17327         this.fireEvent('show', this);
17328         
17329     },
17330     hide : function()
17331     {
17332         this.el.setXY([0,0]);
17333         this.el.removeClass('in');
17334         this.el.hide();
17335         this.hoverState = null;
17336         
17337         this.fireEvent('hide', this);
17338     }
17339     
17340 });
17341
17342 Roo.bootstrap.Popover.alignment = {
17343     'left' : ['r-l', [-10,0], 'right'],
17344     'right' : ['l-r', [10,0], 'left'],
17345     'bottom' : ['t-b', [0,10], 'top'],
17346     'top' : [ 'b-t', [0,-10], 'bottom']
17347 };
17348
17349  /*
17350  * - LGPL
17351  *
17352  * Progress
17353  * 
17354  */
17355
17356 /**
17357  * @class Roo.bootstrap.Progress
17358  * @extends Roo.bootstrap.Component
17359  * Bootstrap Progress class
17360  * @cfg {Boolean} striped striped of the progress bar
17361  * @cfg {Boolean} active animated of the progress bar
17362  * 
17363  * 
17364  * @constructor
17365  * Create a new Progress
17366  * @param {Object} config The config object
17367  */
17368
17369 Roo.bootstrap.Progress = function(config){
17370     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17371 };
17372
17373 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17374     
17375     striped : false,
17376     active: false,
17377     
17378     getAutoCreate : function(){
17379         var cfg = {
17380             tag: 'div',
17381             cls: 'progress'
17382         };
17383         
17384         
17385         if(this.striped){
17386             cfg.cls += ' progress-striped';
17387         }
17388       
17389         if(this.active){
17390             cfg.cls += ' active';
17391         }
17392         
17393         
17394         return cfg;
17395     }
17396    
17397 });
17398
17399  
17400
17401  /*
17402  * - LGPL
17403  *
17404  * ProgressBar
17405  * 
17406  */
17407
17408 /**
17409  * @class Roo.bootstrap.ProgressBar
17410  * @extends Roo.bootstrap.Component
17411  * Bootstrap ProgressBar class
17412  * @cfg {Number} aria_valuenow aria-value now
17413  * @cfg {Number} aria_valuemin aria-value min
17414  * @cfg {Number} aria_valuemax aria-value max
17415  * @cfg {String} label label for the progress bar
17416  * @cfg {String} panel (success | info | warning | danger )
17417  * @cfg {String} role role of the progress bar
17418  * @cfg {String} sr_only text
17419  * 
17420  * 
17421  * @constructor
17422  * Create a new ProgressBar
17423  * @param {Object} config The config object
17424  */
17425
17426 Roo.bootstrap.ProgressBar = function(config){
17427     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17428 };
17429
17430 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17431     
17432     aria_valuenow : 0,
17433     aria_valuemin : 0,
17434     aria_valuemax : 100,
17435     label : false,
17436     panel : false,
17437     role : false,
17438     sr_only: false,
17439     
17440     getAutoCreate : function()
17441     {
17442         
17443         var cfg = {
17444             tag: 'div',
17445             cls: 'progress-bar',
17446             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17447         };
17448         
17449         if(this.sr_only){
17450             cfg.cn = {
17451                 tag: 'span',
17452                 cls: 'sr-only',
17453                 html: this.sr_only
17454             }
17455         }
17456         
17457         if(this.role){
17458             cfg.role = this.role;
17459         }
17460         
17461         if(this.aria_valuenow){
17462             cfg['aria-valuenow'] = this.aria_valuenow;
17463         }
17464         
17465         if(this.aria_valuemin){
17466             cfg['aria-valuemin'] = this.aria_valuemin;
17467         }
17468         
17469         if(this.aria_valuemax){
17470             cfg['aria-valuemax'] = this.aria_valuemax;
17471         }
17472         
17473         if(this.label && !this.sr_only){
17474             cfg.html = this.label;
17475         }
17476         
17477         if(this.panel){
17478             cfg.cls += ' progress-bar-' + this.panel;
17479         }
17480         
17481         return cfg;
17482     },
17483     
17484     update : function(aria_valuenow)
17485     {
17486         this.aria_valuenow = aria_valuenow;
17487         
17488         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17489     }
17490    
17491 });
17492
17493  
17494
17495  /*
17496  * - LGPL
17497  *
17498  * column
17499  * 
17500  */
17501
17502 /**
17503  * @class Roo.bootstrap.TabGroup
17504  * @extends Roo.bootstrap.Column
17505  * Bootstrap Column class
17506  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17507  * @cfg {Boolean} carousel true to make the group behave like a carousel
17508  * @cfg {Boolean} bullets show bullets for the panels
17509  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17510  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17511  * @cfg {Boolean} showarrow (true|false) show arrow default true
17512  * 
17513  * @constructor
17514  * Create a new TabGroup
17515  * @param {Object} config The config object
17516  */
17517
17518 Roo.bootstrap.TabGroup = function(config){
17519     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17520     if (!this.navId) {
17521         this.navId = Roo.id();
17522     }
17523     this.tabs = [];
17524     Roo.bootstrap.TabGroup.register(this);
17525     
17526 };
17527
17528 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17529     
17530     carousel : false,
17531     transition : false,
17532     bullets : 0,
17533     timer : 0,
17534     autoslide : false,
17535     slideFn : false,
17536     slideOnTouch : false,
17537     showarrow : true,
17538     
17539     getAutoCreate : function()
17540     {
17541         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17542         
17543         cfg.cls += ' tab-content';
17544         
17545         if (this.carousel) {
17546             cfg.cls += ' carousel slide';
17547             
17548             cfg.cn = [{
17549                cls : 'carousel-inner',
17550                cn : []
17551             }];
17552         
17553             if(this.bullets  && !Roo.isTouch){
17554                 
17555                 var bullets = {
17556                     cls : 'carousel-bullets',
17557                     cn : []
17558                 };
17559                
17560                 if(this.bullets_cls){
17561                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17562                 }
17563                 
17564                 bullets.cn.push({
17565                     cls : 'clear'
17566                 });
17567                 
17568                 cfg.cn[0].cn.push(bullets);
17569             }
17570             
17571             if(this.showarrow){
17572                 cfg.cn[0].cn.push({
17573                     tag : 'div',
17574                     class : 'carousel-arrow',
17575                     cn : [
17576                         {
17577                             tag : 'div',
17578                             class : 'carousel-prev',
17579                             cn : [
17580                                 {
17581                                     tag : 'i',
17582                                     class : 'fa fa-chevron-left'
17583                                 }
17584                             ]
17585                         },
17586                         {
17587                             tag : 'div',
17588                             class : 'carousel-next',
17589                             cn : [
17590                                 {
17591                                     tag : 'i',
17592                                     class : 'fa fa-chevron-right'
17593                                 }
17594                             ]
17595                         }
17596                     ]
17597                 });
17598             }
17599             
17600         }
17601         
17602         return cfg;
17603     },
17604     
17605     initEvents:  function()
17606     {
17607 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17608 //            this.el.on("touchstart", this.onTouchStart, this);
17609 //        }
17610         
17611         if(this.autoslide){
17612             var _this = this;
17613             
17614             this.slideFn = window.setInterval(function() {
17615                 _this.showPanelNext();
17616             }, this.timer);
17617         }
17618         
17619         if(this.showarrow){
17620             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17621             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17622         }
17623         
17624         
17625     },
17626     
17627 //    onTouchStart : function(e, el, o)
17628 //    {
17629 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17630 //            return;
17631 //        }
17632 //        
17633 //        this.showPanelNext();
17634 //    },
17635     
17636     
17637     getChildContainer : function()
17638     {
17639         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17640     },
17641     
17642     /**
17643     * register a Navigation item
17644     * @param {Roo.bootstrap.NavItem} the navitem to add
17645     */
17646     register : function(item)
17647     {
17648         this.tabs.push( item);
17649         item.navId = this.navId; // not really needed..
17650         this.addBullet();
17651     
17652     },
17653     
17654     getActivePanel : function()
17655     {
17656         var r = false;
17657         Roo.each(this.tabs, function(t) {
17658             if (t.active) {
17659                 r = t;
17660                 return false;
17661             }
17662             return null;
17663         });
17664         return r;
17665         
17666     },
17667     getPanelByName : function(n)
17668     {
17669         var r = false;
17670         Roo.each(this.tabs, function(t) {
17671             if (t.tabId == n) {
17672                 r = t;
17673                 return false;
17674             }
17675             return null;
17676         });
17677         return r;
17678     },
17679     indexOfPanel : function(p)
17680     {
17681         var r = false;
17682         Roo.each(this.tabs, function(t,i) {
17683             if (t.tabId == p.tabId) {
17684                 r = i;
17685                 return false;
17686             }
17687             return null;
17688         });
17689         return r;
17690     },
17691     /**
17692      * show a specific panel
17693      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17694      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17695      */
17696     showPanel : function (pan)
17697     {
17698         if(this.transition || typeof(pan) == 'undefined'){
17699             Roo.log("waiting for the transitionend");
17700             return;
17701         }
17702         
17703         if (typeof(pan) == 'number') {
17704             pan = this.tabs[pan];
17705         }
17706         
17707         if (typeof(pan) == 'string') {
17708             pan = this.getPanelByName(pan);
17709         }
17710         
17711         var cur = this.getActivePanel();
17712         
17713         if(!pan || !cur){
17714             Roo.log('pan or acitve pan is undefined');
17715             return false;
17716         }
17717         
17718         if (pan.tabId == this.getActivePanel().tabId) {
17719             return true;
17720         }
17721         
17722         if (false === cur.fireEvent('beforedeactivate')) {
17723             return false;
17724         }
17725         
17726         if(this.bullets > 0 && !Roo.isTouch){
17727             this.setActiveBullet(this.indexOfPanel(pan));
17728         }
17729         
17730         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17731             
17732             this.transition = true;
17733             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17734             var lr = dir == 'next' ? 'left' : 'right';
17735             pan.el.addClass(dir); // or prev
17736             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17737             cur.el.addClass(lr); // or right
17738             pan.el.addClass(lr);
17739             
17740             var _this = this;
17741             cur.el.on('transitionend', function() {
17742                 Roo.log("trans end?");
17743                 
17744                 pan.el.removeClass([lr,dir]);
17745                 pan.setActive(true);
17746                 
17747                 cur.el.removeClass([lr]);
17748                 cur.setActive(false);
17749                 
17750                 _this.transition = false;
17751                 
17752             }, this, { single:  true } );
17753             
17754             return true;
17755         }
17756         
17757         cur.setActive(false);
17758         pan.setActive(true);
17759         
17760         return true;
17761         
17762     },
17763     showPanelNext : function()
17764     {
17765         var i = this.indexOfPanel(this.getActivePanel());
17766         
17767         if (i >= this.tabs.length - 1 && !this.autoslide) {
17768             return;
17769         }
17770         
17771         if (i >= this.tabs.length - 1 && this.autoslide) {
17772             i = -1;
17773         }
17774         
17775         this.showPanel(this.tabs[i+1]);
17776     },
17777     
17778     showPanelPrev : function()
17779     {
17780         var i = this.indexOfPanel(this.getActivePanel());
17781         
17782         if (i  < 1 && !this.autoslide) {
17783             return;
17784         }
17785         
17786         if (i < 1 && this.autoslide) {
17787             i = this.tabs.length;
17788         }
17789         
17790         this.showPanel(this.tabs[i-1]);
17791     },
17792     
17793     
17794     addBullet: function()
17795     {
17796         if(!this.bullets || Roo.isTouch){
17797             return;
17798         }
17799         var ctr = this.el.select('.carousel-bullets',true).first();
17800         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17801         var bullet = ctr.createChild({
17802             cls : 'bullet bullet-' + i
17803         },ctr.dom.lastChild);
17804         
17805         
17806         var _this = this;
17807         
17808         bullet.on('click', (function(e, el, o, ii, t){
17809
17810             e.preventDefault();
17811
17812             this.showPanel(ii);
17813
17814             if(this.autoslide && this.slideFn){
17815                 clearInterval(this.slideFn);
17816                 this.slideFn = window.setInterval(function() {
17817                     _this.showPanelNext();
17818                 }, this.timer);
17819             }
17820
17821         }).createDelegate(this, [i, bullet], true));
17822                 
17823         
17824     },
17825      
17826     setActiveBullet : function(i)
17827     {
17828         if(Roo.isTouch){
17829             return;
17830         }
17831         
17832         Roo.each(this.el.select('.bullet', true).elements, function(el){
17833             el.removeClass('selected');
17834         });
17835
17836         var bullet = this.el.select('.bullet-' + i, true).first();
17837         
17838         if(!bullet){
17839             return;
17840         }
17841         
17842         bullet.addClass('selected');
17843     }
17844     
17845     
17846   
17847 });
17848
17849  
17850
17851  
17852  
17853 Roo.apply(Roo.bootstrap.TabGroup, {
17854     
17855     groups: {},
17856      /**
17857     * register a Navigation Group
17858     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17859     */
17860     register : function(navgrp)
17861     {
17862         this.groups[navgrp.navId] = navgrp;
17863         
17864     },
17865     /**
17866     * fetch a Navigation Group based on the navigation ID
17867     * if one does not exist , it will get created.
17868     * @param {string} the navgroup to add
17869     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17870     */
17871     get: function(navId) {
17872         if (typeof(this.groups[navId]) == 'undefined') {
17873             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17874         }
17875         return this.groups[navId] ;
17876     }
17877     
17878     
17879     
17880 });
17881
17882  /*
17883  * - LGPL
17884  *
17885  * TabPanel
17886  * 
17887  */
17888
17889 /**
17890  * @class Roo.bootstrap.TabPanel
17891  * @extends Roo.bootstrap.Component
17892  * Bootstrap TabPanel class
17893  * @cfg {Boolean} active panel active
17894  * @cfg {String} html panel content
17895  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17896  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17897  * @cfg {String} href click to link..
17898  * 
17899  * 
17900  * @constructor
17901  * Create a new TabPanel
17902  * @param {Object} config The config object
17903  */
17904
17905 Roo.bootstrap.TabPanel = function(config){
17906     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17907     this.addEvents({
17908         /**
17909              * @event changed
17910              * Fires when the active status changes
17911              * @param {Roo.bootstrap.TabPanel} this
17912              * @param {Boolean} state the new state
17913             
17914          */
17915         'changed': true,
17916         /**
17917              * @event beforedeactivate
17918              * Fires before a tab is de-activated - can be used to do validation on a form.
17919              * @param {Roo.bootstrap.TabPanel} this
17920              * @return {Boolean} false if there is an error
17921             
17922          */
17923         'beforedeactivate': true
17924      });
17925     
17926     this.tabId = this.tabId || Roo.id();
17927   
17928 };
17929
17930 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17931     
17932     active: false,
17933     html: false,
17934     tabId: false,
17935     navId : false,
17936     href : '',
17937     
17938     getAutoCreate : function(){
17939         var cfg = {
17940             tag: 'div',
17941             // item is needed for carousel - not sure if it has any effect otherwise
17942             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17943             html: this.html || ''
17944         };
17945         
17946         if(this.active){
17947             cfg.cls += ' active';
17948         }
17949         
17950         if(this.tabId){
17951             cfg.tabId = this.tabId;
17952         }
17953         
17954         
17955         return cfg;
17956     },
17957     
17958     initEvents:  function()
17959     {
17960         var p = this.parent();
17961         
17962         this.navId = this.navId || p.navId;
17963         
17964         if (typeof(this.navId) != 'undefined') {
17965             // not really needed.. but just in case.. parent should be a NavGroup.
17966             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17967             
17968             tg.register(this);
17969             
17970             var i = tg.tabs.length - 1;
17971             
17972             if(this.active && tg.bullets > 0 && i < tg.bullets){
17973                 tg.setActiveBullet(i);
17974             }
17975         }
17976         
17977         this.el.on('click', this.onClick, this);
17978         
17979         if(Roo.isTouch){
17980             this.el.on("touchstart", this.onTouchStart, this);
17981             this.el.on("touchmove", this.onTouchMove, this);
17982             this.el.on("touchend", this.onTouchEnd, this);
17983         }
17984         
17985     },
17986     
17987     onRender : function(ct, position)
17988     {
17989         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17990     },
17991     
17992     setActive : function(state)
17993     {
17994         Roo.log("panel - set active " + this.tabId + "=" + state);
17995         
17996         this.active = state;
17997         if (!state) {
17998             this.el.removeClass('active');
17999             
18000         } else  if (!this.el.hasClass('active')) {
18001             this.el.addClass('active');
18002         }
18003         
18004         this.fireEvent('changed', this, state);
18005     },
18006     
18007     onClick : function(e)
18008     {
18009         e.preventDefault();
18010         
18011         if(!this.href.length){
18012             return;
18013         }
18014         
18015         window.location.href = this.href;
18016     },
18017     
18018     startX : 0,
18019     startY : 0,
18020     endX : 0,
18021     endY : 0,
18022     swiping : false,
18023     
18024     onTouchStart : function(e)
18025     {
18026         this.swiping = false;
18027         
18028         this.startX = e.browserEvent.touches[0].clientX;
18029         this.startY = e.browserEvent.touches[0].clientY;
18030     },
18031     
18032     onTouchMove : function(e)
18033     {
18034         this.swiping = true;
18035         
18036         this.endX = e.browserEvent.touches[0].clientX;
18037         this.endY = e.browserEvent.touches[0].clientY;
18038     },
18039     
18040     onTouchEnd : function(e)
18041     {
18042         if(!this.swiping){
18043             this.onClick(e);
18044             return;
18045         }
18046         
18047         var tabGroup = this.parent();
18048         
18049         if(this.endX > this.startX){ // swiping right
18050             tabGroup.showPanelPrev();
18051             return;
18052         }
18053         
18054         if(this.startX > this.endX){ // swiping left
18055             tabGroup.showPanelNext();
18056             return;
18057         }
18058     }
18059     
18060     
18061 });
18062  
18063
18064  
18065
18066  /*
18067  * - LGPL
18068  *
18069  * DateField
18070  * 
18071  */
18072
18073 /**
18074  * @class Roo.bootstrap.DateField
18075  * @extends Roo.bootstrap.Input
18076  * Bootstrap DateField class
18077  * @cfg {Number} weekStart default 0
18078  * @cfg {String} viewMode default empty, (months|years)
18079  * @cfg {String} minViewMode default empty, (months|years)
18080  * @cfg {Number} startDate default -Infinity
18081  * @cfg {Number} endDate default Infinity
18082  * @cfg {Boolean} todayHighlight default false
18083  * @cfg {Boolean} todayBtn default false
18084  * @cfg {Boolean} calendarWeeks default false
18085  * @cfg {Object} daysOfWeekDisabled default empty
18086  * @cfg {Boolean} singleMode default false (true | false)
18087  * 
18088  * @cfg {Boolean} keyboardNavigation default true
18089  * @cfg {String} language default en
18090  * 
18091  * @constructor
18092  * Create a new DateField
18093  * @param {Object} config The config object
18094  */
18095
18096 Roo.bootstrap.DateField = function(config){
18097     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18098      this.addEvents({
18099             /**
18100              * @event show
18101              * Fires when this field show.
18102              * @param {Roo.bootstrap.DateField} this
18103              * @param {Mixed} date The date value
18104              */
18105             show : true,
18106             /**
18107              * @event show
18108              * Fires when this field hide.
18109              * @param {Roo.bootstrap.DateField} this
18110              * @param {Mixed} date The date value
18111              */
18112             hide : true,
18113             /**
18114              * @event select
18115              * Fires when select a date.
18116              * @param {Roo.bootstrap.DateField} this
18117              * @param {Mixed} date The date value
18118              */
18119             select : true,
18120             /**
18121              * @event beforeselect
18122              * Fires when before select a date.
18123              * @param {Roo.bootstrap.DateField} this
18124              * @param {Mixed} date The date value
18125              */
18126             beforeselect : true
18127         });
18128 };
18129
18130 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18131     
18132     /**
18133      * @cfg {String} format
18134      * The default date format string which can be overriden for localization support.  The format must be
18135      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18136      */
18137     format : "m/d/y",
18138     /**
18139      * @cfg {String} altFormats
18140      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18141      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18142      */
18143     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18144     
18145     weekStart : 0,
18146     
18147     viewMode : '',
18148     
18149     minViewMode : '',
18150     
18151     todayHighlight : false,
18152     
18153     todayBtn: false,
18154     
18155     language: 'en',
18156     
18157     keyboardNavigation: true,
18158     
18159     calendarWeeks: false,
18160     
18161     startDate: -Infinity,
18162     
18163     endDate: Infinity,
18164     
18165     daysOfWeekDisabled: [],
18166     
18167     _events: [],
18168     
18169     singleMode : false,
18170     
18171     UTCDate: function()
18172     {
18173         return new Date(Date.UTC.apply(Date, arguments));
18174     },
18175     
18176     UTCToday: function()
18177     {
18178         var today = new Date();
18179         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18180     },
18181     
18182     getDate: function() {
18183             var d = this.getUTCDate();
18184             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18185     },
18186     
18187     getUTCDate: function() {
18188             return this.date;
18189     },
18190     
18191     setDate: function(d) {
18192             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18193     },
18194     
18195     setUTCDate: function(d) {
18196             this.date = d;
18197             this.setValue(this.formatDate(this.date));
18198     },
18199         
18200     onRender: function(ct, position)
18201     {
18202         
18203         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18204         
18205         this.language = this.language || 'en';
18206         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18207         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18208         
18209         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18210         this.format = this.format || 'm/d/y';
18211         this.isInline = false;
18212         this.isInput = true;
18213         this.component = this.el.select('.add-on', true).first() || false;
18214         this.component = (this.component && this.component.length === 0) ? false : this.component;
18215         this.hasInput = this.component && this.inputEl().length;
18216         
18217         if (typeof(this.minViewMode === 'string')) {
18218             switch (this.minViewMode) {
18219                 case 'months':
18220                     this.minViewMode = 1;
18221                     break;
18222                 case 'years':
18223                     this.minViewMode = 2;
18224                     break;
18225                 default:
18226                     this.minViewMode = 0;
18227                     break;
18228             }
18229         }
18230         
18231         if (typeof(this.viewMode === 'string')) {
18232             switch (this.viewMode) {
18233                 case 'months':
18234                     this.viewMode = 1;
18235                     break;
18236                 case 'years':
18237                     this.viewMode = 2;
18238                     break;
18239                 default:
18240                     this.viewMode = 0;
18241                     break;
18242             }
18243         }
18244                 
18245         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18246         
18247 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18248         
18249         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18250         
18251         this.picker().on('mousedown', this.onMousedown, this);
18252         this.picker().on('click', this.onClick, this);
18253         
18254         this.picker().addClass('datepicker-dropdown');
18255         
18256         this.startViewMode = this.viewMode;
18257         
18258         if(this.singleMode){
18259             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18260                 v.setVisibilityMode(Roo.Element.DISPLAY);
18261                 v.hide();
18262             });
18263             
18264             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18265                 v.setStyle('width', '189px');
18266             });
18267         }
18268         
18269         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18270             if(!this.calendarWeeks){
18271                 v.remove();
18272                 return;
18273             }
18274             
18275             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18276             v.attr('colspan', function(i, val){
18277                 return parseInt(val) + 1;
18278             });
18279         });
18280                         
18281         
18282         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18283         
18284         this.setStartDate(this.startDate);
18285         this.setEndDate(this.endDate);
18286         
18287         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18288         
18289         this.fillDow();
18290         this.fillMonths();
18291         this.update();
18292         this.showMode();
18293         
18294         if(this.isInline) {
18295             this.show();
18296         }
18297     },
18298     
18299     picker : function()
18300     {
18301         return this.pickerEl;
18302 //        return this.el.select('.datepicker', true).first();
18303     },
18304     
18305     fillDow: function()
18306     {
18307         var dowCnt = this.weekStart;
18308         
18309         var dow = {
18310             tag: 'tr',
18311             cn: [
18312                 
18313             ]
18314         };
18315         
18316         if(this.calendarWeeks){
18317             dow.cn.push({
18318                 tag: 'th',
18319                 cls: 'cw',
18320                 html: '&nbsp;'
18321             })
18322         }
18323         
18324         while (dowCnt < this.weekStart + 7) {
18325             dow.cn.push({
18326                 tag: 'th',
18327                 cls: 'dow',
18328                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18329             });
18330         }
18331         
18332         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18333     },
18334     
18335     fillMonths: function()
18336     {    
18337         var i = 0;
18338         var months = this.picker().select('>.datepicker-months td', true).first();
18339         
18340         months.dom.innerHTML = '';
18341         
18342         while (i < 12) {
18343             var month = {
18344                 tag: 'span',
18345                 cls: 'month',
18346                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18347             };
18348             
18349             months.createChild(month);
18350         }
18351         
18352     },
18353     
18354     update: function()
18355     {
18356         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;
18357         
18358         if (this.date < this.startDate) {
18359             this.viewDate = new Date(this.startDate);
18360         } else if (this.date > this.endDate) {
18361             this.viewDate = new Date(this.endDate);
18362         } else {
18363             this.viewDate = new Date(this.date);
18364         }
18365         
18366         this.fill();
18367     },
18368     
18369     fill: function() 
18370     {
18371         var d = new Date(this.viewDate),
18372                 year = d.getUTCFullYear(),
18373                 month = d.getUTCMonth(),
18374                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18375                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18376                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18377                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18378                 currentDate = this.date && this.date.valueOf(),
18379                 today = this.UTCToday();
18380         
18381         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18382         
18383 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18384         
18385 //        this.picker.select('>tfoot th.today').
18386 //                                              .text(dates[this.language].today)
18387 //                                              .toggle(this.todayBtn !== false);
18388     
18389         this.updateNavArrows();
18390         this.fillMonths();
18391                                                 
18392         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18393         
18394         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18395          
18396         prevMonth.setUTCDate(day);
18397         
18398         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18399         
18400         var nextMonth = new Date(prevMonth);
18401         
18402         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18403         
18404         nextMonth = nextMonth.valueOf();
18405         
18406         var fillMonths = false;
18407         
18408         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18409         
18410         while(prevMonth.valueOf() < nextMonth) {
18411             var clsName = '';
18412             
18413             if (prevMonth.getUTCDay() === this.weekStart) {
18414                 if(fillMonths){
18415                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18416                 }
18417                     
18418                 fillMonths = {
18419                     tag: 'tr',
18420                     cn: []
18421                 };
18422                 
18423                 if(this.calendarWeeks){
18424                     // ISO 8601: First week contains first thursday.
18425                     // ISO also states week starts on Monday, but we can be more abstract here.
18426                     var
18427                     // Start of current week: based on weekstart/current date
18428                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18429                     // Thursday of this week
18430                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18431                     // First Thursday of year, year from thursday
18432                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18433                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18434                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18435                     
18436                     fillMonths.cn.push({
18437                         tag: 'td',
18438                         cls: 'cw',
18439                         html: calWeek
18440                     });
18441                 }
18442             }
18443             
18444             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18445                 clsName += ' old';
18446             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18447                 clsName += ' new';
18448             }
18449             if (this.todayHighlight &&
18450                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18451                 prevMonth.getUTCMonth() == today.getMonth() &&
18452                 prevMonth.getUTCDate() == today.getDate()) {
18453                 clsName += ' today';
18454             }
18455             
18456             if (currentDate && prevMonth.valueOf() === currentDate) {
18457                 clsName += ' active';
18458             }
18459             
18460             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18461                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18462                     clsName += ' disabled';
18463             }
18464             
18465             fillMonths.cn.push({
18466                 tag: 'td',
18467                 cls: 'day ' + clsName,
18468                 html: prevMonth.getDate()
18469             });
18470             
18471             prevMonth.setDate(prevMonth.getDate()+1);
18472         }
18473           
18474         var currentYear = this.date && this.date.getUTCFullYear();
18475         var currentMonth = this.date && this.date.getUTCMonth();
18476         
18477         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18478         
18479         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18480             v.removeClass('active');
18481             
18482             if(currentYear === year && k === currentMonth){
18483                 v.addClass('active');
18484             }
18485             
18486             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18487                 v.addClass('disabled');
18488             }
18489             
18490         });
18491         
18492         
18493         year = parseInt(year/10, 10) * 10;
18494         
18495         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18496         
18497         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18498         
18499         year -= 1;
18500         for (var i = -1; i < 11; i++) {
18501             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18502                 tag: 'span',
18503                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18504                 html: year
18505             });
18506             
18507             year += 1;
18508         }
18509     },
18510     
18511     showMode: function(dir) 
18512     {
18513         if (dir) {
18514             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18515         }
18516         
18517         Roo.each(this.picker().select('>div',true).elements, function(v){
18518             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18519             v.hide();
18520         });
18521         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18522     },
18523     
18524     place: function()
18525     {
18526         if(this.isInline) {
18527             return;
18528         }
18529         
18530         this.picker().removeClass(['bottom', 'top']);
18531         
18532         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18533             /*
18534              * place to the top of element!
18535              *
18536              */
18537             
18538             this.picker().addClass('top');
18539             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18540             
18541             return;
18542         }
18543         
18544         this.picker().addClass('bottom');
18545         
18546         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18547     },
18548     
18549     parseDate : function(value)
18550     {
18551         if(!value || value instanceof Date){
18552             return value;
18553         }
18554         var v = Date.parseDate(value, this.format);
18555         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18556             v = Date.parseDate(value, 'Y-m-d');
18557         }
18558         if(!v && this.altFormats){
18559             if(!this.altFormatsArray){
18560                 this.altFormatsArray = this.altFormats.split("|");
18561             }
18562             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18563                 v = Date.parseDate(value, this.altFormatsArray[i]);
18564             }
18565         }
18566         return v;
18567     },
18568     
18569     formatDate : function(date, fmt)
18570     {   
18571         return (!date || !(date instanceof Date)) ?
18572         date : date.dateFormat(fmt || this.format);
18573     },
18574     
18575     onFocus : function()
18576     {
18577         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18578         this.show();
18579     },
18580     
18581     onBlur : function()
18582     {
18583         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18584         
18585         var d = this.inputEl().getValue();
18586         
18587         this.setValue(d);
18588                 
18589         this.hide();
18590     },
18591     
18592     show : function()
18593     {
18594         this.picker().show();
18595         this.update();
18596         this.place();
18597         
18598         this.fireEvent('show', this, this.date);
18599     },
18600     
18601     hide : function()
18602     {
18603         if(this.isInline) {
18604             return;
18605         }
18606         this.picker().hide();
18607         this.viewMode = this.startViewMode;
18608         this.showMode();
18609         
18610         this.fireEvent('hide', this, this.date);
18611         
18612     },
18613     
18614     onMousedown: function(e)
18615     {
18616         e.stopPropagation();
18617         e.preventDefault();
18618     },
18619     
18620     keyup: function(e)
18621     {
18622         Roo.bootstrap.DateField.superclass.keyup.call(this);
18623         this.update();
18624     },
18625
18626     setValue: function(v)
18627     {
18628         if(this.fireEvent('beforeselect', this, v) !== false){
18629             var d = new Date(this.parseDate(v) ).clearTime();
18630         
18631             if(isNaN(d.getTime())){
18632                 this.date = this.viewDate = '';
18633                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18634                 return;
18635             }
18636
18637             v = this.formatDate(d);
18638
18639             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18640
18641             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18642
18643             this.update();
18644
18645             this.fireEvent('select', this, this.date);
18646         }
18647     },
18648     
18649     getValue: function()
18650     {
18651         return this.formatDate(this.date);
18652     },
18653     
18654     fireKey: function(e)
18655     {
18656         if (!this.picker().isVisible()){
18657             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18658                 this.show();
18659             }
18660             return;
18661         }
18662         
18663         var dateChanged = false,
18664         dir, day, month,
18665         newDate, newViewDate;
18666         
18667         switch(e.keyCode){
18668             case 27: // escape
18669                 this.hide();
18670                 e.preventDefault();
18671                 break;
18672             case 37: // left
18673             case 39: // right
18674                 if (!this.keyboardNavigation) {
18675                     break;
18676                 }
18677                 dir = e.keyCode == 37 ? -1 : 1;
18678                 
18679                 if (e.ctrlKey){
18680                     newDate = this.moveYear(this.date, dir);
18681                     newViewDate = this.moveYear(this.viewDate, dir);
18682                 } else if (e.shiftKey){
18683                     newDate = this.moveMonth(this.date, dir);
18684                     newViewDate = this.moveMonth(this.viewDate, dir);
18685                 } else {
18686                     newDate = new Date(this.date);
18687                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18688                     newViewDate = new Date(this.viewDate);
18689                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18690                 }
18691                 if (this.dateWithinRange(newDate)){
18692                     this.date = newDate;
18693                     this.viewDate = newViewDate;
18694                     this.setValue(this.formatDate(this.date));
18695 //                    this.update();
18696                     e.preventDefault();
18697                     dateChanged = true;
18698                 }
18699                 break;
18700             case 38: // up
18701             case 40: // down
18702                 if (!this.keyboardNavigation) {
18703                     break;
18704                 }
18705                 dir = e.keyCode == 38 ? -1 : 1;
18706                 if (e.ctrlKey){
18707                     newDate = this.moveYear(this.date, dir);
18708                     newViewDate = this.moveYear(this.viewDate, dir);
18709                 } else if (e.shiftKey){
18710                     newDate = this.moveMonth(this.date, dir);
18711                     newViewDate = this.moveMonth(this.viewDate, dir);
18712                 } else {
18713                     newDate = new Date(this.date);
18714                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18715                     newViewDate = new Date(this.viewDate);
18716                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18717                 }
18718                 if (this.dateWithinRange(newDate)){
18719                     this.date = newDate;
18720                     this.viewDate = newViewDate;
18721                     this.setValue(this.formatDate(this.date));
18722 //                    this.update();
18723                     e.preventDefault();
18724                     dateChanged = true;
18725                 }
18726                 break;
18727             case 13: // enter
18728                 this.setValue(this.formatDate(this.date));
18729                 this.hide();
18730                 e.preventDefault();
18731                 break;
18732             case 9: // tab
18733                 this.setValue(this.formatDate(this.date));
18734                 this.hide();
18735                 break;
18736             case 16: // shift
18737             case 17: // ctrl
18738             case 18: // alt
18739                 break;
18740             default :
18741                 this.hide();
18742                 
18743         }
18744     },
18745     
18746     
18747     onClick: function(e) 
18748     {
18749         e.stopPropagation();
18750         e.preventDefault();
18751         
18752         var target = e.getTarget();
18753         
18754         if(target.nodeName.toLowerCase() === 'i'){
18755             target = Roo.get(target).dom.parentNode;
18756         }
18757         
18758         var nodeName = target.nodeName;
18759         var className = target.className;
18760         var html = target.innerHTML;
18761         //Roo.log(nodeName);
18762         
18763         switch(nodeName.toLowerCase()) {
18764             case 'th':
18765                 switch(className) {
18766                     case 'switch':
18767                         this.showMode(1);
18768                         break;
18769                     case 'prev':
18770                     case 'next':
18771                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18772                         switch(this.viewMode){
18773                                 case 0:
18774                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18775                                         break;
18776                                 case 1:
18777                                 case 2:
18778                                         this.viewDate = this.moveYear(this.viewDate, dir);
18779                                         break;
18780                         }
18781                         this.fill();
18782                         break;
18783                     case 'today':
18784                         var date = new Date();
18785                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18786 //                        this.fill()
18787                         this.setValue(this.formatDate(this.date));
18788                         
18789                         this.hide();
18790                         break;
18791                 }
18792                 break;
18793             case 'span':
18794                 if (className.indexOf('disabled') < 0) {
18795                     this.viewDate.setUTCDate(1);
18796                     if (className.indexOf('month') > -1) {
18797                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18798                     } else {
18799                         var year = parseInt(html, 10) || 0;
18800                         this.viewDate.setUTCFullYear(year);
18801                         
18802                     }
18803                     
18804                     if(this.singleMode){
18805                         this.setValue(this.formatDate(this.viewDate));
18806                         this.hide();
18807                         return;
18808                     }
18809                     
18810                     this.showMode(-1);
18811                     this.fill();
18812                 }
18813                 break;
18814                 
18815             case 'td':
18816                 //Roo.log(className);
18817                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18818                     var day = parseInt(html, 10) || 1;
18819                     var year = this.viewDate.getUTCFullYear(),
18820                         month = this.viewDate.getUTCMonth();
18821
18822                     if (className.indexOf('old') > -1) {
18823                         if(month === 0 ){
18824                             month = 11;
18825                             year -= 1;
18826                         }else{
18827                             month -= 1;
18828                         }
18829                     } else if (className.indexOf('new') > -1) {
18830                         if (month == 11) {
18831                             month = 0;
18832                             year += 1;
18833                         } else {
18834                             month += 1;
18835                         }
18836                     }
18837                     //Roo.log([year,month,day]);
18838                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18839                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18840 //                    this.fill();
18841                     //Roo.log(this.formatDate(this.date));
18842                     this.setValue(this.formatDate(this.date));
18843                     this.hide();
18844                 }
18845                 break;
18846         }
18847     },
18848     
18849     setStartDate: function(startDate)
18850     {
18851         this.startDate = startDate || -Infinity;
18852         if (this.startDate !== -Infinity) {
18853             this.startDate = this.parseDate(this.startDate);
18854         }
18855         this.update();
18856         this.updateNavArrows();
18857     },
18858
18859     setEndDate: function(endDate)
18860     {
18861         this.endDate = endDate || Infinity;
18862         if (this.endDate !== Infinity) {
18863             this.endDate = this.parseDate(this.endDate);
18864         }
18865         this.update();
18866         this.updateNavArrows();
18867     },
18868     
18869     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18870     {
18871         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18872         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18873             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18874         }
18875         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18876             return parseInt(d, 10);
18877         });
18878         this.update();
18879         this.updateNavArrows();
18880     },
18881     
18882     updateNavArrows: function() 
18883     {
18884         if(this.singleMode){
18885             return;
18886         }
18887         
18888         var d = new Date(this.viewDate),
18889         year = d.getUTCFullYear(),
18890         month = d.getUTCMonth();
18891         
18892         Roo.each(this.picker().select('.prev', true).elements, function(v){
18893             v.show();
18894             switch (this.viewMode) {
18895                 case 0:
18896
18897                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18898                         v.hide();
18899                     }
18900                     break;
18901                 case 1:
18902                 case 2:
18903                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18904                         v.hide();
18905                     }
18906                     break;
18907             }
18908         });
18909         
18910         Roo.each(this.picker().select('.next', true).elements, function(v){
18911             v.show();
18912             switch (this.viewMode) {
18913                 case 0:
18914
18915                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18916                         v.hide();
18917                     }
18918                     break;
18919                 case 1:
18920                 case 2:
18921                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18922                         v.hide();
18923                     }
18924                     break;
18925             }
18926         })
18927     },
18928     
18929     moveMonth: function(date, dir)
18930     {
18931         if (!dir) {
18932             return date;
18933         }
18934         var new_date = new Date(date.valueOf()),
18935         day = new_date.getUTCDate(),
18936         month = new_date.getUTCMonth(),
18937         mag = Math.abs(dir),
18938         new_month, test;
18939         dir = dir > 0 ? 1 : -1;
18940         if (mag == 1){
18941             test = dir == -1
18942             // If going back one month, make sure month is not current month
18943             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18944             ? function(){
18945                 return new_date.getUTCMonth() == month;
18946             }
18947             // If going forward one month, make sure month is as expected
18948             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18949             : function(){
18950                 return new_date.getUTCMonth() != new_month;
18951             };
18952             new_month = month + dir;
18953             new_date.setUTCMonth(new_month);
18954             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18955             if (new_month < 0 || new_month > 11) {
18956                 new_month = (new_month + 12) % 12;
18957             }
18958         } else {
18959             // For magnitudes >1, move one month at a time...
18960             for (var i=0; i<mag; i++) {
18961                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18962                 new_date = this.moveMonth(new_date, dir);
18963             }
18964             // ...then reset the day, keeping it in the new month
18965             new_month = new_date.getUTCMonth();
18966             new_date.setUTCDate(day);
18967             test = function(){
18968                 return new_month != new_date.getUTCMonth();
18969             };
18970         }
18971         // Common date-resetting loop -- if date is beyond end of month, make it
18972         // end of month
18973         while (test()){
18974             new_date.setUTCDate(--day);
18975             new_date.setUTCMonth(new_month);
18976         }
18977         return new_date;
18978     },
18979
18980     moveYear: function(date, dir)
18981     {
18982         return this.moveMonth(date, dir*12);
18983     },
18984
18985     dateWithinRange: function(date)
18986     {
18987         return date >= this.startDate && date <= this.endDate;
18988     },
18989
18990     
18991     remove: function() 
18992     {
18993         this.picker().remove();
18994     },
18995     
18996     validateValue : function(value)
18997     {
18998         if(value.length < 1)  {
18999             if(this.allowBlank){
19000                 return true;
19001             }
19002             return false;
19003         }
19004         
19005         if(value.length < this.minLength){
19006             return false;
19007         }
19008         if(value.length > this.maxLength){
19009             return false;
19010         }
19011         if(this.vtype){
19012             var vt = Roo.form.VTypes;
19013             if(!vt[this.vtype](value, this)){
19014                 return false;
19015             }
19016         }
19017         if(typeof this.validator == "function"){
19018             var msg = this.validator(value);
19019             if(msg !== true){
19020                 return false;
19021             }
19022         }
19023         
19024         if(this.regex && !this.regex.test(value)){
19025             return false;
19026         }
19027         
19028         if(typeof(this.parseDate(value)) == 'undefined'){
19029             return false;
19030         }
19031         
19032         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19033             return false;
19034         }      
19035         
19036         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19037             return false;
19038         } 
19039         
19040         
19041         return true;
19042     }
19043    
19044 });
19045
19046 Roo.apply(Roo.bootstrap.DateField,  {
19047     
19048     head : {
19049         tag: 'thead',
19050         cn: [
19051         {
19052             tag: 'tr',
19053             cn: [
19054             {
19055                 tag: 'th',
19056                 cls: 'prev',
19057                 html: '<i class="fa fa-arrow-left"/>'
19058             },
19059             {
19060                 tag: 'th',
19061                 cls: 'switch',
19062                 colspan: '5'
19063             },
19064             {
19065                 tag: 'th',
19066                 cls: 'next',
19067                 html: '<i class="fa fa-arrow-right"/>'
19068             }
19069
19070             ]
19071         }
19072         ]
19073     },
19074     
19075     content : {
19076         tag: 'tbody',
19077         cn: [
19078         {
19079             tag: 'tr',
19080             cn: [
19081             {
19082                 tag: 'td',
19083                 colspan: '7'
19084             }
19085             ]
19086         }
19087         ]
19088     },
19089     
19090     footer : {
19091         tag: 'tfoot',
19092         cn: [
19093         {
19094             tag: 'tr',
19095             cn: [
19096             {
19097                 tag: 'th',
19098                 colspan: '7',
19099                 cls: 'today'
19100             }
19101                     
19102             ]
19103         }
19104         ]
19105     },
19106     
19107     dates:{
19108         en: {
19109             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19110             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19111             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19112             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19113             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19114             today: "Today"
19115         }
19116     },
19117     
19118     modes: [
19119     {
19120         clsName: 'days',
19121         navFnc: 'Month',
19122         navStep: 1
19123     },
19124     {
19125         clsName: 'months',
19126         navFnc: 'FullYear',
19127         navStep: 1
19128     },
19129     {
19130         clsName: 'years',
19131         navFnc: 'FullYear',
19132         navStep: 10
19133     }]
19134 });
19135
19136 Roo.apply(Roo.bootstrap.DateField,  {
19137   
19138     template : {
19139         tag: 'div',
19140         cls: 'datepicker dropdown-menu roo-dynamic',
19141         cn: [
19142         {
19143             tag: 'div',
19144             cls: 'datepicker-days',
19145             cn: [
19146             {
19147                 tag: 'table',
19148                 cls: 'table-condensed',
19149                 cn:[
19150                 Roo.bootstrap.DateField.head,
19151                 {
19152                     tag: 'tbody'
19153                 },
19154                 Roo.bootstrap.DateField.footer
19155                 ]
19156             }
19157             ]
19158         },
19159         {
19160             tag: 'div',
19161             cls: 'datepicker-months',
19162             cn: [
19163             {
19164                 tag: 'table',
19165                 cls: 'table-condensed',
19166                 cn:[
19167                 Roo.bootstrap.DateField.head,
19168                 Roo.bootstrap.DateField.content,
19169                 Roo.bootstrap.DateField.footer
19170                 ]
19171             }
19172             ]
19173         },
19174         {
19175             tag: 'div',
19176             cls: 'datepicker-years',
19177             cn: [
19178             {
19179                 tag: 'table',
19180                 cls: 'table-condensed',
19181                 cn:[
19182                 Roo.bootstrap.DateField.head,
19183                 Roo.bootstrap.DateField.content,
19184                 Roo.bootstrap.DateField.footer
19185                 ]
19186             }
19187             ]
19188         }
19189         ]
19190     }
19191 });
19192
19193  
19194
19195  /*
19196  * - LGPL
19197  *
19198  * TimeField
19199  * 
19200  */
19201
19202 /**
19203  * @class Roo.bootstrap.TimeField
19204  * @extends Roo.bootstrap.Input
19205  * Bootstrap DateField class
19206  * 
19207  * 
19208  * @constructor
19209  * Create a new TimeField
19210  * @param {Object} config The config object
19211  */
19212
19213 Roo.bootstrap.TimeField = function(config){
19214     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19215     this.addEvents({
19216             /**
19217              * @event show
19218              * Fires when this field show.
19219              * @param {Roo.bootstrap.DateField} thisthis
19220              * @param {Mixed} date The date value
19221              */
19222             show : true,
19223             /**
19224              * @event show
19225              * Fires when this field hide.
19226              * @param {Roo.bootstrap.DateField} this
19227              * @param {Mixed} date The date value
19228              */
19229             hide : true,
19230             /**
19231              * @event select
19232              * Fires when select a date.
19233              * @param {Roo.bootstrap.DateField} this
19234              * @param {Mixed} date The date value
19235              */
19236             select : true
19237         });
19238 };
19239
19240 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19241     
19242     /**
19243      * @cfg {String} format
19244      * The default time format string which can be overriden for localization support.  The format must be
19245      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19246      */
19247     format : "H:i",
19248        
19249     onRender: function(ct, position)
19250     {
19251         
19252         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19253                 
19254         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19255         
19256         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19257         
19258         this.pop = this.picker().select('>.datepicker-time',true).first();
19259         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19260         
19261         this.picker().on('mousedown', this.onMousedown, this);
19262         this.picker().on('click', this.onClick, this);
19263         
19264         this.picker().addClass('datepicker-dropdown');
19265     
19266         this.fillTime();
19267         this.update();
19268             
19269         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19270         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19271         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19272         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19273         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19274         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19275
19276     },
19277     
19278     fireKey: function(e){
19279         if (!this.picker().isVisible()){
19280             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19281                 this.show();
19282             }
19283             return;
19284         }
19285
19286         e.preventDefault();
19287         
19288         switch(e.keyCode){
19289             case 27: // escape
19290                 this.hide();
19291                 break;
19292             case 37: // left
19293             case 39: // right
19294                 this.onTogglePeriod();
19295                 break;
19296             case 38: // up
19297                 this.onIncrementMinutes();
19298                 break;
19299             case 40: // down
19300                 this.onDecrementMinutes();
19301                 break;
19302             case 13: // enter
19303             case 9: // tab
19304                 this.setTime();
19305                 break;
19306         }
19307     },
19308     
19309     onClick: function(e) {
19310         e.stopPropagation();
19311         e.preventDefault();
19312     },
19313     
19314     picker : function()
19315     {
19316         return this.el.select('.datepicker', true).first();
19317     },
19318     
19319     fillTime: function()
19320     {    
19321         var time = this.pop.select('tbody', true).first();
19322         
19323         time.dom.innerHTML = '';
19324         
19325         time.createChild({
19326             tag: 'tr',
19327             cn: [
19328                 {
19329                     tag: 'td',
19330                     cn: [
19331                         {
19332                             tag: 'a',
19333                             href: '#',
19334                             cls: 'btn',
19335                             cn: [
19336                                 {
19337                                     tag: 'span',
19338                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19339                                 }
19340                             ]
19341                         } 
19342                     ]
19343                 },
19344                 {
19345                     tag: 'td',
19346                     cls: 'separator'
19347                 },
19348                 {
19349                     tag: 'td',
19350                     cn: [
19351                         {
19352                             tag: 'a',
19353                             href: '#',
19354                             cls: 'btn',
19355                             cn: [
19356                                 {
19357                                     tag: 'span',
19358                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19359                                 }
19360                             ]
19361                         }
19362                     ]
19363                 },
19364                 {
19365                     tag: 'td',
19366                     cls: 'separator'
19367                 }
19368             ]
19369         });
19370         
19371         time.createChild({
19372             tag: 'tr',
19373             cn: [
19374                 {
19375                     tag: 'td',
19376                     cn: [
19377                         {
19378                             tag: 'span',
19379                             cls: 'timepicker-hour',
19380                             html: '00'
19381                         }  
19382                     ]
19383                 },
19384                 {
19385                     tag: 'td',
19386                     cls: 'separator',
19387                     html: ':'
19388                 },
19389                 {
19390                     tag: 'td',
19391                     cn: [
19392                         {
19393                             tag: 'span',
19394                             cls: 'timepicker-minute',
19395                             html: '00'
19396                         }  
19397                     ]
19398                 },
19399                 {
19400                     tag: 'td',
19401                     cls: 'separator'
19402                 },
19403                 {
19404                     tag: 'td',
19405                     cn: [
19406                         {
19407                             tag: 'button',
19408                             type: 'button',
19409                             cls: 'btn btn-primary period',
19410                             html: 'AM'
19411                             
19412                         }
19413                     ]
19414                 }
19415             ]
19416         });
19417         
19418         time.createChild({
19419             tag: 'tr',
19420             cn: [
19421                 {
19422                     tag: 'td',
19423                     cn: [
19424                         {
19425                             tag: 'a',
19426                             href: '#',
19427                             cls: 'btn',
19428                             cn: [
19429                                 {
19430                                     tag: 'span',
19431                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19432                                 }
19433                             ]
19434                         }
19435                     ]
19436                 },
19437                 {
19438                     tag: 'td',
19439                     cls: 'separator'
19440                 },
19441                 {
19442                     tag: 'td',
19443                     cn: [
19444                         {
19445                             tag: 'a',
19446                             href: '#',
19447                             cls: 'btn',
19448                             cn: [
19449                                 {
19450                                     tag: 'span',
19451                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19452                                 }
19453                             ]
19454                         }
19455                     ]
19456                 },
19457                 {
19458                     tag: 'td',
19459                     cls: 'separator'
19460                 }
19461             ]
19462         });
19463         
19464     },
19465     
19466     update: function()
19467     {
19468         
19469         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19470         
19471         this.fill();
19472     },
19473     
19474     fill: function() 
19475     {
19476         var hours = this.time.getHours();
19477         var minutes = this.time.getMinutes();
19478         var period = 'AM';
19479         
19480         if(hours > 11){
19481             period = 'PM';
19482         }
19483         
19484         if(hours == 0){
19485             hours = 12;
19486         }
19487         
19488         
19489         if(hours > 12){
19490             hours = hours - 12;
19491         }
19492         
19493         if(hours < 10){
19494             hours = '0' + hours;
19495         }
19496         
19497         if(minutes < 10){
19498             minutes = '0' + minutes;
19499         }
19500         
19501         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19502         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19503         this.pop.select('button', true).first().dom.innerHTML = period;
19504         
19505     },
19506     
19507     place: function()
19508     {   
19509         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19510         
19511         var cls = ['bottom'];
19512         
19513         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19514             cls.pop();
19515             cls.push('top');
19516         }
19517         
19518         cls.push('right');
19519         
19520         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19521             cls.pop();
19522             cls.push('left');
19523         }
19524         
19525         this.picker().addClass(cls.join('-'));
19526         
19527         var _this = this;
19528         
19529         Roo.each(cls, function(c){
19530             if(c == 'bottom'){
19531                 _this.picker().setTop(_this.inputEl().getHeight());
19532                 return;
19533             }
19534             if(c == 'top'){
19535                 _this.picker().setTop(0 - _this.picker().getHeight());
19536                 return;
19537             }
19538             
19539             if(c == 'left'){
19540                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19541                 return;
19542             }
19543             if(c == 'right'){
19544                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19545                 return;
19546             }
19547         });
19548         
19549     },
19550   
19551     onFocus : function()
19552     {
19553         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19554         this.show();
19555     },
19556     
19557     onBlur : function()
19558     {
19559         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19560         this.hide();
19561     },
19562     
19563     show : function()
19564     {
19565         this.picker().show();
19566         this.pop.show();
19567         this.update();
19568         this.place();
19569         
19570         this.fireEvent('show', this, this.date);
19571     },
19572     
19573     hide : function()
19574     {
19575         this.picker().hide();
19576         this.pop.hide();
19577         
19578         this.fireEvent('hide', this, this.date);
19579     },
19580     
19581     setTime : function()
19582     {
19583         this.hide();
19584         this.setValue(this.time.format(this.format));
19585         
19586         this.fireEvent('select', this, this.date);
19587         
19588         
19589     },
19590     
19591     onMousedown: function(e){
19592         e.stopPropagation();
19593         e.preventDefault();
19594     },
19595     
19596     onIncrementHours: function()
19597     {
19598         Roo.log('onIncrementHours');
19599         this.time = this.time.add(Date.HOUR, 1);
19600         this.update();
19601         
19602     },
19603     
19604     onDecrementHours: function()
19605     {
19606         Roo.log('onDecrementHours');
19607         this.time = this.time.add(Date.HOUR, -1);
19608         this.update();
19609     },
19610     
19611     onIncrementMinutes: function()
19612     {
19613         Roo.log('onIncrementMinutes');
19614         this.time = this.time.add(Date.MINUTE, 1);
19615         this.update();
19616     },
19617     
19618     onDecrementMinutes: function()
19619     {
19620         Roo.log('onDecrementMinutes');
19621         this.time = this.time.add(Date.MINUTE, -1);
19622         this.update();
19623     },
19624     
19625     onTogglePeriod: function()
19626     {
19627         Roo.log('onTogglePeriod');
19628         this.time = this.time.add(Date.HOUR, 12);
19629         this.update();
19630     }
19631     
19632    
19633 });
19634
19635 Roo.apply(Roo.bootstrap.TimeField,  {
19636     
19637     content : {
19638         tag: 'tbody',
19639         cn: [
19640             {
19641                 tag: 'tr',
19642                 cn: [
19643                 {
19644                     tag: 'td',
19645                     colspan: '7'
19646                 }
19647                 ]
19648             }
19649         ]
19650     },
19651     
19652     footer : {
19653         tag: 'tfoot',
19654         cn: [
19655             {
19656                 tag: 'tr',
19657                 cn: [
19658                 {
19659                     tag: 'th',
19660                     colspan: '7',
19661                     cls: '',
19662                     cn: [
19663                         {
19664                             tag: 'button',
19665                             cls: 'btn btn-info ok',
19666                             html: 'OK'
19667                         }
19668                     ]
19669                 }
19670
19671                 ]
19672             }
19673         ]
19674     }
19675 });
19676
19677 Roo.apply(Roo.bootstrap.TimeField,  {
19678   
19679     template : {
19680         tag: 'div',
19681         cls: 'datepicker dropdown-menu',
19682         cn: [
19683             {
19684                 tag: 'div',
19685                 cls: 'datepicker-time',
19686                 cn: [
19687                 {
19688                     tag: 'table',
19689                     cls: 'table-condensed',
19690                     cn:[
19691                     Roo.bootstrap.TimeField.content,
19692                     Roo.bootstrap.TimeField.footer
19693                     ]
19694                 }
19695                 ]
19696             }
19697         ]
19698     }
19699 });
19700
19701  
19702
19703  /*
19704  * - LGPL
19705  *
19706  * MonthField
19707  * 
19708  */
19709
19710 /**
19711  * @class Roo.bootstrap.MonthField
19712  * @extends Roo.bootstrap.Input
19713  * Bootstrap MonthField class
19714  * 
19715  * @cfg {String} language default en
19716  * 
19717  * @constructor
19718  * Create a new MonthField
19719  * @param {Object} config The config object
19720  */
19721
19722 Roo.bootstrap.MonthField = function(config){
19723     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19724     
19725     this.addEvents({
19726         /**
19727          * @event show
19728          * Fires when this field show.
19729          * @param {Roo.bootstrap.MonthField} this
19730          * @param {Mixed} date The date value
19731          */
19732         show : true,
19733         /**
19734          * @event show
19735          * Fires when this field hide.
19736          * @param {Roo.bootstrap.MonthField} this
19737          * @param {Mixed} date The date value
19738          */
19739         hide : true,
19740         /**
19741          * @event select
19742          * Fires when select a date.
19743          * @param {Roo.bootstrap.MonthField} this
19744          * @param {String} oldvalue The old value
19745          * @param {String} newvalue The new value
19746          */
19747         select : true
19748     });
19749 };
19750
19751 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19752     
19753     onRender: function(ct, position)
19754     {
19755         
19756         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19757         
19758         this.language = this.language || 'en';
19759         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19760         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19761         
19762         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19763         this.isInline = false;
19764         this.isInput = true;
19765         this.component = this.el.select('.add-on', true).first() || false;
19766         this.component = (this.component && this.component.length === 0) ? false : this.component;
19767         this.hasInput = this.component && this.inputEL().length;
19768         
19769         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19770         
19771         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19772         
19773         this.picker().on('mousedown', this.onMousedown, this);
19774         this.picker().on('click', this.onClick, this);
19775         
19776         this.picker().addClass('datepicker-dropdown');
19777         
19778         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19779             v.setStyle('width', '189px');
19780         });
19781         
19782         this.fillMonths();
19783         
19784         this.update();
19785         
19786         if(this.isInline) {
19787             this.show();
19788         }
19789         
19790     },
19791     
19792     setValue: function(v, suppressEvent)
19793     {   
19794         var o = this.getValue();
19795         
19796         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19797         
19798         this.update();
19799
19800         if(suppressEvent !== true){
19801             this.fireEvent('select', this, o, v);
19802         }
19803         
19804     },
19805     
19806     getValue: function()
19807     {
19808         return this.value;
19809     },
19810     
19811     onClick: function(e) 
19812     {
19813         e.stopPropagation();
19814         e.preventDefault();
19815         
19816         var target = e.getTarget();
19817         
19818         if(target.nodeName.toLowerCase() === 'i'){
19819             target = Roo.get(target).dom.parentNode;
19820         }
19821         
19822         var nodeName = target.nodeName;
19823         var className = target.className;
19824         var html = target.innerHTML;
19825         
19826         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19827             return;
19828         }
19829         
19830         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19831         
19832         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19833         
19834         this.hide();
19835                         
19836     },
19837     
19838     picker : function()
19839     {
19840         return this.pickerEl;
19841     },
19842     
19843     fillMonths: function()
19844     {    
19845         var i = 0;
19846         var months = this.picker().select('>.datepicker-months td', true).first();
19847         
19848         months.dom.innerHTML = '';
19849         
19850         while (i < 12) {
19851             var month = {
19852                 tag: 'span',
19853                 cls: 'month',
19854                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19855             };
19856             
19857             months.createChild(month);
19858         }
19859         
19860     },
19861     
19862     update: function()
19863     {
19864         var _this = this;
19865         
19866         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19867             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19868         }
19869         
19870         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19871             e.removeClass('active');
19872             
19873             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19874                 e.addClass('active');
19875             }
19876         })
19877     },
19878     
19879     place: function()
19880     {
19881         if(this.isInline) {
19882             return;
19883         }
19884         
19885         this.picker().removeClass(['bottom', 'top']);
19886         
19887         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19888             /*
19889              * place to the top of element!
19890              *
19891              */
19892             
19893             this.picker().addClass('top');
19894             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19895             
19896             return;
19897         }
19898         
19899         this.picker().addClass('bottom');
19900         
19901         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19902     },
19903     
19904     onFocus : function()
19905     {
19906         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19907         this.show();
19908     },
19909     
19910     onBlur : function()
19911     {
19912         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19913         
19914         var d = this.inputEl().getValue();
19915         
19916         this.setValue(d);
19917                 
19918         this.hide();
19919     },
19920     
19921     show : function()
19922     {
19923         this.picker().show();
19924         this.picker().select('>.datepicker-months', true).first().show();
19925         this.update();
19926         this.place();
19927         
19928         this.fireEvent('show', this, this.date);
19929     },
19930     
19931     hide : function()
19932     {
19933         if(this.isInline) {
19934             return;
19935         }
19936         this.picker().hide();
19937         this.fireEvent('hide', this, this.date);
19938         
19939     },
19940     
19941     onMousedown: function(e)
19942     {
19943         e.stopPropagation();
19944         e.preventDefault();
19945     },
19946     
19947     keyup: function(e)
19948     {
19949         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19950         this.update();
19951     },
19952
19953     fireKey: function(e)
19954     {
19955         if (!this.picker().isVisible()){
19956             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19957                 this.show();
19958             }
19959             return;
19960         }
19961         
19962         var dir;
19963         
19964         switch(e.keyCode){
19965             case 27: // escape
19966                 this.hide();
19967                 e.preventDefault();
19968                 break;
19969             case 37: // left
19970             case 39: // right
19971                 dir = e.keyCode == 37 ? -1 : 1;
19972                 
19973                 this.vIndex = this.vIndex + dir;
19974                 
19975                 if(this.vIndex < 0){
19976                     this.vIndex = 0;
19977                 }
19978                 
19979                 if(this.vIndex > 11){
19980                     this.vIndex = 11;
19981                 }
19982                 
19983                 if(isNaN(this.vIndex)){
19984                     this.vIndex = 0;
19985                 }
19986                 
19987                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19988                 
19989                 break;
19990             case 38: // up
19991             case 40: // down
19992                 
19993                 dir = e.keyCode == 38 ? -1 : 1;
19994                 
19995                 this.vIndex = this.vIndex + dir * 4;
19996                 
19997                 if(this.vIndex < 0){
19998                     this.vIndex = 0;
19999                 }
20000                 
20001                 if(this.vIndex > 11){
20002                     this.vIndex = 11;
20003                 }
20004                 
20005                 if(isNaN(this.vIndex)){
20006                     this.vIndex = 0;
20007                 }
20008                 
20009                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20010                 break;
20011                 
20012             case 13: // enter
20013                 
20014                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20015                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20016                 }
20017                 
20018                 this.hide();
20019                 e.preventDefault();
20020                 break;
20021             case 9: // tab
20022                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20023                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20024                 }
20025                 this.hide();
20026                 break;
20027             case 16: // shift
20028             case 17: // ctrl
20029             case 18: // alt
20030                 break;
20031             default :
20032                 this.hide();
20033                 
20034         }
20035     },
20036     
20037     remove: function() 
20038     {
20039         this.picker().remove();
20040     }
20041    
20042 });
20043
20044 Roo.apply(Roo.bootstrap.MonthField,  {
20045     
20046     content : {
20047         tag: 'tbody',
20048         cn: [
20049         {
20050             tag: 'tr',
20051             cn: [
20052             {
20053                 tag: 'td',
20054                 colspan: '7'
20055             }
20056             ]
20057         }
20058         ]
20059     },
20060     
20061     dates:{
20062         en: {
20063             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20064             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20065         }
20066     }
20067 });
20068
20069 Roo.apply(Roo.bootstrap.MonthField,  {
20070   
20071     template : {
20072         tag: 'div',
20073         cls: 'datepicker dropdown-menu roo-dynamic',
20074         cn: [
20075             {
20076                 tag: 'div',
20077                 cls: 'datepicker-months',
20078                 cn: [
20079                 {
20080                     tag: 'table',
20081                     cls: 'table-condensed',
20082                     cn:[
20083                         Roo.bootstrap.DateField.content
20084                     ]
20085                 }
20086                 ]
20087             }
20088         ]
20089     }
20090 });
20091
20092  
20093
20094  
20095  /*
20096  * - LGPL
20097  *
20098  * CheckBox
20099  * 
20100  */
20101
20102 /**
20103  * @class Roo.bootstrap.CheckBox
20104  * @extends Roo.bootstrap.Input
20105  * Bootstrap CheckBox class
20106  * 
20107  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20108  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20109  * @cfg {String} boxLabel The text that appears beside the checkbox
20110  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20111  * @cfg {Boolean} checked initnal the element
20112  * @cfg {Boolean} inline inline the element (default false)
20113  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20114  * 
20115  * @constructor
20116  * Create a new CheckBox
20117  * @param {Object} config The config object
20118  */
20119
20120 Roo.bootstrap.CheckBox = function(config){
20121     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20122    
20123     this.addEvents({
20124         /**
20125         * @event check
20126         * Fires when the element is checked or unchecked.
20127         * @param {Roo.bootstrap.CheckBox} this This input
20128         * @param {Boolean} checked The new checked value
20129         */
20130        check : true
20131     });
20132     
20133 };
20134
20135 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20136   
20137     inputType: 'checkbox',
20138     inputValue: 1,
20139     valueOff: 0,
20140     boxLabel: false,
20141     checked: false,
20142     weight : false,
20143     inline: false,
20144     
20145     getAutoCreate : function()
20146     {
20147         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20148         
20149         var id = Roo.id();
20150         
20151         var cfg = {};
20152         
20153         cfg.cls = 'form-group ' + this.inputType; //input-group
20154         
20155         if(this.inline){
20156             cfg.cls += ' ' + this.inputType + '-inline';
20157         }
20158         
20159         var input =  {
20160             tag: 'input',
20161             id : id,
20162             type : this.inputType,
20163             value : this.inputValue,
20164             cls : 'roo-' + this.inputType, //'form-box',
20165             placeholder : this.placeholder || ''
20166             
20167         };
20168         
20169         if(this.inputType != 'radio'){
20170             var hidden =  {
20171                 tag: 'input',
20172                 type : 'hidden',
20173                 cls : 'roo-hidden-value',
20174                 value : this.checked ? this.valueOff : this.inputValue
20175             };
20176         }
20177         
20178             
20179         if (this.weight) { // Validity check?
20180             cfg.cls += " " + this.inputType + "-" + this.weight;
20181         }
20182         
20183         if (this.disabled) {
20184             input.disabled=true;
20185         }
20186         
20187         if(this.checked){
20188             input.checked = this.checked;
20189             
20190         }
20191         
20192         
20193         if (this.name) {
20194             
20195             input.name = this.name;
20196             
20197             if(this.inputType != 'radio'){
20198                 hidden.name = this.name;
20199                 input.name = '_hidden_' + this.name;
20200             }
20201         }
20202         
20203         if (this.size) {
20204             input.cls += ' input-' + this.size;
20205         }
20206         
20207         var settings=this;
20208         
20209         ['xs','sm','md','lg'].map(function(size){
20210             if (settings[size]) {
20211                 cfg.cls += ' col-' + size + '-' + settings[size];
20212             }
20213         });
20214         
20215         var inputblock = input;
20216          
20217         if (this.before || this.after) {
20218             
20219             inputblock = {
20220                 cls : 'input-group',
20221                 cn :  [] 
20222             };
20223             
20224             if (this.before) {
20225                 inputblock.cn.push({
20226                     tag :'span',
20227                     cls : 'input-group-addon',
20228                     html : this.before
20229                 });
20230             }
20231             
20232             inputblock.cn.push(input);
20233             
20234             if(this.inputType != 'radio'){
20235                 inputblock.cn.push(hidden);
20236             }
20237             
20238             if (this.after) {
20239                 inputblock.cn.push({
20240                     tag :'span',
20241                     cls : 'input-group-addon',
20242                     html : this.after
20243                 });
20244             }
20245             
20246         }
20247         
20248         if (align ==='left' && this.fieldLabel.length) {
20249 //                Roo.log("left and has label");
20250             cfg.cn = [
20251                 {
20252                     tag: 'label',
20253                     'for' :  id,
20254                     cls : 'control-label',
20255                     html : this.fieldLabel
20256
20257                 },
20258                 {
20259                     cls : "", 
20260                     cn: [
20261                         inputblock
20262                     ]
20263                 }
20264             ];
20265             
20266             if(this.labelWidth > 12){
20267                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20268             }
20269             
20270             if(this.labelWidth < 13 && this.labelmd == 0){
20271                 this.labelmd = this.labelWidth;
20272             }
20273             
20274             if(this.labellg > 0){
20275                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20276                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20277             }
20278             
20279             if(this.labelmd > 0){
20280                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20281                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20282             }
20283             
20284             if(this.labelsm > 0){
20285                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20286                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20287             }
20288             
20289             if(this.labelxs > 0){
20290                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20291                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20292             }
20293             
20294         } else if ( this.fieldLabel.length) {
20295 //                Roo.log(" label");
20296                 cfg.cn = [
20297                    
20298                     {
20299                         tag: this.boxLabel ? 'span' : 'label',
20300                         'for': id,
20301                         cls: 'control-label box-input-label',
20302                         //cls : 'input-group-addon',
20303                         html : this.fieldLabel
20304                         
20305                     },
20306                     
20307                     inputblock
20308                     
20309                 ];
20310
20311         } else {
20312             
20313 //                Roo.log(" no label && no align");
20314                 cfg.cn = [  inputblock ] ;
20315                 
20316                 
20317         }
20318         
20319         if(this.boxLabel){
20320              var boxLabelCfg = {
20321                 tag: 'label',
20322                 //'for': id, // box label is handled by onclick - so no for...
20323                 cls: 'box-label',
20324                 html: this.boxLabel
20325             };
20326             
20327             if(this.tooltip){
20328                 boxLabelCfg.tooltip = this.tooltip;
20329             }
20330              
20331             cfg.cn.push(boxLabelCfg);
20332         }
20333         
20334         if(this.inputType != 'radio'){
20335             cfg.cn.push(hidden);
20336         }
20337         
20338         return cfg;
20339         
20340     },
20341     
20342     /**
20343      * return the real input element.
20344      */
20345     inputEl: function ()
20346     {
20347         return this.el.select('input.roo-' + this.inputType,true).first();
20348     },
20349     hiddenEl: function ()
20350     {
20351         return this.el.select('input.roo-hidden-value',true).first();
20352     },
20353     
20354     labelEl: function()
20355     {
20356         return this.el.select('label.control-label',true).first();
20357     },
20358     /* depricated... */
20359     
20360     label: function()
20361     {
20362         return this.labelEl();
20363     },
20364     
20365     boxLabelEl: function()
20366     {
20367         return this.el.select('label.box-label',true).first();
20368     },
20369     
20370     initEvents : function()
20371     {
20372 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20373         
20374         this.inputEl().on('click', this.onClick,  this);
20375         
20376         if (this.boxLabel) { 
20377             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20378         }
20379         
20380         this.startValue = this.getValue();
20381         
20382         if(this.groupId){
20383             Roo.bootstrap.CheckBox.register(this);
20384         }
20385     },
20386     
20387     onClick : function()
20388     {   
20389         this.setChecked(!this.checked);
20390     },
20391     
20392     setChecked : function(state,suppressEvent)
20393     {
20394         this.startValue = this.getValue();
20395
20396         if(this.inputType == 'radio'){
20397             
20398             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20399                 e.dom.checked = false;
20400             });
20401             
20402             this.inputEl().dom.checked = true;
20403             
20404             this.inputEl().dom.value = this.inputValue;
20405             
20406             if(suppressEvent !== true){
20407                 this.fireEvent('check', this, true);
20408             }
20409             
20410             this.validate();
20411             
20412             return;
20413         }
20414         
20415         this.checked = state;
20416         
20417         this.inputEl().dom.checked = state;
20418         
20419         
20420         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20421         
20422         if(suppressEvent !== true){
20423             this.fireEvent('check', this, state);
20424         }
20425         
20426         this.validate();
20427     },
20428     
20429     getValue : function()
20430     {
20431         if(this.inputType == 'radio'){
20432             return this.getGroupValue();
20433         }
20434         
20435         return this.hiddenEl().dom.value;
20436         
20437     },
20438     
20439     getGroupValue : function()
20440     {
20441         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20442             return '';
20443         }
20444         
20445         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20446     },
20447     
20448     setValue : function(v,suppressEvent)
20449     {
20450         if(this.inputType == 'radio'){
20451             this.setGroupValue(v, suppressEvent);
20452             return;
20453         }
20454         
20455         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20456         
20457         this.validate();
20458     },
20459     
20460     setGroupValue : function(v, suppressEvent)
20461     {
20462         this.startValue = this.getValue();
20463         
20464         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20465             e.dom.checked = false;
20466             
20467             if(e.dom.value == v){
20468                 e.dom.checked = true;
20469             }
20470         });
20471         
20472         if(suppressEvent !== true){
20473             this.fireEvent('check', this, true);
20474         }
20475
20476         this.validate();
20477         
20478         return;
20479     },
20480     
20481     validate : function()
20482     {
20483         if(
20484                 this.disabled || 
20485                 (this.inputType == 'radio' && this.validateRadio()) ||
20486                 (this.inputType == 'checkbox' && this.validateCheckbox())
20487         ){
20488             this.markValid();
20489             return true;
20490         }
20491         
20492         this.markInvalid();
20493         return false;
20494     },
20495     
20496     validateRadio : function()
20497     {
20498         if(this.allowBlank){
20499             return true;
20500         }
20501         
20502         var valid = false;
20503         
20504         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20505             if(!e.dom.checked){
20506                 return;
20507             }
20508             
20509             valid = true;
20510             
20511             return false;
20512         });
20513         
20514         return valid;
20515     },
20516     
20517     validateCheckbox : function()
20518     {
20519         if(!this.groupId){
20520             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20521             //return (this.getValue() == this.inputValue) ? true : false;
20522         }
20523         
20524         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20525         
20526         if(!group){
20527             return false;
20528         }
20529         
20530         var r = false;
20531         
20532         for(var i in group){
20533             if(r){
20534                 break;
20535             }
20536             
20537             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20538         }
20539         
20540         return r;
20541     },
20542     
20543     /**
20544      * Mark this field as valid
20545      */
20546     markValid : function()
20547     {
20548         var _this = this;
20549         
20550         this.fireEvent('valid', this);
20551         
20552         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20553         
20554         if(this.groupId){
20555             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20556         }
20557         
20558         if(label){
20559             label.markValid();
20560         }
20561
20562         if(this.inputType == 'radio'){
20563             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20564                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20565                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20566             });
20567             
20568             return;
20569         }
20570
20571         if(!this.groupId){
20572             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20573             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20574             return;
20575         }
20576         
20577         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20578         
20579         if(!group){
20580             return;
20581         }
20582         
20583         for(var i in group){
20584             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20585             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20586         }
20587     },
20588     
20589      /**
20590      * Mark this field as invalid
20591      * @param {String} msg The validation message
20592      */
20593     markInvalid : function(msg)
20594     {
20595         if(this.allowBlank){
20596             return;
20597         }
20598         
20599         var _this = this;
20600         
20601         this.fireEvent('invalid', this, msg);
20602         
20603         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20604         
20605         if(this.groupId){
20606             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20607         }
20608         
20609         if(label){
20610             label.markInvalid();
20611         }
20612             
20613         if(this.inputType == 'radio'){
20614             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20615                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20616                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20617             });
20618             
20619             return;
20620         }
20621         
20622         if(!this.groupId){
20623             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20624             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20625             return;
20626         }
20627         
20628         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20629         
20630         if(!group){
20631             return;
20632         }
20633         
20634         for(var i in group){
20635             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20636             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20637         }
20638         
20639     },
20640     
20641     clearInvalid : function()
20642     {
20643         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20644         
20645         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20646         
20647         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20648         
20649         if (label) {
20650             label.iconEl.removeClass(label.validClass);
20651             label.iconEl.removeClass(label.invalidClass);
20652         }
20653     },
20654     
20655     disable : function()
20656     {
20657         if(this.inputType != 'radio'){
20658             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20659             return;
20660         }
20661         
20662         var _this = this;
20663         
20664         if(this.rendered){
20665             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20666                 _this.getActionEl().addClass(this.disabledClass);
20667                 e.dom.disabled = true;
20668             });
20669         }
20670         
20671         this.disabled = true;
20672         this.fireEvent("disable", this);
20673         return this;
20674     },
20675
20676     enable : function()
20677     {
20678         if(this.inputType != 'radio'){
20679             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20680             return;
20681         }
20682         
20683         var _this = this;
20684         
20685         if(this.rendered){
20686             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20687                 _this.getActionEl().removeClass(this.disabledClass);
20688                 e.dom.disabled = false;
20689             });
20690         }
20691         
20692         this.disabled = false;
20693         this.fireEvent("enable", this);
20694         return this;
20695     }
20696
20697 });
20698
20699 Roo.apply(Roo.bootstrap.CheckBox, {
20700     
20701     groups: {},
20702     
20703      /**
20704     * register a CheckBox Group
20705     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20706     */
20707     register : function(checkbox)
20708     {
20709         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20710             this.groups[checkbox.groupId] = {};
20711         }
20712         
20713         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20714             return;
20715         }
20716         
20717         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20718         
20719     },
20720     /**
20721     * fetch a CheckBox Group based on the group ID
20722     * @param {string} the group ID
20723     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20724     */
20725     get: function(groupId) {
20726         if (typeof(this.groups[groupId]) == 'undefined') {
20727             return false;
20728         }
20729         
20730         return this.groups[groupId] ;
20731     }
20732     
20733     
20734 });
20735 /*
20736  * - LGPL
20737  *
20738  * RadioItem
20739  * 
20740  */
20741
20742 /**
20743  * @class Roo.bootstrap.Radio
20744  * @extends Roo.bootstrap.Component
20745  * Bootstrap Radio class
20746  * @cfg {String} boxLabel - the label associated
20747  * @cfg {String} value - the value of radio
20748  * 
20749  * @constructor
20750  * Create a new Radio
20751  * @param {Object} config The config object
20752  */
20753 Roo.bootstrap.Radio = function(config){
20754     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20755     
20756 };
20757
20758 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20759     
20760     boxLabel : '',
20761     
20762     value : '',
20763     
20764     getAutoCreate : function()
20765     {
20766         var cfg = {
20767             tag : 'div',
20768             cls : 'form-group radio',
20769             cn : [
20770                 {
20771                     tag : 'label',
20772                     cls : 'box-label',
20773                     html : this.boxLabel
20774                 }
20775             ]
20776         };
20777         
20778         return cfg;
20779     },
20780     
20781     initEvents : function() 
20782     {
20783         this.parent().register(this);
20784         
20785         this.el.on('click', this.onClick, this);
20786         
20787     },
20788     
20789     onClick : function()
20790     {
20791         this.setChecked(true);
20792     },
20793     
20794     setChecked : function(state, suppressEvent)
20795     {
20796         this.parent().setValue(this.value, suppressEvent);
20797         
20798     },
20799     
20800     setBoxLabel : function(v)
20801     {
20802         this.boxLabel = v;
20803         
20804         if(this.rendered){
20805             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20806         }
20807     }
20808     
20809 });
20810  
20811
20812  /*
20813  * - LGPL
20814  *
20815  * Input
20816  * 
20817  */
20818
20819 /**
20820  * @class Roo.bootstrap.SecurePass
20821  * @extends Roo.bootstrap.Input
20822  * Bootstrap SecurePass class
20823  *
20824  * 
20825  * @constructor
20826  * Create a new SecurePass
20827  * @param {Object} config The config object
20828  */
20829  
20830 Roo.bootstrap.SecurePass = function (config) {
20831     // these go here, so the translation tool can replace them..
20832     this.errors = {
20833         PwdEmpty: "Please type a password, and then retype it to confirm.",
20834         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20835         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20836         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20837         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20838         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20839         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20840         TooWeak: "Your password is Too Weak."
20841     },
20842     this.meterLabel = "Password strength:";
20843     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20844     this.meterClass = [
20845         "roo-password-meter-tooweak", 
20846         "roo-password-meter-weak", 
20847         "roo-password-meter-medium", 
20848         "roo-password-meter-strong", 
20849         "roo-password-meter-grey"
20850     ];
20851     
20852     this.errors = {};
20853     
20854     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20855 }
20856
20857 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20858     /**
20859      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20860      * {
20861      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20862      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20863      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20864      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20865      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20866      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20867      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20868      * })
20869      */
20870     // private
20871     
20872     meterWidth: 300,
20873     errorMsg :'',    
20874     errors: false,
20875     imageRoot: '/',
20876     /**
20877      * @cfg {String/Object} Label for the strength meter (defaults to
20878      * 'Password strength:')
20879      */
20880     // private
20881     meterLabel: '',
20882     /**
20883      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20884      * ['Weak', 'Medium', 'Strong'])
20885      */
20886     // private    
20887     pwdStrengths: false,    
20888     // private
20889     strength: 0,
20890     // private
20891     _lastPwd: null,
20892     // private
20893     kCapitalLetter: 0,
20894     kSmallLetter: 1,
20895     kDigit: 2,
20896     kPunctuation: 3,
20897     
20898     insecure: false,
20899     // private
20900     initEvents: function ()
20901     {
20902         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20903
20904         if (this.el.is('input[type=password]') && Roo.isSafari) {
20905             this.el.on('keydown', this.SafariOnKeyDown, this);
20906         }
20907
20908         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20909     },
20910     // private
20911     onRender: function (ct, position)
20912     {
20913         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20914         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20915         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20916
20917         this.trigger.createChild({
20918                    cn: [
20919                     {
20920                     //id: 'PwdMeter',
20921                     tag: 'div',
20922                     cls: 'roo-password-meter-grey col-xs-12',
20923                     style: {
20924                         //width: 0,
20925                         //width: this.meterWidth + 'px'                                                
20926                         }
20927                     },
20928                     {                            
20929                          cls: 'roo-password-meter-text'                          
20930                     }
20931                 ]            
20932         });
20933
20934          
20935         if (this.hideTrigger) {
20936             this.trigger.setDisplayed(false);
20937         }
20938         this.setSize(this.width || '', this.height || '');
20939     },
20940     // private
20941     onDestroy: function ()
20942     {
20943         if (this.trigger) {
20944             this.trigger.removeAllListeners();
20945             this.trigger.remove();
20946         }
20947         if (this.wrap) {
20948             this.wrap.remove();
20949         }
20950         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20951     },
20952     // private
20953     checkStrength: function ()
20954     {
20955         var pwd = this.inputEl().getValue();
20956         if (pwd == this._lastPwd) {
20957             return;
20958         }
20959
20960         var strength;
20961         if (this.ClientSideStrongPassword(pwd)) {
20962             strength = 3;
20963         } else if (this.ClientSideMediumPassword(pwd)) {
20964             strength = 2;
20965         } else if (this.ClientSideWeakPassword(pwd)) {
20966             strength = 1;
20967         } else {
20968             strength = 0;
20969         }
20970         
20971         Roo.log('strength1: ' + strength);
20972         
20973         //var pm = this.trigger.child('div/div/div').dom;
20974         var pm = this.trigger.child('div/div');
20975         pm.removeClass(this.meterClass);
20976         pm.addClass(this.meterClass[strength]);
20977                 
20978         
20979         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20980                 
20981         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20982         
20983         this._lastPwd = pwd;
20984     },
20985     reset: function ()
20986     {
20987         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20988         
20989         this._lastPwd = '';
20990         
20991         var pm = this.trigger.child('div/div');
20992         pm.removeClass(this.meterClass);
20993         pm.addClass('roo-password-meter-grey');        
20994         
20995         
20996         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20997         
20998         pt.innerHTML = '';
20999         this.inputEl().dom.type='password';
21000     },
21001     // private
21002     validateValue: function (value)
21003     {
21004         
21005         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21006             return false;
21007         }
21008         if (value.length == 0) {
21009             if (this.allowBlank) {
21010                 this.clearInvalid();
21011                 return true;
21012             }
21013
21014             this.markInvalid(this.errors.PwdEmpty);
21015             this.errorMsg = this.errors.PwdEmpty;
21016             return false;
21017         }
21018         
21019         if(this.insecure){
21020             return true;
21021         }
21022         
21023         if ('[\x21-\x7e]*'.match(value)) {
21024             this.markInvalid(this.errors.PwdBadChar);
21025             this.errorMsg = this.errors.PwdBadChar;
21026             return false;
21027         }
21028         if (value.length < 6) {
21029             this.markInvalid(this.errors.PwdShort);
21030             this.errorMsg = this.errors.PwdShort;
21031             return false;
21032         }
21033         if (value.length > 16) {
21034             this.markInvalid(this.errors.PwdLong);
21035             this.errorMsg = this.errors.PwdLong;
21036             return false;
21037         }
21038         var strength;
21039         if (this.ClientSideStrongPassword(value)) {
21040             strength = 3;
21041         } else if (this.ClientSideMediumPassword(value)) {
21042             strength = 2;
21043         } else if (this.ClientSideWeakPassword(value)) {
21044             strength = 1;
21045         } else {
21046             strength = 0;
21047         }
21048
21049         
21050         if (strength < 2) {
21051             //this.markInvalid(this.errors.TooWeak);
21052             this.errorMsg = this.errors.TooWeak;
21053             //return false;
21054         }
21055         
21056         
21057         console.log('strength2: ' + strength);
21058         
21059         //var pm = this.trigger.child('div/div/div').dom;
21060         
21061         var pm = this.trigger.child('div/div');
21062         pm.removeClass(this.meterClass);
21063         pm.addClass(this.meterClass[strength]);
21064                 
21065         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21066                 
21067         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21068         
21069         this.errorMsg = ''; 
21070         return true;
21071     },
21072     // private
21073     CharacterSetChecks: function (type)
21074     {
21075         this.type = type;
21076         this.fResult = false;
21077     },
21078     // private
21079     isctype: function (character, type)
21080     {
21081         switch (type) {  
21082             case this.kCapitalLetter:
21083                 if (character >= 'A' && character <= 'Z') {
21084                     return true;
21085                 }
21086                 break;
21087             
21088             case this.kSmallLetter:
21089                 if (character >= 'a' && character <= 'z') {
21090                     return true;
21091                 }
21092                 break;
21093             
21094             case this.kDigit:
21095                 if (character >= '0' && character <= '9') {
21096                     return true;
21097                 }
21098                 break;
21099             
21100             case this.kPunctuation:
21101                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21102                     return true;
21103                 }
21104                 break;
21105             
21106             default:
21107                 return false;
21108         }
21109
21110     },
21111     // private
21112     IsLongEnough: function (pwd, size)
21113     {
21114         return !(pwd == null || isNaN(size) || pwd.length < size);
21115     },
21116     // private
21117     SpansEnoughCharacterSets: function (word, nb)
21118     {
21119         if (!this.IsLongEnough(word, nb))
21120         {
21121             return false;
21122         }
21123
21124         var characterSetChecks = new Array(
21125             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21126             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21127         );
21128         
21129         for (var index = 0; index < word.length; ++index) {
21130             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21131                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21132                     characterSetChecks[nCharSet].fResult = true;
21133                     break;
21134                 }
21135             }
21136         }
21137
21138         var nCharSets = 0;
21139         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21140             if (characterSetChecks[nCharSet].fResult) {
21141                 ++nCharSets;
21142             }
21143         }
21144
21145         if (nCharSets < nb) {
21146             return false;
21147         }
21148         return true;
21149     },
21150     // private
21151     ClientSideStrongPassword: function (pwd)
21152     {
21153         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21154     },
21155     // private
21156     ClientSideMediumPassword: function (pwd)
21157     {
21158         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21159     },
21160     // private
21161     ClientSideWeakPassword: function (pwd)
21162     {
21163         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21164     }
21165           
21166 })//<script type="text/javascript">
21167
21168 /*
21169  * Based  Ext JS Library 1.1.1
21170  * Copyright(c) 2006-2007, Ext JS, LLC.
21171  * LGPL
21172  *
21173  */
21174  
21175 /**
21176  * @class Roo.HtmlEditorCore
21177  * @extends Roo.Component
21178  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21179  *
21180  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21181  */
21182
21183 Roo.HtmlEditorCore = function(config){
21184     
21185     
21186     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21187     
21188     
21189     this.addEvents({
21190         /**
21191          * @event initialize
21192          * Fires when the editor is fully initialized (including the iframe)
21193          * @param {Roo.HtmlEditorCore} this
21194          */
21195         initialize: true,
21196         /**
21197          * @event activate
21198          * Fires when the editor is first receives the focus. Any insertion must wait
21199          * until after this event.
21200          * @param {Roo.HtmlEditorCore} this
21201          */
21202         activate: true,
21203          /**
21204          * @event beforesync
21205          * Fires before the textarea is updated with content from the editor iframe. Return false
21206          * to cancel the sync.
21207          * @param {Roo.HtmlEditorCore} this
21208          * @param {String} html
21209          */
21210         beforesync: true,
21211          /**
21212          * @event beforepush
21213          * Fires before the iframe editor is updated with content from the textarea. Return false
21214          * to cancel the push.
21215          * @param {Roo.HtmlEditorCore} this
21216          * @param {String} html
21217          */
21218         beforepush: true,
21219          /**
21220          * @event sync
21221          * Fires when the textarea is updated with content from the editor iframe.
21222          * @param {Roo.HtmlEditorCore} this
21223          * @param {String} html
21224          */
21225         sync: true,
21226          /**
21227          * @event push
21228          * Fires when the iframe editor is updated with content from the textarea.
21229          * @param {Roo.HtmlEditorCore} this
21230          * @param {String} html
21231          */
21232         push: true,
21233         
21234         /**
21235          * @event editorevent
21236          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21237          * @param {Roo.HtmlEditorCore} this
21238          */
21239         editorevent: true
21240         
21241     });
21242     
21243     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21244     
21245     // defaults : white / black...
21246     this.applyBlacklists();
21247     
21248     
21249     
21250 };
21251
21252
21253 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21254
21255
21256      /**
21257      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21258      */
21259     
21260     owner : false,
21261     
21262      /**
21263      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21264      *                        Roo.resizable.
21265      */
21266     resizable : false,
21267      /**
21268      * @cfg {Number} height (in pixels)
21269      */   
21270     height: 300,
21271    /**
21272      * @cfg {Number} width (in pixels)
21273      */   
21274     width: 500,
21275     
21276     /**
21277      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21278      * 
21279      */
21280     stylesheets: false,
21281     
21282     // id of frame..
21283     frameId: false,
21284     
21285     // private properties
21286     validationEvent : false,
21287     deferHeight: true,
21288     initialized : false,
21289     activated : false,
21290     sourceEditMode : false,
21291     onFocus : Roo.emptyFn,
21292     iframePad:3,
21293     hideMode:'offsets',
21294     
21295     clearUp: true,
21296     
21297     // blacklist + whitelisted elements..
21298     black: false,
21299     white: false,
21300      
21301     bodyCls : '',
21302
21303     /**
21304      * Protected method that will not generally be called directly. It
21305      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21306      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21307      */
21308     getDocMarkup : function(){
21309         // body styles..
21310         var st = '';
21311         
21312         // inherit styels from page...?? 
21313         if (this.stylesheets === false) {
21314             
21315             Roo.get(document.head).select('style').each(function(node) {
21316                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21317             });
21318             
21319             Roo.get(document.head).select('link').each(function(node) { 
21320                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21321             });
21322             
21323         } else if (!this.stylesheets.length) {
21324                 // simple..
21325                 st = '<style type="text/css">' +
21326                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21327                    '</style>';
21328         } else { 
21329             st = '<style type="text/css">' +
21330                     this.stylesheets +
21331                 '</style>';
21332         }
21333         
21334         st +=  '<style type="text/css">' +
21335             'IMG { cursor: pointer } ' +
21336         '</style>';
21337
21338         var cls = 'roo-htmleditor-body';
21339         
21340         if(this.bodyCls.length){
21341             cls += ' ' + this.bodyCls;
21342         }
21343         
21344         return '<html><head>' + st  +
21345             //<style type="text/css">' +
21346             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21347             //'</style>' +
21348             ' </head><body class="' +  cls + '"></body></html>';
21349     },
21350
21351     // private
21352     onRender : function(ct, position)
21353     {
21354         var _t = this;
21355         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21356         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21357         
21358         
21359         this.el.dom.style.border = '0 none';
21360         this.el.dom.setAttribute('tabIndex', -1);
21361         this.el.addClass('x-hidden hide');
21362         
21363         
21364         
21365         if(Roo.isIE){ // fix IE 1px bogus margin
21366             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21367         }
21368        
21369         
21370         this.frameId = Roo.id();
21371         
21372          
21373         
21374         var iframe = this.owner.wrap.createChild({
21375             tag: 'iframe',
21376             cls: 'form-control', // bootstrap..
21377             id: this.frameId,
21378             name: this.frameId,
21379             frameBorder : 'no',
21380             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21381         }, this.el
21382         );
21383         
21384         
21385         this.iframe = iframe.dom;
21386
21387          this.assignDocWin();
21388         
21389         this.doc.designMode = 'on';
21390        
21391         this.doc.open();
21392         this.doc.write(this.getDocMarkup());
21393         this.doc.close();
21394
21395         
21396         var task = { // must defer to wait for browser to be ready
21397             run : function(){
21398                 //console.log("run task?" + this.doc.readyState);
21399                 this.assignDocWin();
21400                 if(this.doc.body || this.doc.readyState == 'complete'){
21401                     try {
21402                         this.doc.designMode="on";
21403                     } catch (e) {
21404                         return;
21405                     }
21406                     Roo.TaskMgr.stop(task);
21407                     this.initEditor.defer(10, this);
21408                 }
21409             },
21410             interval : 10,
21411             duration: 10000,
21412             scope: this
21413         };
21414         Roo.TaskMgr.start(task);
21415
21416     },
21417
21418     // private
21419     onResize : function(w, h)
21420     {
21421          Roo.log('resize: ' +w + ',' + h );
21422         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21423         if(!this.iframe){
21424             return;
21425         }
21426         if(typeof w == 'number'){
21427             
21428             this.iframe.style.width = w + 'px';
21429         }
21430         if(typeof h == 'number'){
21431             
21432             this.iframe.style.height = h + 'px';
21433             if(this.doc){
21434                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21435             }
21436         }
21437         
21438     },
21439
21440     /**
21441      * Toggles the editor between standard and source edit mode.
21442      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21443      */
21444     toggleSourceEdit : function(sourceEditMode){
21445         
21446         this.sourceEditMode = sourceEditMode === true;
21447         
21448         if(this.sourceEditMode){
21449  
21450             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21451             
21452         }else{
21453             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21454             //this.iframe.className = '';
21455             this.deferFocus();
21456         }
21457         //this.setSize(this.owner.wrap.getSize());
21458         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21459     },
21460
21461     
21462   
21463
21464     /**
21465      * Protected method that will not generally be called directly. If you need/want
21466      * custom HTML cleanup, this is the method you should override.
21467      * @param {String} html The HTML to be cleaned
21468      * return {String} The cleaned HTML
21469      */
21470     cleanHtml : function(html){
21471         html = String(html);
21472         if(html.length > 5){
21473             if(Roo.isSafari){ // strip safari nonsense
21474                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21475             }
21476         }
21477         if(html == '&nbsp;'){
21478             html = '';
21479         }
21480         return html;
21481     },
21482
21483     /**
21484      * HTML Editor -> Textarea
21485      * Protected method that will not generally be called directly. Syncs the contents
21486      * of the editor iframe with the textarea.
21487      */
21488     syncValue : function(){
21489         if(this.initialized){
21490             var bd = (this.doc.body || this.doc.documentElement);
21491             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21492             var html = bd.innerHTML;
21493             if(Roo.isSafari){
21494                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21495                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21496                 if(m && m[1]){
21497                     html = '<div style="'+m[0]+'">' + html + '</div>';
21498                 }
21499             }
21500             html = this.cleanHtml(html);
21501             // fix up the special chars.. normaly like back quotes in word...
21502             // however we do not want to do this with chinese..
21503             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21504                 var cc = b.charCodeAt();
21505                 if (
21506                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21507                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21508                     (cc >= 0xf900 && cc < 0xfb00 )
21509                 ) {
21510                         return b;
21511                 }
21512                 return "&#"+cc+";" 
21513             });
21514             if(this.owner.fireEvent('beforesync', this, html) !== false){
21515                 this.el.dom.value = html;
21516                 this.owner.fireEvent('sync', this, html);
21517             }
21518         }
21519     },
21520
21521     /**
21522      * Protected method that will not generally be called directly. Pushes the value of the textarea
21523      * into the iframe editor.
21524      */
21525     pushValue : function(){
21526         if(this.initialized){
21527             var v = this.el.dom.value.trim();
21528             
21529 //            if(v.length < 1){
21530 //                v = '&#160;';
21531 //            }
21532             
21533             if(this.owner.fireEvent('beforepush', this, v) !== false){
21534                 var d = (this.doc.body || this.doc.documentElement);
21535                 d.innerHTML = v;
21536                 this.cleanUpPaste();
21537                 this.el.dom.value = d.innerHTML;
21538                 this.owner.fireEvent('push', this, v);
21539             }
21540         }
21541     },
21542
21543     // private
21544     deferFocus : function(){
21545         this.focus.defer(10, this);
21546     },
21547
21548     // doc'ed in Field
21549     focus : function(){
21550         if(this.win && !this.sourceEditMode){
21551             this.win.focus();
21552         }else{
21553             this.el.focus();
21554         }
21555     },
21556     
21557     assignDocWin: function()
21558     {
21559         var iframe = this.iframe;
21560         
21561          if(Roo.isIE){
21562             this.doc = iframe.contentWindow.document;
21563             this.win = iframe.contentWindow;
21564         } else {
21565 //            if (!Roo.get(this.frameId)) {
21566 //                return;
21567 //            }
21568 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21569 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21570             
21571             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21572                 return;
21573             }
21574             
21575             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21576             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21577         }
21578     },
21579     
21580     // private
21581     initEditor : function(){
21582         //console.log("INIT EDITOR");
21583         this.assignDocWin();
21584         
21585         
21586         
21587         this.doc.designMode="on";
21588         this.doc.open();
21589         this.doc.write(this.getDocMarkup());
21590         this.doc.close();
21591         
21592         var dbody = (this.doc.body || this.doc.documentElement);
21593         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21594         // this copies styles from the containing element into thsi one..
21595         // not sure why we need all of this..
21596         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21597         
21598         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21599         //ss['background-attachment'] = 'fixed'; // w3c
21600         dbody.bgProperties = 'fixed'; // ie
21601         //Roo.DomHelper.applyStyles(dbody, ss);
21602         Roo.EventManager.on(this.doc, {
21603             //'mousedown': this.onEditorEvent,
21604             'mouseup': this.onEditorEvent,
21605             'dblclick': this.onEditorEvent,
21606             'click': this.onEditorEvent,
21607             'keyup': this.onEditorEvent,
21608             buffer:100,
21609             scope: this
21610         });
21611         if(Roo.isGecko){
21612             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21613         }
21614         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21615             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21616         }
21617         this.initialized = true;
21618
21619         this.owner.fireEvent('initialize', this);
21620         this.pushValue();
21621     },
21622
21623     // private
21624     onDestroy : function(){
21625         
21626         
21627         
21628         if(this.rendered){
21629             
21630             //for (var i =0; i < this.toolbars.length;i++) {
21631             //    // fixme - ask toolbars for heights?
21632             //    this.toolbars[i].onDestroy();
21633            // }
21634             
21635             //this.wrap.dom.innerHTML = '';
21636             //this.wrap.remove();
21637         }
21638     },
21639
21640     // private
21641     onFirstFocus : function(){
21642         
21643         this.assignDocWin();
21644         
21645         
21646         this.activated = true;
21647          
21648     
21649         if(Roo.isGecko){ // prevent silly gecko errors
21650             this.win.focus();
21651             var s = this.win.getSelection();
21652             if(!s.focusNode || s.focusNode.nodeType != 3){
21653                 var r = s.getRangeAt(0);
21654                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21655                 r.collapse(true);
21656                 this.deferFocus();
21657             }
21658             try{
21659                 this.execCmd('useCSS', true);
21660                 this.execCmd('styleWithCSS', false);
21661             }catch(e){}
21662         }
21663         this.owner.fireEvent('activate', this);
21664     },
21665
21666     // private
21667     adjustFont: function(btn){
21668         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21669         //if(Roo.isSafari){ // safari
21670         //    adjust *= 2;
21671        // }
21672         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21673         if(Roo.isSafari){ // safari
21674             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21675             v =  (v < 10) ? 10 : v;
21676             v =  (v > 48) ? 48 : v;
21677             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21678             
21679         }
21680         
21681         
21682         v = Math.max(1, v+adjust);
21683         
21684         this.execCmd('FontSize', v  );
21685     },
21686
21687     onEditorEvent : function(e)
21688     {
21689         this.owner.fireEvent('editorevent', this, e);
21690       //  this.updateToolbar();
21691         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21692     },
21693
21694     insertTag : function(tg)
21695     {
21696         // could be a bit smarter... -> wrap the current selected tRoo..
21697         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21698             
21699             range = this.createRange(this.getSelection());
21700             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21701             wrappingNode.appendChild(range.extractContents());
21702             range.insertNode(wrappingNode);
21703
21704             return;
21705             
21706             
21707             
21708         }
21709         this.execCmd("formatblock",   tg);
21710         
21711     },
21712     
21713     insertText : function(txt)
21714     {
21715         
21716         
21717         var range = this.createRange();
21718         range.deleteContents();
21719                //alert(Sender.getAttribute('label'));
21720                
21721         range.insertNode(this.doc.createTextNode(txt));
21722     } ,
21723     
21724      
21725
21726     /**
21727      * Executes a Midas editor command on the editor document and performs necessary focus and
21728      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21729      * @param {String} cmd The Midas command
21730      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21731      */
21732     relayCmd : function(cmd, value){
21733         this.win.focus();
21734         this.execCmd(cmd, value);
21735         this.owner.fireEvent('editorevent', this);
21736         //this.updateToolbar();
21737         this.owner.deferFocus();
21738     },
21739
21740     /**
21741      * Executes a Midas editor command directly on the editor document.
21742      * For visual commands, you should use {@link #relayCmd} instead.
21743      * <b>This should only be called after the editor is initialized.</b>
21744      * @param {String} cmd The Midas command
21745      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21746      */
21747     execCmd : function(cmd, value){
21748         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21749         this.syncValue();
21750     },
21751  
21752  
21753    
21754     /**
21755      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21756      * to insert tRoo.
21757      * @param {String} text | dom node.. 
21758      */
21759     insertAtCursor : function(text)
21760     {
21761         
21762         if(!this.activated){
21763             return;
21764         }
21765         /*
21766         if(Roo.isIE){
21767             this.win.focus();
21768             var r = this.doc.selection.createRange();
21769             if(r){
21770                 r.collapse(true);
21771                 r.pasteHTML(text);
21772                 this.syncValue();
21773                 this.deferFocus();
21774             
21775             }
21776             return;
21777         }
21778         */
21779         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21780             this.win.focus();
21781             
21782             
21783             // from jquery ui (MIT licenced)
21784             var range, node;
21785             var win = this.win;
21786             
21787             if (win.getSelection && win.getSelection().getRangeAt) {
21788                 range = win.getSelection().getRangeAt(0);
21789                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21790                 range.insertNode(node);
21791             } else if (win.document.selection && win.document.selection.createRange) {
21792                 // no firefox support
21793                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21794                 win.document.selection.createRange().pasteHTML(txt);
21795             } else {
21796                 // no firefox support
21797                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21798                 this.execCmd('InsertHTML', txt);
21799             } 
21800             
21801             this.syncValue();
21802             
21803             this.deferFocus();
21804         }
21805     },
21806  // private
21807     mozKeyPress : function(e){
21808         if(e.ctrlKey){
21809             var c = e.getCharCode(), cmd;
21810           
21811             if(c > 0){
21812                 c = String.fromCharCode(c).toLowerCase();
21813                 switch(c){
21814                     case 'b':
21815                         cmd = 'bold';
21816                         break;
21817                     case 'i':
21818                         cmd = 'italic';
21819                         break;
21820                     
21821                     case 'u':
21822                         cmd = 'underline';
21823                         break;
21824                     
21825                     case 'v':
21826                         this.cleanUpPaste.defer(100, this);
21827                         return;
21828                         
21829                 }
21830                 if(cmd){
21831                     this.win.focus();
21832                     this.execCmd(cmd);
21833                     this.deferFocus();
21834                     e.preventDefault();
21835                 }
21836                 
21837             }
21838         }
21839     },
21840
21841     // private
21842     fixKeys : function(){ // load time branching for fastest keydown performance
21843         if(Roo.isIE){
21844             return function(e){
21845                 var k = e.getKey(), r;
21846                 if(k == e.TAB){
21847                     e.stopEvent();
21848                     r = this.doc.selection.createRange();
21849                     if(r){
21850                         r.collapse(true);
21851                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21852                         this.deferFocus();
21853                     }
21854                     return;
21855                 }
21856                 
21857                 if(k == e.ENTER){
21858                     r = this.doc.selection.createRange();
21859                     if(r){
21860                         var target = r.parentElement();
21861                         if(!target || target.tagName.toLowerCase() != 'li'){
21862                             e.stopEvent();
21863                             r.pasteHTML('<br />');
21864                             r.collapse(false);
21865                             r.select();
21866                         }
21867                     }
21868                 }
21869                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21870                     this.cleanUpPaste.defer(100, this);
21871                     return;
21872                 }
21873                 
21874                 
21875             };
21876         }else if(Roo.isOpera){
21877             return function(e){
21878                 var k = e.getKey();
21879                 if(k == e.TAB){
21880                     e.stopEvent();
21881                     this.win.focus();
21882                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21883                     this.deferFocus();
21884                 }
21885                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21886                     this.cleanUpPaste.defer(100, this);
21887                     return;
21888                 }
21889                 
21890             };
21891         }else if(Roo.isSafari){
21892             return function(e){
21893                 var k = e.getKey();
21894                 
21895                 if(k == e.TAB){
21896                     e.stopEvent();
21897                     this.execCmd('InsertText','\t');
21898                     this.deferFocus();
21899                     return;
21900                 }
21901                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21902                     this.cleanUpPaste.defer(100, this);
21903                     return;
21904                 }
21905                 
21906              };
21907         }
21908     }(),
21909     
21910     getAllAncestors: function()
21911     {
21912         var p = this.getSelectedNode();
21913         var a = [];
21914         if (!p) {
21915             a.push(p); // push blank onto stack..
21916             p = this.getParentElement();
21917         }
21918         
21919         
21920         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21921             a.push(p);
21922             p = p.parentNode;
21923         }
21924         a.push(this.doc.body);
21925         return a;
21926     },
21927     lastSel : false,
21928     lastSelNode : false,
21929     
21930     
21931     getSelection : function() 
21932     {
21933         this.assignDocWin();
21934         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21935     },
21936     
21937     getSelectedNode: function() 
21938     {
21939         // this may only work on Gecko!!!
21940         
21941         // should we cache this!!!!
21942         
21943         
21944         
21945          
21946         var range = this.createRange(this.getSelection()).cloneRange();
21947         
21948         if (Roo.isIE) {
21949             var parent = range.parentElement();
21950             while (true) {
21951                 var testRange = range.duplicate();
21952                 testRange.moveToElementText(parent);
21953                 if (testRange.inRange(range)) {
21954                     break;
21955                 }
21956                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21957                     break;
21958                 }
21959                 parent = parent.parentElement;
21960             }
21961             return parent;
21962         }
21963         
21964         // is ancestor a text element.
21965         var ac =  range.commonAncestorContainer;
21966         if (ac.nodeType == 3) {
21967             ac = ac.parentNode;
21968         }
21969         
21970         var ar = ac.childNodes;
21971          
21972         var nodes = [];
21973         var other_nodes = [];
21974         var has_other_nodes = false;
21975         for (var i=0;i<ar.length;i++) {
21976             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21977                 continue;
21978             }
21979             // fullly contained node.
21980             
21981             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21982                 nodes.push(ar[i]);
21983                 continue;
21984             }
21985             
21986             // probably selected..
21987             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21988                 other_nodes.push(ar[i]);
21989                 continue;
21990             }
21991             // outer..
21992             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21993                 continue;
21994             }
21995             
21996             
21997             has_other_nodes = true;
21998         }
21999         if (!nodes.length && other_nodes.length) {
22000             nodes= other_nodes;
22001         }
22002         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22003             return false;
22004         }
22005         
22006         return nodes[0];
22007     },
22008     createRange: function(sel)
22009     {
22010         // this has strange effects when using with 
22011         // top toolbar - not sure if it's a great idea.
22012         //this.editor.contentWindow.focus();
22013         if (typeof sel != "undefined") {
22014             try {
22015                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22016             } catch(e) {
22017                 return this.doc.createRange();
22018             }
22019         } else {
22020             return this.doc.createRange();
22021         }
22022     },
22023     getParentElement: function()
22024     {
22025         
22026         this.assignDocWin();
22027         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22028         
22029         var range = this.createRange(sel);
22030          
22031         try {
22032             var p = range.commonAncestorContainer;
22033             while (p.nodeType == 3) { // text node
22034                 p = p.parentNode;
22035             }
22036             return p;
22037         } catch (e) {
22038             return null;
22039         }
22040     
22041     },
22042     /***
22043      *
22044      * Range intersection.. the hard stuff...
22045      *  '-1' = before
22046      *  '0' = hits..
22047      *  '1' = after.
22048      *         [ -- selected range --- ]
22049      *   [fail]                        [fail]
22050      *
22051      *    basically..
22052      *      if end is before start or  hits it. fail.
22053      *      if start is after end or hits it fail.
22054      *
22055      *   if either hits (but other is outside. - then it's not 
22056      *   
22057      *    
22058      **/
22059     
22060     
22061     // @see http://www.thismuchiknow.co.uk/?p=64.
22062     rangeIntersectsNode : function(range, node)
22063     {
22064         var nodeRange = node.ownerDocument.createRange();
22065         try {
22066             nodeRange.selectNode(node);
22067         } catch (e) {
22068             nodeRange.selectNodeContents(node);
22069         }
22070     
22071         var rangeStartRange = range.cloneRange();
22072         rangeStartRange.collapse(true);
22073     
22074         var rangeEndRange = range.cloneRange();
22075         rangeEndRange.collapse(false);
22076     
22077         var nodeStartRange = nodeRange.cloneRange();
22078         nodeStartRange.collapse(true);
22079     
22080         var nodeEndRange = nodeRange.cloneRange();
22081         nodeEndRange.collapse(false);
22082     
22083         return rangeStartRange.compareBoundaryPoints(
22084                  Range.START_TO_START, nodeEndRange) == -1 &&
22085                rangeEndRange.compareBoundaryPoints(
22086                  Range.START_TO_START, nodeStartRange) == 1;
22087         
22088          
22089     },
22090     rangeCompareNode : function(range, node)
22091     {
22092         var nodeRange = node.ownerDocument.createRange();
22093         try {
22094             nodeRange.selectNode(node);
22095         } catch (e) {
22096             nodeRange.selectNodeContents(node);
22097         }
22098         
22099         
22100         range.collapse(true);
22101     
22102         nodeRange.collapse(true);
22103      
22104         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22105         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22106          
22107         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22108         
22109         var nodeIsBefore   =  ss == 1;
22110         var nodeIsAfter    = ee == -1;
22111         
22112         if (nodeIsBefore && nodeIsAfter) {
22113             return 0; // outer
22114         }
22115         if (!nodeIsBefore && nodeIsAfter) {
22116             return 1; //right trailed.
22117         }
22118         
22119         if (nodeIsBefore && !nodeIsAfter) {
22120             return 2;  // left trailed.
22121         }
22122         // fully contined.
22123         return 3;
22124     },
22125
22126     // private? - in a new class?
22127     cleanUpPaste :  function()
22128     {
22129         // cleans up the whole document..
22130         Roo.log('cleanuppaste');
22131         
22132         this.cleanUpChildren(this.doc.body);
22133         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22134         if (clean != this.doc.body.innerHTML) {
22135             this.doc.body.innerHTML = clean;
22136         }
22137         
22138     },
22139     
22140     cleanWordChars : function(input) {// change the chars to hex code
22141         var he = Roo.HtmlEditorCore;
22142         
22143         var output = input;
22144         Roo.each(he.swapCodes, function(sw) { 
22145             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22146             
22147             output = output.replace(swapper, sw[1]);
22148         });
22149         
22150         return output;
22151     },
22152     
22153     
22154     cleanUpChildren : function (n)
22155     {
22156         if (!n.childNodes.length) {
22157             return;
22158         }
22159         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22160            this.cleanUpChild(n.childNodes[i]);
22161         }
22162     },
22163     
22164     
22165         
22166     
22167     cleanUpChild : function (node)
22168     {
22169         var ed = this;
22170         //console.log(node);
22171         if (node.nodeName == "#text") {
22172             // clean up silly Windows -- stuff?
22173             return; 
22174         }
22175         if (node.nodeName == "#comment") {
22176             node.parentNode.removeChild(node);
22177             // clean up silly Windows -- stuff?
22178             return; 
22179         }
22180         var lcname = node.tagName.toLowerCase();
22181         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22182         // whitelist of tags..
22183         
22184         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22185             // remove node.
22186             node.parentNode.removeChild(node);
22187             return;
22188             
22189         }
22190         
22191         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22192         
22193         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22194         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22195         
22196         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22197         //    remove_keep_children = true;
22198         //}
22199         
22200         if (remove_keep_children) {
22201             this.cleanUpChildren(node);
22202             // inserts everything just before this node...
22203             while (node.childNodes.length) {
22204                 var cn = node.childNodes[0];
22205                 node.removeChild(cn);
22206                 node.parentNode.insertBefore(cn, node);
22207             }
22208             node.parentNode.removeChild(node);
22209             return;
22210         }
22211         
22212         if (!node.attributes || !node.attributes.length) {
22213             this.cleanUpChildren(node);
22214             return;
22215         }
22216         
22217         function cleanAttr(n,v)
22218         {
22219             
22220             if (v.match(/^\./) || v.match(/^\//)) {
22221                 return;
22222             }
22223             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22224                 return;
22225             }
22226             if (v.match(/^#/)) {
22227                 return;
22228             }
22229 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22230             node.removeAttribute(n);
22231             
22232         }
22233         
22234         var cwhite = this.cwhite;
22235         var cblack = this.cblack;
22236             
22237         function cleanStyle(n,v)
22238         {
22239             if (v.match(/expression/)) { //XSS?? should we even bother..
22240                 node.removeAttribute(n);
22241                 return;
22242             }
22243             
22244             var parts = v.split(/;/);
22245             var clean = [];
22246             
22247             Roo.each(parts, function(p) {
22248                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22249                 if (!p.length) {
22250                     return true;
22251                 }
22252                 var l = p.split(':').shift().replace(/\s+/g,'');
22253                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22254                 
22255                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22256 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22257                     //node.removeAttribute(n);
22258                     return true;
22259                 }
22260                 //Roo.log()
22261                 // only allow 'c whitelisted system attributes'
22262                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22263 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22264                     //node.removeAttribute(n);
22265                     return true;
22266                 }
22267                 
22268                 
22269                  
22270                 
22271                 clean.push(p);
22272                 return true;
22273             });
22274             if (clean.length) { 
22275                 node.setAttribute(n, clean.join(';'));
22276             } else {
22277                 node.removeAttribute(n);
22278             }
22279             
22280         }
22281         
22282         
22283         for (var i = node.attributes.length-1; i > -1 ; i--) {
22284             var a = node.attributes[i];
22285             //console.log(a);
22286             
22287             if (a.name.toLowerCase().substr(0,2)=='on')  {
22288                 node.removeAttribute(a.name);
22289                 continue;
22290             }
22291             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22292                 node.removeAttribute(a.name);
22293                 continue;
22294             }
22295             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22296                 cleanAttr(a.name,a.value); // fixme..
22297                 continue;
22298             }
22299             if (a.name == 'style') {
22300                 cleanStyle(a.name,a.value);
22301                 continue;
22302             }
22303             /// clean up MS crap..
22304             // tecnically this should be a list of valid class'es..
22305             
22306             
22307             if (a.name == 'class') {
22308                 if (a.value.match(/^Mso/)) {
22309                     node.className = '';
22310                 }
22311                 
22312                 if (a.value.match(/^body$/)) {
22313                     node.className = '';
22314                 }
22315                 continue;
22316             }
22317             
22318             // style cleanup!?
22319             // class cleanup?
22320             
22321         }
22322         
22323         
22324         this.cleanUpChildren(node);
22325         
22326         
22327     },
22328     
22329     /**
22330      * Clean up MS wordisms...
22331      */
22332     cleanWord : function(node)
22333     {
22334         
22335         
22336         if (!node) {
22337             this.cleanWord(this.doc.body);
22338             return;
22339         }
22340         if (node.nodeName == "#text") {
22341             // clean up silly Windows -- stuff?
22342             return; 
22343         }
22344         if (node.nodeName == "#comment") {
22345             node.parentNode.removeChild(node);
22346             // clean up silly Windows -- stuff?
22347             return; 
22348         }
22349         
22350         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22351             node.parentNode.removeChild(node);
22352             return;
22353         }
22354         
22355         // remove - but keep children..
22356         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22357             while (node.childNodes.length) {
22358                 var cn = node.childNodes[0];
22359                 node.removeChild(cn);
22360                 node.parentNode.insertBefore(cn, node);
22361             }
22362             node.parentNode.removeChild(node);
22363             this.iterateChildren(node, this.cleanWord);
22364             return;
22365         }
22366         // clean styles
22367         if (node.className.length) {
22368             
22369             var cn = node.className.split(/\W+/);
22370             var cna = [];
22371             Roo.each(cn, function(cls) {
22372                 if (cls.match(/Mso[a-zA-Z]+/)) {
22373                     return;
22374                 }
22375                 cna.push(cls);
22376             });
22377             node.className = cna.length ? cna.join(' ') : '';
22378             if (!cna.length) {
22379                 node.removeAttribute("class");
22380             }
22381         }
22382         
22383         if (node.hasAttribute("lang")) {
22384             node.removeAttribute("lang");
22385         }
22386         
22387         if (node.hasAttribute("style")) {
22388             
22389             var styles = node.getAttribute("style").split(";");
22390             var nstyle = [];
22391             Roo.each(styles, function(s) {
22392                 if (!s.match(/:/)) {
22393                     return;
22394                 }
22395                 var kv = s.split(":");
22396                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22397                     return;
22398                 }
22399                 // what ever is left... we allow.
22400                 nstyle.push(s);
22401             });
22402             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22403             if (!nstyle.length) {
22404                 node.removeAttribute('style');
22405             }
22406         }
22407         this.iterateChildren(node, this.cleanWord);
22408         
22409         
22410         
22411     },
22412     /**
22413      * iterateChildren of a Node, calling fn each time, using this as the scole..
22414      * @param {DomNode} node node to iterate children of.
22415      * @param {Function} fn method of this class to call on each item.
22416      */
22417     iterateChildren : function(node, fn)
22418     {
22419         if (!node.childNodes.length) {
22420                 return;
22421         }
22422         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22423            fn.call(this, node.childNodes[i])
22424         }
22425     },
22426     
22427     
22428     /**
22429      * cleanTableWidths.
22430      *
22431      * Quite often pasting from word etc.. results in tables with column and widths.
22432      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22433      *
22434      */
22435     cleanTableWidths : function(node)
22436     {
22437          
22438          
22439         if (!node) {
22440             this.cleanTableWidths(this.doc.body);
22441             return;
22442         }
22443         
22444         // ignore list...
22445         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22446             return; 
22447         }
22448         Roo.log(node.tagName);
22449         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22450             this.iterateChildren(node, this.cleanTableWidths);
22451             return;
22452         }
22453         if (node.hasAttribute('width')) {
22454             node.removeAttribute('width');
22455         }
22456         
22457          
22458         if (node.hasAttribute("style")) {
22459             // pretty basic...
22460             
22461             var styles = node.getAttribute("style").split(";");
22462             var nstyle = [];
22463             Roo.each(styles, function(s) {
22464                 if (!s.match(/:/)) {
22465                     return;
22466                 }
22467                 var kv = s.split(":");
22468                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22469                     return;
22470                 }
22471                 // what ever is left... we allow.
22472                 nstyle.push(s);
22473             });
22474             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22475             if (!nstyle.length) {
22476                 node.removeAttribute('style');
22477             }
22478         }
22479         
22480         this.iterateChildren(node, this.cleanTableWidths);
22481         
22482         
22483     },
22484     
22485     
22486     
22487     
22488     domToHTML : function(currentElement, depth, nopadtext) {
22489         
22490         depth = depth || 0;
22491         nopadtext = nopadtext || false;
22492     
22493         if (!currentElement) {
22494             return this.domToHTML(this.doc.body);
22495         }
22496         
22497         //Roo.log(currentElement);
22498         var j;
22499         var allText = false;
22500         var nodeName = currentElement.nodeName;
22501         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22502         
22503         if  (nodeName == '#text') {
22504             
22505             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22506         }
22507         
22508         
22509         var ret = '';
22510         if (nodeName != 'BODY') {
22511              
22512             var i = 0;
22513             // Prints the node tagName, such as <A>, <IMG>, etc
22514             if (tagName) {
22515                 var attr = [];
22516                 for(i = 0; i < currentElement.attributes.length;i++) {
22517                     // quoting?
22518                     var aname = currentElement.attributes.item(i).name;
22519                     if (!currentElement.attributes.item(i).value.length) {
22520                         continue;
22521                     }
22522                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22523                 }
22524                 
22525                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22526             } 
22527             else {
22528                 
22529                 // eack
22530             }
22531         } else {
22532             tagName = false;
22533         }
22534         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22535             return ret;
22536         }
22537         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22538             nopadtext = true;
22539         }
22540         
22541         
22542         // Traverse the tree
22543         i = 0;
22544         var currentElementChild = currentElement.childNodes.item(i);
22545         var allText = true;
22546         var innerHTML  = '';
22547         lastnode = '';
22548         while (currentElementChild) {
22549             // Formatting code (indent the tree so it looks nice on the screen)
22550             var nopad = nopadtext;
22551             if (lastnode == 'SPAN') {
22552                 nopad  = true;
22553             }
22554             // text
22555             if  (currentElementChild.nodeName == '#text') {
22556                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22557                 toadd = nopadtext ? toadd : toadd.trim();
22558                 if (!nopad && toadd.length > 80) {
22559                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22560                 }
22561                 innerHTML  += toadd;
22562                 
22563                 i++;
22564                 currentElementChild = currentElement.childNodes.item(i);
22565                 lastNode = '';
22566                 continue;
22567             }
22568             allText = false;
22569             
22570             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22571                 
22572             // Recursively traverse the tree structure of the child node
22573             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22574             lastnode = currentElementChild.nodeName;
22575             i++;
22576             currentElementChild=currentElement.childNodes.item(i);
22577         }
22578         
22579         ret += innerHTML;
22580         
22581         if (!allText) {
22582                 // The remaining code is mostly for formatting the tree
22583             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22584         }
22585         
22586         
22587         if (tagName) {
22588             ret+= "</"+tagName+">";
22589         }
22590         return ret;
22591         
22592     },
22593         
22594     applyBlacklists : function()
22595     {
22596         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22597         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22598         
22599         this.white = [];
22600         this.black = [];
22601         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22602             if (b.indexOf(tag) > -1) {
22603                 return;
22604             }
22605             this.white.push(tag);
22606             
22607         }, this);
22608         
22609         Roo.each(w, function(tag) {
22610             if (b.indexOf(tag) > -1) {
22611                 return;
22612             }
22613             if (this.white.indexOf(tag) > -1) {
22614                 return;
22615             }
22616             this.white.push(tag);
22617             
22618         }, this);
22619         
22620         
22621         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22622             if (w.indexOf(tag) > -1) {
22623                 return;
22624             }
22625             this.black.push(tag);
22626             
22627         }, this);
22628         
22629         Roo.each(b, function(tag) {
22630             if (w.indexOf(tag) > -1) {
22631                 return;
22632             }
22633             if (this.black.indexOf(tag) > -1) {
22634                 return;
22635             }
22636             this.black.push(tag);
22637             
22638         }, this);
22639         
22640         
22641         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22642         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22643         
22644         this.cwhite = [];
22645         this.cblack = [];
22646         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22647             if (b.indexOf(tag) > -1) {
22648                 return;
22649             }
22650             this.cwhite.push(tag);
22651             
22652         }, this);
22653         
22654         Roo.each(w, function(tag) {
22655             if (b.indexOf(tag) > -1) {
22656                 return;
22657             }
22658             if (this.cwhite.indexOf(tag) > -1) {
22659                 return;
22660             }
22661             this.cwhite.push(tag);
22662             
22663         }, this);
22664         
22665         
22666         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22667             if (w.indexOf(tag) > -1) {
22668                 return;
22669             }
22670             this.cblack.push(tag);
22671             
22672         }, this);
22673         
22674         Roo.each(b, function(tag) {
22675             if (w.indexOf(tag) > -1) {
22676                 return;
22677             }
22678             if (this.cblack.indexOf(tag) > -1) {
22679                 return;
22680             }
22681             this.cblack.push(tag);
22682             
22683         }, this);
22684     },
22685     
22686     setStylesheets : function(stylesheets)
22687     {
22688         if(typeof(stylesheets) == 'string'){
22689             Roo.get(this.iframe.contentDocument.head).createChild({
22690                 tag : 'link',
22691                 rel : 'stylesheet',
22692                 type : 'text/css',
22693                 href : stylesheets
22694             });
22695             
22696             return;
22697         }
22698         var _this = this;
22699      
22700         Roo.each(stylesheets, function(s) {
22701             if(!s.length){
22702                 return;
22703             }
22704             
22705             Roo.get(_this.iframe.contentDocument.head).createChild({
22706                 tag : 'link',
22707                 rel : 'stylesheet',
22708                 type : 'text/css',
22709                 href : s
22710             });
22711         });
22712
22713         
22714     },
22715     
22716     removeStylesheets : function()
22717     {
22718         var _this = this;
22719         
22720         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22721             s.remove();
22722         });
22723     },
22724     
22725     setStyle : function(style)
22726     {
22727         Roo.get(this.iframe.contentDocument.head).createChild({
22728             tag : 'style',
22729             type : 'text/css',
22730             html : style
22731         });
22732
22733         return;
22734     }
22735     
22736     // hide stuff that is not compatible
22737     /**
22738      * @event blur
22739      * @hide
22740      */
22741     /**
22742      * @event change
22743      * @hide
22744      */
22745     /**
22746      * @event focus
22747      * @hide
22748      */
22749     /**
22750      * @event specialkey
22751      * @hide
22752      */
22753     /**
22754      * @cfg {String} fieldClass @hide
22755      */
22756     /**
22757      * @cfg {String} focusClass @hide
22758      */
22759     /**
22760      * @cfg {String} autoCreate @hide
22761      */
22762     /**
22763      * @cfg {String} inputType @hide
22764      */
22765     /**
22766      * @cfg {String} invalidClass @hide
22767      */
22768     /**
22769      * @cfg {String} invalidText @hide
22770      */
22771     /**
22772      * @cfg {String} msgFx @hide
22773      */
22774     /**
22775      * @cfg {String} validateOnBlur @hide
22776      */
22777 });
22778
22779 Roo.HtmlEditorCore.white = [
22780         'area', 'br', 'img', 'input', 'hr', 'wbr',
22781         
22782        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22783        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22784        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22785        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22786        'table',   'ul',         'xmp', 
22787        
22788        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22789       'thead',   'tr', 
22790      
22791       'dir', 'menu', 'ol', 'ul', 'dl',
22792        
22793       'embed',  'object'
22794 ];
22795
22796
22797 Roo.HtmlEditorCore.black = [
22798     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22799         'applet', // 
22800         'base',   'basefont', 'bgsound', 'blink',  'body', 
22801         'frame',  'frameset', 'head',    'html',   'ilayer', 
22802         'iframe', 'layer',  'link',     'meta',    'object',   
22803         'script', 'style' ,'title',  'xml' // clean later..
22804 ];
22805 Roo.HtmlEditorCore.clean = [
22806     'script', 'style', 'title', 'xml'
22807 ];
22808 Roo.HtmlEditorCore.remove = [
22809     'font'
22810 ];
22811 // attributes..
22812
22813 Roo.HtmlEditorCore.ablack = [
22814     'on'
22815 ];
22816     
22817 Roo.HtmlEditorCore.aclean = [ 
22818     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22819 ];
22820
22821 // protocols..
22822 Roo.HtmlEditorCore.pwhite= [
22823         'http',  'https',  'mailto'
22824 ];
22825
22826 // white listed style attributes.
22827 Roo.HtmlEditorCore.cwhite= [
22828       //  'text-align', /// default is to allow most things..
22829       
22830          
22831 //        'font-size'//??
22832 ];
22833
22834 // black listed style attributes.
22835 Roo.HtmlEditorCore.cblack= [
22836       //  'font-size' -- this can be set by the project 
22837 ];
22838
22839
22840 Roo.HtmlEditorCore.swapCodes   =[ 
22841     [    8211, "--" ], 
22842     [    8212, "--" ], 
22843     [    8216,  "'" ],  
22844     [    8217, "'" ],  
22845     [    8220, '"' ],  
22846     [    8221, '"' ],  
22847     [    8226, "*" ],  
22848     [    8230, "..." ]
22849 ]; 
22850
22851     /*
22852  * - LGPL
22853  *
22854  * HtmlEditor
22855  * 
22856  */
22857
22858 /**
22859  * @class Roo.bootstrap.HtmlEditor
22860  * @extends Roo.bootstrap.TextArea
22861  * Bootstrap HtmlEditor class
22862
22863  * @constructor
22864  * Create a new HtmlEditor
22865  * @param {Object} config The config object
22866  */
22867
22868 Roo.bootstrap.HtmlEditor = function(config){
22869     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22870     if (!this.toolbars) {
22871         this.toolbars = [];
22872     }
22873     
22874     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22875     this.addEvents({
22876             /**
22877              * @event initialize
22878              * Fires when the editor is fully initialized (including the iframe)
22879              * @param {HtmlEditor} this
22880              */
22881             initialize: true,
22882             /**
22883              * @event activate
22884              * Fires when the editor is first receives the focus. Any insertion must wait
22885              * until after this event.
22886              * @param {HtmlEditor} this
22887              */
22888             activate: true,
22889              /**
22890              * @event beforesync
22891              * Fires before the textarea is updated with content from the editor iframe. Return false
22892              * to cancel the sync.
22893              * @param {HtmlEditor} this
22894              * @param {String} html
22895              */
22896             beforesync: true,
22897              /**
22898              * @event beforepush
22899              * Fires before the iframe editor is updated with content from the textarea. Return false
22900              * to cancel the push.
22901              * @param {HtmlEditor} this
22902              * @param {String} html
22903              */
22904             beforepush: true,
22905              /**
22906              * @event sync
22907              * Fires when the textarea is updated with content from the editor iframe.
22908              * @param {HtmlEditor} this
22909              * @param {String} html
22910              */
22911             sync: true,
22912              /**
22913              * @event push
22914              * Fires when the iframe editor is updated with content from the textarea.
22915              * @param {HtmlEditor} this
22916              * @param {String} html
22917              */
22918             push: true,
22919              /**
22920              * @event editmodechange
22921              * Fires when the editor switches edit modes
22922              * @param {HtmlEditor} this
22923              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22924              */
22925             editmodechange: true,
22926             /**
22927              * @event editorevent
22928              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22929              * @param {HtmlEditor} this
22930              */
22931             editorevent: true,
22932             /**
22933              * @event firstfocus
22934              * Fires when on first focus - needed by toolbars..
22935              * @param {HtmlEditor} this
22936              */
22937             firstfocus: true,
22938             /**
22939              * @event autosave
22940              * Auto save the htmlEditor value as a file into Events
22941              * @param {HtmlEditor} this
22942              */
22943             autosave: true,
22944             /**
22945              * @event savedpreview
22946              * preview the saved version of htmlEditor
22947              * @param {HtmlEditor} this
22948              */
22949             savedpreview: true
22950         });
22951 };
22952
22953
22954 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22955     
22956     
22957       /**
22958      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22959      */
22960     toolbars : false,
22961     
22962      /**
22963     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22964     */
22965     btns : [],
22966    
22967      /**
22968      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22969      *                        Roo.resizable.
22970      */
22971     resizable : false,
22972      /**
22973      * @cfg {Number} height (in pixels)
22974      */   
22975     height: 300,
22976    /**
22977      * @cfg {Number} width (in pixels)
22978      */   
22979     width: false,
22980     
22981     /**
22982      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22983      * 
22984      */
22985     stylesheets: false,
22986     
22987     // id of frame..
22988     frameId: false,
22989     
22990     // private properties
22991     validationEvent : false,
22992     deferHeight: true,
22993     initialized : false,
22994     activated : false,
22995     
22996     onFocus : Roo.emptyFn,
22997     iframePad:3,
22998     hideMode:'offsets',
22999     
23000     tbContainer : false,
23001     
23002     bodyCls : '',
23003     
23004     toolbarContainer :function() {
23005         return this.wrap.select('.x-html-editor-tb',true).first();
23006     },
23007
23008     /**
23009      * Protected method that will not generally be called directly. It
23010      * is called when the editor creates its toolbar. Override this method if you need to
23011      * add custom toolbar buttons.
23012      * @param {HtmlEditor} editor
23013      */
23014     createToolbar : function(){
23015         Roo.log('renewing');
23016         Roo.log("create toolbars");
23017         
23018         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23019         this.toolbars[0].render(this.toolbarContainer());
23020         
23021         return;
23022         
23023 //        if (!editor.toolbars || !editor.toolbars.length) {
23024 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23025 //        }
23026 //        
23027 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23028 //            editor.toolbars[i] = Roo.factory(
23029 //                    typeof(editor.toolbars[i]) == 'string' ?
23030 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23031 //                Roo.bootstrap.HtmlEditor);
23032 //            editor.toolbars[i].init(editor);
23033 //        }
23034     },
23035
23036      
23037     // private
23038     onRender : function(ct, position)
23039     {
23040        // Roo.log("Call onRender: " + this.xtype);
23041         var _t = this;
23042         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23043       
23044         this.wrap = this.inputEl().wrap({
23045             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23046         });
23047         
23048         this.editorcore.onRender(ct, position);
23049          
23050         if (this.resizable) {
23051             this.resizeEl = new Roo.Resizable(this.wrap, {
23052                 pinned : true,
23053                 wrap: true,
23054                 dynamic : true,
23055                 minHeight : this.height,
23056                 height: this.height,
23057                 handles : this.resizable,
23058                 width: this.width,
23059                 listeners : {
23060                     resize : function(r, w, h) {
23061                         _t.onResize(w,h); // -something
23062                     }
23063                 }
23064             });
23065             
23066         }
23067         this.createToolbar(this);
23068        
23069         
23070         if(!this.width && this.resizable){
23071             this.setSize(this.wrap.getSize());
23072         }
23073         if (this.resizeEl) {
23074             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23075             // should trigger onReize..
23076         }
23077         
23078     },
23079
23080     // private
23081     onResize : function(w, h)
23082     {
23083         Roo.log('resize: ' +w + ',' + h );
23084         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23085         var ew = false;
23086         var eh = false;
23087         
23088         if(this.inputEl() ){
23089             if(typeof w == 'number'){
23090                 var aw = w - this.wrap.getFrameWidth('lr');
23091                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23092                 ew = aw;
23093             }
23094             if(typeof h == 'number'){
23095                  var tbh = -11;  // fixme it needs to tool bar size!
23096                 for (var i =0; i < this.toolbars.length;i++) {
23097                     // fixme - ask toolbars for heights?
23098                     tbh += this.toolbars[i].el.getHeight();
23099                     //if (this.toolbars[i].footer) {
23100                     //    tbh += this.toolbars[i].footer.el.getHeight();
23101                     //}
23102                 }
23103               
23104                 
23105                 
23106                 
23107                 
23108                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23109                 ah -= 5; // knock a few pixes off for look..
23110                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23111                 var eh = ah;
23112             }
23113         }
23114         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23115         this.editorcore.onResize(ew,eh);
23116         
23117     },
23118
23119     /**
23120      * Toggles the editor between standard and source edit mode.
23121      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23122      */
23123     toggleSourceEdit : function(sourceEditMode)
23124     {
23125         this.editorcore.toggleSourceEdit(sourceEditMode);
23126         
23127         if(this.editorcore.sourceEditMode){
23128             Roo.log('editor - showing textarea');
23129             
23130 //            Roo.log('in');
23131 //            Roo.log(this.syncValue());
23132             this.syncValue();
23133             this.inputEl().removeClass(['hide', 'x-hidden']);
23134             this.inputEl().dom.removeAttribute('tabIndex');
23135             this.inputEl().focus();
23136         }else{
23137             Roo.log('editor - hiding textarea');
23138 //            Roo.log('out')
23139 //            Roo.log(this.pushValue()); 
23140             this.pushValue();
23141             
23142             this.inputEl().addClass(['hide', 'x-hidden']);
23143             this.inputEl().dom.setAttribute('tabIndex', -1);
23144             //this.deferFocus();
23145         }
23146          
23147         if(this.resizable){
23148             this.setSize(this.wrap.getSize());
23149         }
23150         
23151         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23152     },
23153  
23154     // private (for BoxComponent)
23155     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23156
23157     // private (for BoxComponent)
23158     getResizeEl : function(){
23159         return this.wrap;
23160     },
23161
23162     // private (for BoxComponent)
23163     getPositionEl : function(){
23164         return this.wrap;
23165     },
23166
23167     // private
23168     initEvents : function(){
23169         this.originalValue = this.getValue();
23170     },
23171
23172 //    /**
23173 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23174 //     * @method
23175 //     */
23176 //    markInvalid : Roo.emptyFn,
23177 //    /**
23178 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23179 //     * @method
23180 //     */
23181 //    clearInvalid : Roo.emptyFn,
23182
23183     setValue : function(v){
23184         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23185         this.editorcore.pushValue();
23186     },
23187
23188      
23189     // private
23190     deferFocus : function(){
23191         this.focus.defer(10, this);
23192     },
23193
23194     // doc'ed in Field
23195     focus : function(){
23196         this.editorcore.focus();
23197         
23198     },
23199       
23200
23201     // private
23202     onDestroy : function(){
23203         
23204         
23205         
23206         if(this.rendered){
23207             
23208             for (var i =0; i < this.toolbars.length;i++) {
23209                 // fixme - ask toolbars for heights?
23210                 this.toolbars[i].onDestroy();
23211             }
23212             
23213             this.wrap.dom.innerHTML = '';
23214             this.wrap.remove();
23215         }
23216     },
23217
23218     // private
23219     onFirstFocus : function(){
23220         //Roo.log("onFirstFocus");
23221         this.editorcore.onFirstFocus();
23222          for (var i =0; i < this.toolbars.length;i++) {
23223             this.toolbars[i].onFirstFocus();
23224         }
23225         
23226     },
23227     
23228     // private
23229     syncValue : function()
23230     {   
23231         this.editorcore.syncValue();
23232     },
23233     
23234     pushValue : function()
23235     {   
23236         this.editorcore.pushValue();
23237     }
23238      
23239     
23240     // hide stuff that is not compatible
23241     /**
23242      * @event blur
23243      * @hide
23244      */
23245     /**
23246      * @event change
23247      * @hide
23248      */
23249     /**
23250      * @event focus
23251      * @hide
23252      */
23253     /**
23254      * @event specialkey
23255      * @hide
23256      */
23257     /**
23258      * @cfg {String} fieldClass @hide
23259      */
23260     /**
23261      * @cfg {String} focusClass @hide
23262      */
23263     /**
23264      * @cfg {String} autoCreate @hide
23265      */
23266     /**
23267      * @cfg {String} inputType @hide
23268      */
23269     /**
23270      * @cfg {String} invalidClass @hide
23271      */
23272     /**
23273      * @cfg {String} invalidText @hide
23274      */
23275     /**
23276      * @cfg {String} msgFx @hide
23277      */
23278     /**
23279      * @cfg {String} validateOnBlur @hide
23280      */
23281 });
23282  
23283     
23284    
23285    
23286    
23287       
23288 Roo.namespace('Roo.bootstrap.htmleditor');
23289 /**
23290  * @class Roo.bootstrap.HtmlEditorToolbar1
23291  * Basic Toolbar
23292  * 
23293  * Usage:
23294  *
23295  new Roo.bootstrap.HtmlEditor({
23296     ....
23297     toolbars : [
23298         new Roo.bootstrap.HtmlEditorToolbar1({
23299             disable : { fonts: 1 , format: 1, ..., ... , ...],
23300             btns : [ .... ]
23301         })
23302     }
23303      
23304  * 
23305  * @cfg {Object} disable List of elements to disable..
23306  * @cfg {Array} btns List of additional buttons.
23307  * 
23308  * 
23309  * NEEDS Extra CSS? 
23310  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23311  */
23312  
23313 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23314 {
23315     
23316     Roo.apply(this, config);
23317     
23318     // default disabled, based on 'good practice'..
23319     this.disable = this.disable || {};
23320     Roo.applyIf(this.disable, {
23321         fontSize : true,
23322         colors : true,
23323         specialElements : true
23324     });
23325     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23326     
23327     this.editor = config.editor;
23328     this.editorcore = config.editor.editorcore;
23329     
23330     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23331     
23332     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23333     // dont call parent... till later.
23334 }
23335 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23336      
23337     bar : true,
23338     
23339     editor : false,
23340     editorcore : false,
23341     
23342     
23343     formats : [
23344         "p" ,  
23345         "h1","h2","h3","h4","h5","h6", 
23346         "pre", "code", 
23347         "abbr", "acronym", "address", "cite", "samp", "var",
23348         'div','span'
23349     ],
23350     
23351     onRender : function(ct, position)
23352     {
23353        // Roo.log("Call onRender: " + this.xtype);
23354         
23355        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23356        Roo.log(this.el);
23357        this.el.dom.style.marginBottom = '0';
23358        var _this = this;
23359        var editorcore = this.editorcore;
23360        var editor= this.editor;
23361        
23362        var children = [];
23363        var btn = function(id,cmd , toggle, handler, html){
23364        
23365             var  event = toggle ? 'toggle' : 'click';
23366        
23367             var a = {
23368                 size : 'sm',
23369                 xtype: 'Button',
23370                 xns: Roo.bootstrap,
23371                 glyphicon : id,
23372                 cmd : id || cmd,
23373                 enableToggle:toggle !== false,
23374                 html : html || '',
23375                 pressed : toggle ? false : null,
23376                 listeners : {}
23377             };
23378             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23379                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23380             };
23381             children.push(a);
23382             return a;
23383        }
23384        
23385     //    var cb_box = function...
23386         
23387         var style = {
23388                 xtype: 'Button',
23389                 size : 'sm',
23390                 xns: Roo.bootstrap,
23391                 glyphicon : 'font',
23392                 //html : 'submit'
23393                 menu : {
23394                     xtype: 'Menu',
23395                     xns: Roo.bootstrap,
23396                     items:  []
23397                 }
23398         };
23399         Roo.each(this.formats, function(f) {
23400             style.menu.items.push({
23401                 xtype :'MenuItem',
23402                 xns: Roo.bootstrap,
23403                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23404                 tagname : f,
23405                 listeners : {
23406                     click : function()
23407                     {
23408                         editorcore.insertTag(this.tagname);
23409                         editor.focus();
23410                     }
23411                 }
23412                 
23413             });
23414         });
23415         children.push(style);   
23416         
23417         btn('bold',false,true);
23418         btn('italic',false,true);
23419         btn('align-left', 'justifyleft',true);
23420         btn('align-center', 'justifycenter',true);
23421         btn('align-right' , 'justifyright',true);
23422         btn('link', false, false, function(btn) {
23423             //Roo.log("create link?");
23424             var url = prompt(this.createLinkText, this.defaultLinkValue);
23425             if(url && url != 'http:/'+'/'){
23426                 this.editorcore.relayCmd('createlink', url);
23427             }
23428         }),
23429         btn('list','insertunorderedlist',true);
23430         btn('pencil', false,true, function(btn){
23431                 Roo.log(this);
23432                 this.toggleSourceEdit(btn.pressed);
23433         });
23434         
23435         if (this.editor.btns.length > 0) {
23436             for (var i = 0; i<this.editor.btns.length; i++) {
23437                 children.push(this.editor.btns[i]);
23438             }
23439         }
23440         
23441         /*
23442         var cog = {
23443                 xtype: 'Button',
23444                 size : 'sm',
23445                 xns: Roo.bootstrap,
23446                 glyphicon : 'cog',
23447                 //html : 'submit'
23448                 menu : {
23449                     xtype: 'Menu',
23450                     xns: Roo.bootstrap,
23451                     items:  []
23452                 }
23453         };
23454         
23455         cog.menu.items.push({
23456             xtype :'MenuItem',
23457             xns: Roo.bootstrap,
23458             html : Clean styles,
23459             tagname : f,
23460             listeners : {
23461                 click : function()
23462                 {
23463                     editorcore.insertTag(this.tagname);
23464                     editor.focus();
23465                 }
23466             }
23467             
23468         });
23469        */
23470         
23471          
23472        this.xtype = 'NavSimplebar';
23473         
23474         for(var i=0;i< children.length;i++) {
23475             
23476             this.buttons.add(this.addxtypeChild(children[i]));
23477             
23478         }
23479         
23480         editor.on('editorevent', this.updateToolbar, this);
23481     },
23482     onBtnClick : function(id)
23483     {
23484        this.editorcore.relayCmd(id);
23485        this.editorcore.focus();
23486     },
23487     
23488     /**
23489      * Protected method that will not generally be called directly. It triggers
23490      * a toolbar update by reading the markup state of the current selection in the editor.
23491      */
23492     updateToolbar: function(){
23493
23494         if(!this.editorcore.activated){
23495             this.editor.onFirstFocus(); // is this neeed?
23496             return;
23497         }
23498
23499         var btns = this.buttons; 
23500         var doc = this.editorcore.doc;
23501         btns.get('bold').setActive(doc.queryCommandState('bold'));
23502         btns.get('italic').setActive(doc.queryCommandState('italic'));
23503         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23504         
23505         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23506         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23507         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23508         
23509         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23510         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23511          /*
23512         
23513         var ans = this.editorcore.getAllAncestors();
23514         if (this.formatCombo) {
23515             
23516             
23517             var store = this.formatCombo.store;
23518             this.formatCombo.setValue("");
23519             for (var i =0; i < ans.length;i++) {
23520                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23521                     // select it..
23522                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23523                     break;
23524                 }
23525             }
23526         }
23527         
23528         
23529         
23530         // hides menus... - so this cant be on a menu...
23531         Roo.bootstrap.MenuMgr.hideAll();
23532         */
23533         Roo.bootstrap.MenuMgr.hideAll();
23534         //this.editorsyncValue();
23535     },
23536     onFirstFocus: function() {
23537         this.buttons.each(function(item){
23538            item.enable();
23539         });
23540     },
23541     toggleSourceEdit : function(sourceEditMode){
23542         
23543           
23544         if(sourceEditMode){
23545             Roo.log("disabling buttons");
23546            this.buttons.each( function(item){
23547                 if(item.cmd != 'pencil'){
23548                     item.disable();
23549                 }
23550             });
23551           
23552         }else{
23553             Roo.log("enabling buttons");
23554             if(this.editorcore.initialized){
23555                 this.buttons.each( function(item){
23556                     item.enable();
23557                 });
23558             }
23559             
23560         }
23561         Roo.log("calling toggole on editor");
23562         // tell the editor that it's been pressed..
23563         this.editor.toggleSourceEdit(sourceEditMode);
23564        
23565     }
23566 });
23567
23568
23569
23570
23571
23572 /**
23573  * @class Roo.bootstrap.Table.AbstractSelectionModel
23574  * @extends Roo.util.Observable
23575  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23576  * implemented by descendant classes.  This class should not be directly instantiated.
23577  * @constructor
23578  */
23579 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23580     this.locked = false;
23581     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23582 };
23583
23584
23585 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23586     /** @ignore Called by the grid automatically. Do not call directly. */
23587     init : function(grid){
23588         this.grid = grid;
23589         this.initEvents();
23590     },
23591
23592     /**
23593      * Locks the selections.
23594      */
23595     lock : function(){
23596         this.locked = true;
23597     },
23598
23599     /**
23600      * Unlocks the selections.
23601      */
23602     unlock : function(){
23603         this.locked = false;
23604     },
23605
23606     /**
23607      * Returns true if the selections are locked.
23608      * @return {Boolean}
23609      */
23610     isLocked : function(){
23611         return this.locked;
23612     }
23613 });
23614 /**
23615  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23616  * @class Roo.bootstrap.Table.RowSelectionModel
23617  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23618  * It supports multiple selections and keyboard selection/navigation. 
23619  * @constructor
23620  * @param {Object} config
23621  */
23622
23623 Roo.bootstrap.Table.RowSelectionModel = function(config){
23624     Roo.apply(this, config);
23625     this.selections = new Roo.util.MixedCollection(false, function(o){
23626         return o.id;
23627     });
23628
23629     this.last = false;
23630     this.lastActive = false;
23631
23632     this.addEvents({
23633         /**
23634              * @event selectionchange
23635              * Fires when the selection changes
23636              * @param {SelectionModel} this
23637              */
23638             "selectionchange" : true,
23639         /**
23640              * @event afterselectionchange
23641              * Fires after the selection changes (eg. by key press or clicking)
23642              * @param {SelectionModel} this
23643              */
23644             "afterselectionchange" : true,
23645         /**
23646              * @event beforerowselect
23647              * Fires when a row is selected being selected, return false to cancel.
23648              * @param {SelectionModel} this
23649              * @param {Number} rowIndex The selected index
23650              * @param {Boolean} keepExisting False if other selections will be cleared
23651              */
23652             "beforerowselect" : true,
23653         /**
23654              * @event rowselect
23655              * Fires when a row is selected.
23656              * @param {SelectionModel} this
23657              * @param {Number} rowIndex The selected index
23658              * @param {Roo.data.Record} r The record
23659              */
23660             "rowselect" : true,
23661         /**
23662              * @event rowdeselect
23663              * Fires when a row is deselected.
23664              * @param {SelectionModel} this
23665              * @param {Number} rowIndex The selected index
23666              */
23667         "rowdeselect" : true
23668     });
23669     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23670     this.locked = false;
23671  };
23672
23673 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23674     /**
23675      * @cfg {Boolean} singleSelect
23676      * True to allow selection of only one row at a time (defaults to false)
23677      */
23678     singleSelect : false,
23679
23680     // private
23681     initEvents : function()
23682     {
23683
23684         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23685         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23686         //}else{ // allow click to work like normal
23687          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23688         //}
23689         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23690         this.grid.on("rowclick", this.handleMouseDown, this);
23691         
23692         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23693             "up" : function(e){
23694                 if(!e.shiftKey){
23695                     this.selectPrevious(e.shiftKey);
23696                 }else if(this.last !== false && this.lastActive !== false){
23697                     var last = this.last;
23698                     this.selectRange(this.last,  this.lastActive-1);
23699                     this.grid.getView().focusRow(this.lastActive);
23700                     if(last !== false){
23701                         this.last = last;
23702                     }
23703                 }else{
23704                     this.selectFirstRow();
23705                 }
23706                 this.fireEvent("afterselectionchange", this);
23707             },
23708             "down" : function(e){
23709                 if(!e.shiftKey){
23710                     this.selectNext(e.shiftKey);
23711                 }else if(this.last !== false && this.lastActive !== false){
23712                     var last = this.last;
23713                     this.selectRange(this.last,  this.lastActive+1);
23714                     this.grid.getView().focusRow(this.lastActive);
23715                     if(last !== false){
23716                         this.last = last;
23717                     }
23718                 }else{
23719                     this.selectFirstRow();
23720                 }
23721                 this.fireEvent("afterselectionchange", this);
23722             },
23723             scope: this
23724         });
23725         this.grid.store.on('load', function(){
23726             this.selections.clear();
23727         },this);
23728         /*
23729         var view = this.grid.view;
23730         view.on("refresh", this.onRefresh, this);
23731         view.on("rowupdated", this.onRowUpdated, this);
23732         view.on("rowremoved", this.onRemove, this);
23733         */
23734     },
23735
23736     // private
23737     onRefresh : function()
23738     {
23739         var ds = this.grid.store, i, v = this.grid.view;
23740         var s = this.selections;
23741         s.each(function(r){
23742             if((i = ds.indexOfId(r.id)) != -1){
23743                 v.onRowSelect(i);
23744             }else{
23745                 s.remove(r);
23746             }
23747         });
23748     },
23749
23750     // private
23751     onRemove : function(v, index, r){
23752         this.selections.remove(r);
23753     },
23754
23755     // private
23756     onRowUpdated : function(v, index, r){
23757         if(this.isSelected(r)){
23758             v.onRowSelect(index);
23759         }
23760     },
23761
23762     /**
23763      * Select records.
23764      * @param {Array} records The records to select
23765      * @param {Boolean} keepExisting (optional) True to keep existing selections
23766      */
23767     selectRecords : function(records, keepExisting)
23768     {
23769         if(!keepExisting){
23770             this.clearSelections();
23771         }
23772             var ds = this.grid.store;
23773         for(var i = 0, len = records.length; i < len; i++){
23774             this.selectRow(ds.indexOf(records[i]), true);
23775         }
23776     },
23777
23778     /**
23779      * Gets the number of selected rows.
23780      * @return {Number}
23781      */
23782     getCount : function(){
23783         return this.selections.length;
23784     },
23785
23786     /**
23787      * Selects the first row in the grid.
23788      */
23789     selectFirstRow : function(){
23790         this.selectRow(0);
23791     },
23792
23793     /**
23794      * Select the last row.
23795      * @param {Boolean} keepExisting (optional) True to keep existing selections
23796      */
23797     selectLastRow : function(keepExisting){
23798         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23799         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23800     },
23801
23802     /**
23803      * Selects the row immediately following the last selected row.
23804      * @param {Boolean} keepExisting (optional) True to keep existing selections
23805      */
23806     selectNext : function(keepExisting)
23807     {
23808             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23809             this.selectRow(this.last+1, keepExisting);
23810             this.grid.getView().focusRow(this.last);
23811         }
23812     },
23813
23814     /**
23815      * Selects the row that precedes the last selected row.
23816      * @param {Boolean} keepExisting (optional) True to keep existing selections
23817      */
23818     selectPrevious : function(keepExisting){
23819         if(this.last){
23820             this.selectRow(this.last-1, keepExisting);
23821             this.grid.getView().focusRow(this.last);
23822         }
23823     },
23824
23825     /**
23826      * Returns the selected records
23827      * @return {Array} Array of selected records
23828      */
23829     getSelections : function(){
23830         return [].concat(this.selections.items);
23831     },
23832
23833     /**
23834      * Returns the first selected record.
23835      * @return {Record}
23836      */
23837     getSelected : function(){
23838         return this.selections.itemAt(0);
23839     },
23840
23841
23842     /**
23843      * Clears all selections.
23844      */
23845     clearSelections : function(fast)
23846     {
23847         if(this.locked) {
23848             return;
23849         }
23850         if(fast !== true){
23851                 var ds = this.grid.store;
23852             var s = this.selections;
23853             s.each(function(r){
23854                 this.deselectRow(ds.indexOfId(r.id));
23855             }, this);
23856             s.clear();
23857         }else{
23858             this.selections.clear();
23859         }
23860         this.last = false;
23861     },
23862
23863
23864     /**
23865      * Selects all rows.
23866      */
23867     selectAll : function(){
23868         if(this.locked) {
23869             return;
23870         }
23871         this.selections.clear();
23872         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23873             this.selectRow(i, true);
23874         }
23875     },
23876
23877     /**
23878      * Returns True if there is a selection.
23879      * @return {Boolean}
23880      */
23881     hasSelection : function(){
23882         return this.selections.length > 0;
23883     },
23884
23885     /**
23886      * Returns True if the specified row is selected.
23887      * @param {Number/Record} record The record or index of the record to check
23888      * @return {Boolean}
23889      */
23890     isSelected : function(index){
23891             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23892         return (r && this.selections.key(r.id) ? true : false);
23893     },
23894
23895     /**
23896      * Returns True if the specified record id is selected.
23897      * @param {String} id The id of record to check
23898      * @return {Boolean}
23899      */
23900     isIdSelected : function(id){
23901         return (this.selections.key(id) ? true : false);
23902     },
23903
23904
23905     // private
23906     handleMouseDBClick : function(e, t){
23907         
23908     },
23909     // private
23910     handleMouseDown : function(e, t)
23911     {
23912             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23913         if(this.isLocked() || rowIndex < 0 ){
23914             return;
23915         };
23916         if(e.shiftKey && this.last !== false){
23917             var last = this.last;
23918             this.selectRange(last, rowIndex, e.ctrlKey);
23919             this.last = last; // reset the last
23920             t.focus();
23921     
23922         }else{
23923             var isSelected = this.isSelected(rowIndex);
23924             //Roo.log("select row:" + rowIndex);
23925             if(isSelected){
23926                 this.deselectRow(rowIndex);
23927             } else {
23928                         this.selectRow(rowIndex, true);
23929             }
23930     
23931             /*
23932                 if(e.button !== 0 && isSelected){
23933                 alert('rowIndex 2: ' + rowIndex);
23934                     view.focusRow(rowIndex);
23935                 }else if(e.ctrlKey && isSelected){
23936                     this.deselectRow(rowIndex);
23937                 }else if(!isSelected){
23938                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23939                     view.focusRow(rowIndex);
23940                 }
23941             */
23942         }
23943         this.fireEvent("afterselectionchange", this);
23944     },
23945     // private
23946     handleDragableRowClick :  function(grid, rowIndex, e) 
23947     {
23948         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23949             this.selectRow(rowIndex, false);
23950             grid.view.focusRow(rowIndex);
23951              this.fireEvent("afterselectionchange", this);
23952         }
23953     },
23954     
23955     /**
23956      * Selects multiple rows.
23957      * @param {Array} rows Array of the indexes of the row to select
23958      * @param {Boolean} keepExisting (optional) True to keep existing selections
23959      */
23960     selectRows : function(rows, keepExisting){
23961         if(!keepExisting){
23962             this.clearSelections();
23963         }
23964         for(var i = 0, len = rows.length; i < len; i++){
23965             this.selectRow(rows[i], true);
23966         }
23967     },
23968
23969     /**
23970      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23971      * @param {Number} startRow The index of the first row in the range
23972      * @param {Number} endRow The index of the last row in the range
23973      * @param {Boolean} keepExisting (optional) True to retain existing selections
23974      */
23975     selectRange : function(startRow, endRow, keepExisting){
23976         if(this.locked) {
23977             return;
23978         }
23979         if(!keepExisting){
23980             this.clearSelections();
23981         }
23982         if(startRow <= endRow){
23983             for(var i = startRow; i <= endRow; i++){
23984                 this.selectRow(i, true);
23985             }
23986         }else{
23987             for(var i = startRow; i >= endRow; i--){
23988                 this.selectRow(i, true);
23989             }
23990         }
23991     },
23992
23993     /**
23994      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23995      * @param {Number} startRow The index of the first row in the range
23996      * @param {Number} endRow The index of the last row in the range
23997      */
23998     deselectRange : function(startRow, endRow, preventViewNotify){
23999         if(this.locked) {
24000             return;
24001         }
24002         for(var i = startRow; i <= endRow; i++){
24003             this.deselectRow(i, preventViewNotify);
24004         }
24005     },
24006
24007     /**
24008      * Selects a row.
24009      * @param {Number} row The index of the row to select
24010      * @param {Boolean} keepExisting (optional) True to keep existing selections
24011      */
24012     selectRow : function(index, keepExisting, preventViewNotify)
24013     {
24014             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24015             return;
24016         }
24017         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24018             if(!keepExisting || this.singleSelect){
24019                 this.clearSelections();
24020             }
24021             
24022             var r = this.grid.store.getAt(index);
24023             //console.log('selectRow - record id :' + r.id);
24024             
24025             this.selections.add(r);
24026             this.last = this.lastActive = index;
24027             if(!preventViewNotify){
24028                 var proxy = new Roo.Element(
24029                                 this.grid.getRowDom(index)
24030                 );
24031                 proxy.addClass('bg-info info');
24032             }
24033             this.fireEvent("rowselect", this, index, r);
24034             this.fireEvent("selectionchange", this);
24035         }
24036     },
24037
24038     /**
24039      * Deselects a row.
24040      * @param {Number} row The index of the row to deselect
24041      */
24042     deselectRow : function(index, preventViewNotify)
24043     {
24044         if(this.locked) {
24045             return;
24046         }
24047         if(this.last == index){
24048             this.last = false;
24049         }
24050         if(this.lastActive == index){
24051             this.lastActive = false;
24052         }
24053         
24054         var r = this.grid.store.getAt(index);
24055         if (!r) {
24056             return;
24057         }
24058         
24059         this.selections.remove(r);
24060         //.console.log('deselectRow - record id :' + r.id);
24061         if(!preventViewNotify){
24062         
24063             var proxy = new Roo.Element(
24064                 this.grid.getRowDom(index)
24065             );
24066             proxy.removeClass('bg-info info');
24067         }
24068         this.fireEvent("rowdeselect", this, index);
24069         this.fireEvent("selectionchange", this);
24070     },
24071
24072     // private
24073     restoreLast : function(){
24074         if(this._last){
24075             this.last = this._last;
24076         }
24077     },
24078
24079     // private
24080     acceptsNav : function(row, col, cm){
24081         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24082     },
24083
24084     // private
24085     onEditorKey : function(field, e){
24086         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24087         if(k == e.TAB){
24088             e.stopEvent();
24089             ed.completeEdit();
24090             if(e.shiftKey){
24091                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24092             }else{
24093                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24094             }
24095         }else if(k == e.ENTER && !e.ctrlKey){
24096             e.stopEvent();
24097             ed.completeEdit();
24098             if(e.shiftKey){
24099                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24100             }else{
24101                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24102             }
24103         }else if(k == e.ESC){
24104             ed.cancelEdit();
24105         }
24106         if(newCell){
24107             g.startEditing(newCell[0], newCell[1]);
24108         }
24109     }
24110 });
24111 /*
24112  * Based on:
24113  * Ext JS Library 1.1.1
24114  * Copyright(c) 2006-2007, Ext JS, LLC.
24115  *
24116  * Originally Released Under LGPL - original licence link has changed is not relivant.
24117  *
24118  * Fork - LGPL
24119  * <script type="text/javascript">
24120  */
24121  
24122 /**
24123  * @class Roo.bootstrap.PagingToolbar
24124  * @extends Roo.bootstrap.NavSimplebar
24125  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24126  * @constructor
24127  * Create a new PagingToolbar
24128  * @param {Object} config The config object
24129  * @param {Roo.data.Store} store
24130  */
24131 Roo.bootstrap.PagingToolbar = function(config)
24132 {
24133     // old args format still supported... - xtype is prefered..
24134         // created from xtype...
24135     
24136     this.ds = config.dataSource;
24137     
24138     if (config.store && !this.ds) {
24139         this.store= Roo.factory(config.store, Roo.data);
24140         this.ds = this.store;
24141         this.ds.xmodule = this.xmodule || false;
24142     }
24143     
24144     this.toolbarItems = [];
24145     if (config.items) {
24146         this.toolbarItems = config.items;
24147     }
24148     
24149     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24150     
24151     this.cursor = 0;
24152     
24153     if (this.ds) { 
24154         this.bind(this.ds);
24155     }
24156     
24157     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24158     
24159 };
24160
24161 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24162     /**
24163      * @cfg {Roo.data.Store} dataSource
24164      * The underlying data store providing the paged data
24165      */
24166     /**
24167      * @cfg {String/HTMLElement/Element} container
24168      * container The id or element that will contain the toolbar
24169      */
24170     /**
24171      * @cfg {Boolean} displayInfo
24172      * True to display the displayMsg (defaults to false)
24173      */
24174     /**
24175      * @cfg {Number} pageSize
24176      * The number of records to display per page (defaults to 20)
24177      */
24178     pageSize: 20,
24179     /**
24180      * @cfg {String} displayMsg
24181      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24182      */
24183     displayMsg : 'Displaying {0} - {1} of {2}',
24184     /**
24185      * @cfg {String} emptyMsg
24186      * The message to display when no records are found (defaults to "No data to display")
24187      */
24188     emptyMsg : 'No data to display',
24189     /**
24190      * Customizable piece of the default paging text (defaults to "Page")
24191      * @type String
24192      */
24193     beforePageText : "Page",
24194     /**
24195      * Customizable piece of the default paging text (defaults to "of %0")
24196      * @type String
24197      */
24198     afterPageText : "of {0}",
24199     /**
24200      * Customizable piece of the default paging text (defaults to "First Page")
24201      * @type String
24202      */
24203     firstText : "First Page",
24204     /**
24205      * Customizable piece of the default paging text (defaults to "Previous Page")
24206      * @type String
24207      */
24208     prevText : "Previous Page",
24209     /**
24210      * Customizable piece of the default paging text (defaults to "Next Page")
24211      * @type String
24212      */
24213     nextText : "Next Page",
24214     /**
24215      * Customizable piece of the default paging text (defaults to "Last Page")
24216      * @type String
24217      */
24218     lastText : "Last Page",
24219     /**
24220      * Customizable piece of the default paging text (defaults to "Refresh")
24221      * @type String
24222      */
24223     refreshText : "Refresh",
24224
24225     buttons : false,
24226     // private
24227     onRender : function(ct, position) 
24228     {
24229         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24230         this.navgroup.parentId = this.id;
24231         this.navgroup.onRender(this.el, null);
24232         // add the buttons to the navgroup
24233         
24234         if(this.displayInfo){
24235             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24236             this.displayEl = this.el.select('.x-paging-info', true).first();
24237 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24238 //            this.displayEl = navel.el.select('span',true).first();
24239         }
24240         
24241         var _this = this;
24242         
24243         if(this.buttons){
24244             Roo.each(_this.buttons, function(e){ // this might need to use render????
24245                Roo.factory(e).onRender(_this.el, null);
24246             });
24247         }
24248             
24249         Roo.each(_this.toolbarItems, function(e) {
24250             _this.navgroup.addItem(e);
24251         });
24252         
24253         
24254         this.first = this.navgroup.addItem({
24255             tooltip: this.firstText,
24256             cls: "prev",
24257             icon : 'fa fa-backward',
24258             disabled: true,
24259             preventDefault: true,
24260             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24261         });
24262         
24263         this.prev =  this.navgroup.addItem({
24264             tooltip: this.prevText,
24265             cls: "prev",
24266             icon : 'fa fa-step-backward',
24267             disabled: true,
24268             preventDefault: true,
24269             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24270         });
24271     //this.addSeparator();
24272         
24273         
24274         var field = this.navgroup.addItem( {
24275             tagtype : 'span',
24276             cls : 'x-paging-position',
24277             
24278             html : this.beforePageText  +
24279                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24280                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24281          } ); //?? escaped?
24282         
24283         this.field = field.el.select('input', true).first();
24284         this.field.on("keydown", this.onPagingKeydown, this);
24285         this.field.on("focus", function(){this.dom.select();});
24286     
24287     
24288         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24289         //this.field.setHeight(18);
24290         //this.addSeparator();
24291         this.next = this.navgroup.addItem({
24292             tooltip: this.nextText,
24293             cls: "next",
24294             html : ' <i class="fa fa-step-forward">',
24295             disabled: true,
24296             preventDefault: true,
24297             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24298         });
24299         this.last = this.navgroup.addItem({
24300             tooltip: this.lastText,
24301             icon : 'fa fa-forward',
24302             cls: "next",
24303             disabled: true,
24304             preventDefault: true,
24305             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24306         });
24307     //this.addSeparator();
24308         this.loading = this.navgroup.addItem({
24309             tooltip: this.refreshText,
24310             icon: 'fa fa-refresh',
24311             preventDefault: true,
24312             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24313         });
24314         
24315     },
24316
24317     // private
24318     updateInfo : function(){
24319         if(this.displayEl){
24320             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24321             var msg = count == 0 ?
24322                 this.emptyMsg :
24323                 String.format(
24324                     this.displayMsg,
24325                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24326                 );
24327             this.displayEl.update(msg);
24328         }
24329     },
24330
24331     // private
24332     onLoad : function(ds, r, o)
24333     {
24334         this.cursor = o.params ? o.params.start : 0;
24335         var d = this.getPageData(),
24336             ap = d.activePage,
24337             ps = d.pages;
24338         
24339         
24340         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24341         this.field.dom.value = ap;
24342         this.first.setDisabled(ap == 1);
24343         this.prev.setDisabled(ap == 1);
24344         this.next.setDisabled(ap == ps);
24345         this.last.setDisabled(ap == ps);
24346         this.loading.enable();
24347         this.updateInfo();
24348     },
24349
24350     // private
24351     getPageData : function(){
24352         var total = this.ds.getTotalCount();
24353         return {
24354             total : total,
24355             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24356             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24357         };
24358     },
24359
24360     // private
24361     onLoadError : function(){
24362         this.loading.enable();
24363     },
24364
24365     // private
24366     onPagingKeydown : function(e){
24367         var k = e.getKey();
24368         var d = this.getPageData();
24369         if(k == e.RETURN){
24370             var v = this.field.dom.value, pageNum;
24371             if(!v || isNaN(pageNum = parseInt(v, 10))){
24372                 this.field.dom.value = d.activePage;
24373                 return;
24374             }
24375             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24376             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24377             e.stopEvent();
24378         }
24379         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))
24380         {
24381           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24382           this.field.dom.value = pageNum;
24383           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24384           e.stopEvent();
24385         }
24386         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24387         {
24388           var v = this.field.dom.value, pageNum; 
24389           var increment = (e.shiftKey) ? 10 : 1;
24390           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24391                 increment *= -1;
24392           }
24393           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24394             this.field.dom.value = d.activePage;
24395             return;
24396           }
24397           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24398           {
24399             this.field.dom.value = parseInt(v, 10) + increment;
24400             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24401             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24402           }
24403           e.stopEvent();
24404         }
24405     },
24406
24407     // private
24408     beforeLoad : function(){
24409         if(this.loading){
24410             this.loading.disable();
24411         }
24412     },
24413
24414     // private
24415     onClick : function(which){
24416         
24417         var ds = this.ds;
24418         if (!ds) {
24419             return;
24420         }
24421         
24422         switch(which){
24423             case "first":
24424                 ds.load({params:{start: 0, limit: this.pageSize}});
24425             break;
24426             case "prev":
24427                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24428             break;
24429             case "next":
24430                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24431             break;
24432             case "last":
24433                 var total = ds.getTotalCount();
24434                 var extra = total % this.pageSize;
24435                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24436                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24437             break;
24438             case "refresh":
24439                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24440             break;
24441         }
24442     },
24443
24444     /**
24445      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24446      * @param {Roo.data.Store} store The data store to unbind
24447      */
24448     unbind : function(ds){
24449         ds.un("beforeload", this.beforeLoad, this);
24450         ds.un("load", this.onLoad, this);
24451         ds.un("loadexception", this.onLoadError, this);
24452         ds.un("remove", this.updateInfo, this);
24453         ds.un("add", this.updateInfo, this);
24454         this.ds = undefined;
24455     },
24456
24457     /**
24458      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24459      * @param {Roo.data.Store} store The data store to bind
24460      */
24461     bind : function(ds){
24462         ds.on("beforeload", this.beforeLoad, this);
24463         ds.on("load", this.onLoad, this);
24464         ds.on("loadexception", this.onLoadError, this);
24465         ds.on("remove", this.updateInfo, this);
24466         ds.on("add", this.updateInfo, this);
24467         this.ds = ds;
24468     }
24469 });/*
24470  * - LGPL
24471  *
24472  * element
24473  * 
24474  */
24475
24476 /**
24477  * @class Roo.bootstrap.MessageBar
24478  * @extends Roo.bootstrap.Component
24479  * Bootstrap MessageBar class
24480  * @cfg {String} html contents of the MessageBar
24481  * @cfg {String} weight (info | success | warning | danger) default info
24482  * @cfg {String} beforeClass insert the bar before the given class
24483  * @cfg {Boolean} closable (true | false) default false
24484  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24485  * 
24486  * @constructor
24487  * Create a new Element
24488  * @param {Object} config The config object
24489  */
24490
24491 Roo.bootstrap.MessageBar = function(config){
24492     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24493 };
24494
24495 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24496     
24497     html: '',
24498     weight: 'info',
24499     closable: false,
24500     fixed: false,
24501     beforeClass: 'bootstrap-sticky-wrap',
24502     
24503     getAutoCreate : function(){
24504         
24505         var cfg = {
24506             tag: 'div',
24507             cls: 'alert alert-dismissable alert-' + this.weight,
24508             cn: [
24509                 {
24510                     tag: 'span',
24511                     cls: 'message',
24512                     html: this.html || ''
24513                 }
24514             ]
24515         };
24516         
24517         if(this.fixed){
24518             cfg.cls += ' alert-messages-fixed';
24519         }
24520         
24521         if(this.closable){
24522             cfg.cn.push({
24523                 tag: 'button',
24524                 cls: 'close',
24525                 html: 'x'
24526             });
24527         }
24528         
24529         return cfg;
24530     },
24531     
24532     onRender : function(ct, position)
24533     {
24534         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24535         
24536         if(!this.el){
24537             var cfg = Roo.apply({},  this.getAutoCreate());
24538             cfg.id = Roo.id();
24539             
24540             if (this.cls) {
24541                 cfg.cls += ' ' + this.cls;
24542             }
24543             if (this.style) {
24544                 cfg.style = this.style;
24545             }
24546             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24547             
24548             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24549         }
24550         
24551         this.el.select('>button.close').on('click', this.hide, this);
24552         
24553     },
24554     
24555     show : function()
24556     {
24557         if (!this.rendered) {
24558             this.render();
24559         }
24560         
24561         this.el.show();
24562         
24563         this.fireEvent('show', this);
24564         
24565     },
24566     
24567     hide : function()
24568     {
24569         if (!this.rendered) {
24570             this.render();
24571         }
24572         
24573         this.el.hide();
24574         
24575         this.fireEvent('hide', this);
24576     },
24577     
24578     update : function()
24579     {
24580 //        var e = this.el.dom.firstChild;
24581 //        
24582 //        if(this.closable){
24583 //            e = e.nextSibling;
24584 //        }
24585 //        
24586 //        e.data = this.html || '';
24587
24588         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24589     }
24590    
24591 });
24592
24593  
24594
24595      /*
24596  * - LGPL
24597  *
24598  * Graph
24599  * 
24600  */
24601
24602
24603 /**
24604  * @class Roo.bootstrap.Graph
24605  * @extends Roo.bootstrap.Component
24606  * Bootstrap Graph class
24607 > Prameters
24608  -sm {number} sm 4
24609  -md {number} md 5
24610  @cfg {String} graphtype  bar | vbar | pie
24611  @cfg {number} g_x coodinator | centre x (pie)
24612  @cfg {number} g_y coodinator | centre y (pie)
24613  @cfg {number} g_r radius (pie)
24614  @cfg {number} g_height height of the chart (respected by all elements in the set)
24615  @cfg {number} g_width width of the chart (respected by all elements in the set)
24616  @cfg {Object} title The title of the chart
24617     
24618  -{Array}  values
24619  -opts (object) options for the chart 
24620      o {
24621      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24622      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24623      o vgutter (number)
24624      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.
24625      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24626      o to
24627      o stretch (boolean)
24628      o }
24629  -opts (object) options for the pie
24630      o{
24631      o cut
24632      o startAngle (number)
24633      o endAngle (number)
24634      } 
24635  *
24636  * @constructor
24637  * Create a new Input
24638  * @param {Object} config The config object
24639  */
24640
24641 Roo.bootstrap.Graph = function(config){
24642     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24643     
24644     this.addEvents({
24645         // img events
24646         /**
24647          * @event click
24648          * The img click event for the img.
24649          * @param {Roo.EventObject} e
24650          */
24651         "click" : true
24652     });
24653 };
24654
24655 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24656     
24657     sm: 4,
24658     md: 5,
24659     graphtype: 'bar',
24660     g_height: 250,
24661     g_width: 400,
24662     g_x: 50,
24663     g_y: 50,
24664     g_r: 30,
24665     opts:{
24666         //g_colors: this.colors,
24667         g_type: 'soft',
24668         g_gutter: '20%'
24669
24670     },
24671     title : false,
24672
24673     getAutoCreate : function(){
24674         
24675         var cfg = {
24676             tag: 'div',
24677             html : null
24678         };
24679         
24680         
24681         return  cfg;
24682     },
24683
24684     onRender : function(ct,position){
24685         
24686         
24687         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24688         
24689         if (typeof(Raphael) == 'undefined') {
24690             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24691             return;
24692         }
24693         
24694         this.raphael = Raphael(this.el.dom);
24695         
24696                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24697                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24698                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24699                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24700                 /*
24701                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24702                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24703                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24704                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24705                 
24706                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24707                 r.barchart(330, 10, 300, 220, data1);
24708                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24709                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24710                 */
24711                 
24712                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24713                 // r.barchart(30, 30, 560, 250,  xdata, {
24714                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24715                 //     axis : "0 0 1 1",
24716                 //     axisxlabels :  xdata
24717                 //     //yvalues : cols,
24718                    
24719                 // });
24720 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24721 //        
24722 //        this.load(null,xdata,{
24723 //                axis : "0 0 1 1",
24724 //                axisxlabels :  xdata
24725 //                });
24726
24727     },
24728
24729     load : function(graphtype,xdata,opts)
24730     {
24731         this.raphael.clear();
24732         if(!graphtype) {
24733             graphtype = this.graphtype;
24734         }
24735         if(!opts){
24736             opts = this.opts;
24737         }
24738         var r = this.raphael,
24739             fin = function () {
24740                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24741             },
24742             fout = function () {
24743                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24744             },
24745             pfin = function() {
24746                 this.sector.stop();
24747                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24748
24749                 if (this.label) {
24750                     this.label[0].stop();
24751                     this.label[0].attr({ r: 7.5 });
24752                     this.label[1].attr({ "font-weight": 800 });
24753                 }
24754             },
24755             pfout = function() {
24756                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24757
24758                 if (this.label) {
24759                     this.label[0].animate({ r: 5 }, 500, "bounce");
24760                     this.label[1].attr({ "font-weight": 400 });
24761                 }
24762             };
24763
24764         switch(graphtype){
24765             case 'bar':
24766                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24767                 break;
24768             case 'hbar':
24769                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24770                 break;
24771             case 'pie':
24772 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24773 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24774 //            
24775                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24776                 
24777                 break;
24778
24779         }
24780         
24781         if(this.title){
24782             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24783         }
24784         
24785     },
24786     
24787     setTitle: function(o)
24788     {
24789         this.title = o;
24790     },
24791     
24792     initEvents: function() {
24793         
24794         if(!this.href){
24795             this.el.on('click', this.onClick, this);
24796         }
24797     },
24798     
24799     onClick : function(e)
24800     {
24801         Roo.log('img onclick');
24802         this.fireEvent('click', this, e);
24803     }
24804    
24805 });
24806
24807  
24808 /*
24809  * - LGPL
24810  *
24811  * numberBox
24812  * 
24813  */
24814 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24815
24816 /**
24817  * @class Roo.bootstrap.dash.NumberBox
24818  * @extends Roo.bootstrap.Component
24819  * Bootstrap NumberBox class
24820  * @cfg {String} headline Box headline
24821  * @cfg {String} content Box content
24822  * @cfg {String} icon Box icon
24823  * @cfg {String} footer Footer text
24824  * @cfg {String} fhref Footer href
24825  * 
24826  * @constructor
24827  * Create a new NumberBox
24828  * @param {Object} config The config object
24829  */
24830
24831
24832 Roo.bootstrap.dash.NumberBox = function(config){
24833     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24834     
24835 };
24836
24837 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24838     
24839     headline : '',
24840     content : '',
24841     icon : '',
24842     footer : '',
24843     fhref : '',
24844     ficon : '',
24845     
24846     getAutoCreate : function(){
24847         
24848         var cfg = {
24849             tag : 'div',
24850             cls : 'small-box ',
24851             cn : [
24852                 {
24853                     tag : 'div',
24854                     cls : 'inner',
24855                     cn :[
24856                         {
24857                             tag : 'h3',
24858                             cls : 'roo-headline',
24859                             html : this.headline
24860                         },
24861                         {
24862                             tag : 'p',
24863                             cls : 'roo-content',
24864                             html : this.content
24865                         }
24866                     ]
24867                 }
24868             ]
24869         };
24870         
24871         if(this.icon){
24872             cfg.cn.push({
24873                 tag : 'div',
24874                 cls : 'icon',
24875                 cn :[
24876                     {
24877                         tag : 'i',
24878                         cls : 'ion ' + this.icon
24879                     }
24880                 ]
24881             });
24882         }
24883         
24884         if(this.footer){
24885             var footer = {
24886                 tag : 'a',
24887                 cls : 'small-box-footer',
24888                 href : this.fhref || '#',
24889                 html : this.footer
24890             };
24891             
24892             cfg.cn.push(footer);
24893             
24894         }
24895         
24896         return  cfg;
24897     },
24898
24899     onRender : function(ct,position){
24900         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24901
24902
24903        
24904                 
24905     },
24906
24907     setHeadline: function (value)
24908     {
24909         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24910     },
24911     
24912     setFooter: function (value, href)
24913     {
24914         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24915         
24916         if(href){
24917             this.el.select('a.small-box-footer',true).first().attr('href', href);
24918         }
24919         
24920     },
24921
24922     setContent: function (value)
24923     {
24924         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24925     },
24926
24927     initEvents: function() 
24928     {   
24929         
24930     }
24931     
24932 });
24933
24934  
24935 /*
24936  * - LGPL
24937  *
24938  * TabBox
24939  * 
24940  */
24941 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24942
24943 /**
24944  * @class Roo.bootstrap.dash.TabBox
24945  * @extends Roo.bootstrap.Component
24946  * Bootstrap TabBox class
24947  * @cfg {String} title Title of the TabBox
24948  * @cfg {String} icon Icon of the TabBox
24949  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24950  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24951  * 
24952  * @constructor
24953  * Create a new TabBox
24954  * @param {Object} config The config object
24955  */
24956
24957
24958 Roo.bootstrap.dash.TabBox = function(config){
24959     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24960     this.addEvents({
24961         // raw events
24962         /**
24963          * @event addpane
24964          * When a pane is added
24965          * @param {Roo.bootstrap.dash.TabPane} pane
24966          */
24967         "addpane" : true,
24968         /**
24969          * @event activatepane
24970          * When a pane is activated
24971          * @param {Roo.bootstrap.dash.TabPane} pane
24972          */
24973         "activatepane" : true
24974         
24975          
24976     });
24977     
24978     this.panes = [];
24979 };
24980
24981 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24982
24983     title : '',
24984     icon : false,
24985     showtabs : true,
24986     tabScrollable : false,
24987     
24988     getChildContainer : function()
24989     {
24990         return this.el.select('.tab-content', true).first();
24991     },
24992     
24993     getAutoCreate : function(){
24994         
24995         var header = {
24996             tag: 'li',
24997             cls: 'pull-left header',
24998             html: this.title,
24999             cn : []
25000         };
25001         
25002         if(this.icon){
25003             header.cn.push({
25004                 tag: 'i',
25005                 cls: 'fa ' + this.icon
25006             });
25007         }
25008         
25009         var h = {
25010             tag: 'ul',
25011             cls: 'nav nav-tabs pull-right',
25012             cn: [
25013                 header
25014             ]
25015         };
25016         
25017         if(this.tabScrollable){
25018             h = {
25019                 tag: 'div',
25020                 cls: 'tab-header',
25021                 cn: [
25022                     {
25023                         tag: 'ul',
25024                         cls: 'nav nav-tabs pull-right',
25025                         cn: [
25026                             header
25027                         ]
25028                     }
25029                 ]
25030             };
25031         }
25032         
25033         var cfg = {
25034             tag: 'div',
25035             cls: 'nav-tabs-custom',
25036             cn: [
25037                 h,
25038                 {
25039                     tag: 'div',
25040                     cls: 'tab-content no-padding',
25041                     cn: []
25042                 }
25043             ]
25044         };
25045
25046         return  cfg;
25047     },
25048     initEvents : function()
25049     {
25050         //Roo.log('add add pane handler');
25051         this.on('addpane', this.onAddPane, this);
25052     },
25053      /**
25054      * Updates the box title
25055      * @param {String} html to set the title to.
25056      */
25057     setTitle : function(value)
25058     {
25059         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25060     },
25061     onAddPane : function(pane)
25062     {
25063         this.panes.push(pane);
25064         //Roo.log('addpane');
25065         //Roo.log(pane);
25066         // tabs are rendere left to right..
25067         if(!this.showtabs){
25068             return;
25069         }
25070         
25071         var ctr = this.el.select('.nav-tabs', true).first();
25072          
25073          
25074         var existing = ctr.select('.nav-tab',true);
25075         var qty = existing.getCount();;
25076         
25077         
25078         var tab = ctr.createChild({
25079             tag : 'li',
25080             cls : 'nav-tab' + (qty ? '' : ' active'),
25081             cn : [
25082                 {
25083                     tag : 'a',
25084                     href:'#',
25085                     html : pane.title
25086                 }
25087             ]
25088         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25089         pane.tab = tab;
25090         
25091         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25092         if (!qty) {
25093             pane.el.addClass('active');
25094         }
25095         
25096                 
25097     },
25098     onTabClick : function(ev,un,ob,pane)
25099     {
25100         //Roo.log('tab - prev default');
25101         ev.preventDefault();
25102         
25103         
25104         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25105         pane.tab.addClass('active');
25106         //Roo.log(pane.title);
25107         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25108         // technically we should have a deactivate event.. but maybe add later.
25109         // and it should not de-activate the selected tab...
25110         this.fireEvent('activatepane', pane);
25111         pane.el.addClass('active');
25112         pane.fireEvent('activate');
25113         
25114         
25115     },
25116     
25117     getActivePane : function()
25118     {
25119         var r = false;
25120         Roo.each(this.panes, function(p) {
25121             if(p.el.hasClass('active')){
25122                 r = p;
25123                 return false;
25124             }
25125             
25126             return;
25127         });
25128         
25129         return r;
25130     }
25131     
25132     
25133 });
25134
25135  
25136 /*
25137  * - LGPL
25138  *
25139  * Tab pane
25140  * 
25141  */
25142 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25143 /**
25144  * @class Roo.bootstrap.TabPane
25145  * @extends Roo.bootstrap.Component
25146  * Bootstrap TabPane class
25147  * @cfg {Boolean} active (false | true) Default false
25148  * @cfg {String} title title of panel
25149
25150  * 
25151  * @constructor
25152  * Create a new TabPane
25153  * @param {Object} config The config object
25154  */
25155
25156 Roo.bootstrap.dash.TabPane = function(config){
25157     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25158     
25159     this.addEvents({
25160         // raw events
25161         /**
25162          * @event activate
25163          * When a pane is activated
25164          * @param {Roo.bootstrap.dash.TabPane} pane
25165          */
25166         "activate" : true
25167          
25168     });
25169 };
25170
25171 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25172     
25173     active : false,
25174     title : '',
25175     
25176     // the tabBox that this is attached to.
25177     tab : false,
25178      
25179     getAutoCreate : function() 
25180     {
25181         var cfg = {
25182             tag: 'div',
25183             cls: 'tab-pane'
25184         };
25185         
25186         if(this.active){
25187             cfg.cls += ' active';
25188         }
25189         
25190         return cfg;
25191     },
25192     initEvents  : function()
25193     {
25194         //Roo.log('trigger add pane handler');
25195         this.parent().fireEvent('addpane', this)
25196     },
25197     
25198      /**
25199      * Updates the tab title 
25200      * @param {String} html to set the title to.
25201      */
25202     setTitle: function(str)
25203     {
25204         if (!this.tab) {
25205             return;
25206         }
25207         this.title = str;
25208         this.tab.select('a', true).first().dom.innerHTML = str;
25209         
25210     }
25211     
25212     
25213     
25214 });
25215
25216  
25217
25218
25219  /*
25220  * - LGPL
25221  *
25222  * menu
25223  * 
25224  */
25225 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25226
25227 /**
25228  * @class Roo.bootstrap.menu.Menu
25229  * @extends Roo.bootstrap.Component
25230  * Bootstrap Menu class - container for Menu
25231  * @cfg {String} html Text of the menu
25232  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25233  * @cfg {String} icon Font awesome icon
25234  * @cfg {String} pos Menu align to (top | bottom) default bottom
25235  * 
25236  * 
25237  * @constructor
25238  * Create a new Menu
25239  * @param {Object} config The config object
25240  */
25241
25242
25243 Roo.bootstrap.menu.Menu = function(config){
25244     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25245     
25246     this.addEvents({
25247         /**
25248          * @event beforeshow
25249          * Fires before this menu is displayed
25250          * @param {Roo.bootstrap.menu.Menu} this
25251          */
25252         beforeshow : true,
25253         /**
25254          * @event beforehide
25255          * Fires before this menu is hidden
25256          * @param {Roo.bootstrap.menu.Menu} this
25257          */
25258         beforehide : true,
25259         /**
25260          * @event show
25261          * Fires after this menu is displayed
25262          * @param {Roo.bootstrap.menu.Menu} this
25263          */
25264         show : true,
25265         /**
25266          * @event hide
25267          * Fires after this menu is hidden
25268          * @param {Roo.bootstrap.menu.Menu} this
25269          */
25270         hide : true,
25271         /**
25272          * @event click
25273          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25274          * @param {Roo.bootstrap.menu.Menu} this
25275          * @param {Roo.EventObject} e
25276          */
25277         click : true
25278     });
25279     
25280 };
25281
25282 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25283     
25284     submenu : false,
25285     html : '',
25286     weight : 'default',
25287     icon : false,
25288     pos : 'bottom',
25289     
25290     
25291     getChildContainer : function() {
25292         if(this.isSubMenu){
25293             return this.el;
25294         }
25295         
25296         return this.el.select('ul.dropdown-menu', true).first();  
25297     },
25298     
25299     getAutoCreate : function()
25300     {
25301         var text = [
25302             {
25303                 tag : 'span',
25304                 cls : 'roo-menu-text',
25305                 html : this.html
25306             }
25307         ];
25308         
25309         if(this.icon){
25310             text.unshift({
25311                 tag : 'i',
25312                 cls : 'fa ' + this.icon
25313             })
25314         }
25315         
25316         
25317         var cfg = {
25318             tag : 'div',
25319             cls : 'btn-group',
25320             cn : [
25321                 {
25322                     tag : 'button',
25323                     cls : 'dropdown-button btn btn-' + this.weight,
25324                     cn : text
25325                 },
25326                 {
25327                     tag : 'button',
25328                     cls : 'dropdown-toggle btn btn-' + this.weight,
25329                     cn : [
25330                         {
25331                             tag : 'span',
25332                             cls : 'caret'
25333                         }
25334                     ]
25335                 },
25336                 {
25337                     tag : 'ul',
25338                     cls : 'dropdown-menu'
25339                 }
25340             ]
25341             
25342         };
25343         
25344         if(this.pos == 'top'){
25345             cfg.cls += ' dropup';
25346         }
25347         
25348         if(this.isSubMenu){
25349             cfg = {
25350                 tag : 'ul',
25351                 cls : 'dropdown-menu'
25352             }
25353         }
25354         
25355         return cfg;
25356     },
25357     
25358     onRender : function(ct, position)
25359     {
25360         this.isSubMenu = ct.hasClass('dropdown-submenu');
25361         
25362         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25363     },
25364     
25365     initEvents : function() 
25366     {
25367         if(this.isSubMenu){
25368             return;
25369         }
25370         
25371         this.hidden = true;
25372         
25373         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25374         this.triggerEl.on('click', this.onTriggerPress, this);
25375         
25376         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25377         this.buttonEl.on('click', this.onClick, this);
25378         
25379     },
25380     
25381     list : function()
25382     {
25383         if(this.isSubMenu){
25384             return this.el;
25385         }
25386         
25387         return this.el.select('ul.dropdown-menu', true).first();
25388     },
25389     
25390     onClick : function(e)
25391     {
25392         this.fireEvent("click", this, e);
25393     },
25394     
25395     onTriggerPress  : function(e)
25396     {   
25397         if (this.isVisible()) {
25398             this.hide();
25399         } else {
25400             this.show();
25401         }
25402     },
25403     
25404     isVisible : function(){
25405         return !this.hidden;
25406     },
25407     
25408     show : function()
25409     {
25410         this.fireEvent("beforeshow", this);
25411         
25412         this.hidden = false;
25413         this.el.addClass('open');
25414         
25415         Roo.get(document).on("mouseup", this.onMouseUp, this);
25416         
25417         this.fireEvent("show", this);
25418         
25419         
25420     },
25421     
25422     hide : function()
25423     {
25424         this.fireEvent("beforehide", this);
25425         
25426         this.hidden = true;
25427         this.el.removeClass('open');
25428         
25429         Roo.get(document).un("mouseup", this.onMouseUp);
25430         
25431         this.fireEvent("hide", this);
25432     },
25433     
25434     onMouseUp : function()
25435     {
25436         this.hide();
25437     }
25438     
25439 });
25440
25441  
25442  /*
25443  * - LGPL
25444  *
25445  * menu item
25446  * 
25447  */
25448 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25449
25450 /**
25451  * @class Roo.bootstrap.menu.Item
25452  * @extends Roo.bootstrap.Component
25453  * Bootstrap MenuItem class
25454  * @cfg {Boolean} submenu (true | false) default false
25455  * @cfg {String} html text of the item
25456  * @cfg {String} href the link
25457  * @cfg {Boolean} disable (true | false) default false
25458  * @cfg {Boolean} preventDefault (true | false) default true
25459  * @cfg {String} icon Font awesome icon
25460  * @cfg {String} pos Submenu align to (left | right) default right 
25461  * 
25462  * 
25463  * @constructor
25464  * Create a new Item
25465  * @param {Object} config The config object
25466  */
25467
25468
25469 Roo.bootstrap.menu.Item = function(config){
25470     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25471     this.addEvents({
25472         /**
25473          * @event mouseover
25474          * Fires when the mouse is hovering over this menu
25475          * @param {Roo.bootstrap.menu.Item} this
25476          * @param {Roo.EventObject} e
25477          */
25478         mouseover : true,
25479         /**
25480          * @event mouseout
25481          * Fires when the mouse exits this menu
25482          * @param {Roo.bootstrap.menu.Item} this
25483          * @param {Roo.EventObject} e
25484          */
25485         mouseout : true,
25486         // raw events
25487         /**
25488          * @event click
25489          * The raw click event for the entire grid.
25490          * @param {Roo.EventObject} e
25491          */
25492         click : true
25493     });
25494 };
25495
25496 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25497     
25498     submenu : false,
25499     href : '',
25500     html : '',
25501     preventDefault: true,
25502     disable : false,
25503     icon : false,
25504     pos : 'right',
25505     
25506     getAutoCreate : function()
25507     {
25508         var text = [
25509             {
25510                 tag : 'span',
25511                 cls : 'roo-menu-item-text',
25512                 html : this.html
25513             }
25514         ];
25515         
25516         if(this.icon){
25517             text.unshift({
25518                 tag : 'i',
25519                 cls : 'fa ' + this.icon
25520             })
25521         }
25522         
25523         var cfg = {
25524             tag : 'li',
25525             cn : [
25526                 {
25527                     tag : 'a',
25528                     href : this.href || '#',
25529                     cn : text
25530                 }
25531             ]
25532         };
25533         
25534         if(this.disable){
25535             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25536         }
25537         
25538         if(this.submenu){
25539             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25540             
25541             if(this.pos == 'left'){
25542                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25543             }
25544         }
25545         
25546         return cfg;
25547     },
25548     
25549     initEvents : function() 
25550     {
25551         this.el.on('mouseover', this.onMouseOver, this);
25552         this.el.on('mouseout', this.onMouseOut, this);
25553         
25554         this.el.select('a', true).first().on('click', this.onClick, this);
25555         
25556     },
25557     
25558     onClick : function(e)
25559     {
25560         if(this.preventDefault){
25561             e.preventDefault();
25562         }
25563         
25564         this.fireEvent("click", this, e);
25565     },
25566     
25567     onMouseOver : function(e)
25568     {
25569         if(this.submenu && this.pos == 'left'){
25570             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25571         }
25572         
25573         this.fireEvent("mouseover", this, e);
25574     },
25575     
25576     onMouseOut : function(e)
25577     {
25578         this.fireEvent("mouseout", this, e);
25579     }
25580 });
25581
25582  
25583
25584  /*
25585  * - LGPL
25586  *
25587  * menu separator
25588  * 
25589  */
25590 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25591
25592 /**
25593  * @class Roo.bootstrap.menu.Separator
25594  * @extends Roo.bootstrap.Component
25595  * Bootstrap Separator class
25596  * 
25597  * @constructor
25598  * Create a new Separator
25599  * @param {Object} config The config object
25600  */
25601
25602
25603 Roo.bootstrap.menu.Separator = function(config){
25604     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25605 };
25606
25607 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25608     
25609     getAutoCreate : function(){
25610         var cfg = {
25611             tag : 'li',
25612             cls: 'divider'
25613         };
25614         
25615         return cfg;
25616     }
25617    
25618 });
25619
25620  
25621
25622  /*
25623  * - LGPL
25624  *
25625  * Tooltip
25626  * 
25627  */
25628
25629 /**
25630  * @class Roo.bootstrap.Tooltip
25631  * Bootstrap Tooltip class
25632  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25633  * to determine which dom element triggers the tooltip.
25634  * 
25635  * It needs to add support for additional attributes like tooltip-position
25636  * 
25637  * @constructor
25638  * Create a new Toolti
25639  * @param {Object} config The config object
25640  */
25641
25642 Roo.bootstrap.Tooltip = function(config){
25643     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25644     
25645     this.alignment = Roo.bootstrap.Tooltip.alignment;
25646     
25647     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25648         this.alignment = config.alignment;
25649     }
25650     
25651 };
25652
25653 Roo.apply(Roo.bootstrap.Tooltip, {
25654     /**
25655      * @function init initialize tooltip monitoring.
25656      * @static
25657      */
25658     currentEl : false,
25659     currentTip : false,
25660     currentRegion : false,
25661     
25662     //  init : delay?
25663     
25664     init : function()
25665     {
25666         Roo.get(document).on('mouseover', this.enter ,this);
25667         Roo.get(document).on('mouseout', this.leave, this);
25668          
25669         
25670         this.currentTip = new Roo.bootstrap.Tooltip();
25671     },
25672     
25673     enter : function(ev)
25674     {
25675         var dom = ev.getTarget();
25676         
25677         //Roo.log(['enter',dom]);
25678         var el = Roo.fly(dom);
25679         if (this.currentEl) {
25680             //Roo.log(dom);
25681             //Roo.log(this.currentEl);
25682             //Roo.log(this.currentEl.contains(dom));
25683             if (this.currentEl == el) {
25684                 return;
25685             }
25686             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25687                 return;
25688             }
25689
25690         }
25691         
25692         if (this.currentTip.el) {
25693             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25694         }    
25695         //Roo.log(ev);
25696         
25697         if(!el || el.dom == document){
25698             return;
25699         }
25700         
25701         var bindEl = el;
25702         
25703         // you can not look for children, as if el is the body.. then everythign is the child..
25704         if (!el.attr('tooltip')) { //
25705             if (!el.select("[tooltip]").elements.length) {
25706                 return;
25707             }
25708             // is the mouse over this child...?
25709             bindEl = el.select("[tooltip]").first();
25710             var xy = ev.getXY();
25711             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25712                 //Roo.log("not in region.");
25713                 return;
25714             }
25715             //Roo.log("child element over..");
25716             
25717         }
25718         this.currentEl = bindEl;
25719         this.currentTip.bind(bindEl);
25720         this.currentRegion = Roo.lib.Region.getRegion(dom);
25721         this.currentTip.enter();
25722         
25723     },
25724     leave : function(ev)
25725     {
25726         var dom = ev.getTarget();
25727         //Roo.log(['leave',dom]);
25728         if (!this.currentEl) {
25729             return;
25730         }
25731         
25732         
25733         if (dom != this.currentEl.dom) {
25734             return;
25735         }
25736         var xy = ev.getXY();
25737         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25738             return;
25739         }
25740         // only activate leave if mouse cursor is outside... bounding box..
25741         
25742         
25743         
25744         
25745         if (this.currentTip) {
25746             this.currentTip.leave();
25747         }
25748         //Roo.log('clear currentEl');
25749         this.currentEl = false;
25750         
25751         
25752     },
25753     alignment : {
25754         'left' : ['r-l', [-2,0], 'right'],
25755         'right' : ['l-r', [2,0], 'left'],
25756         'bottom' : ['t-b', [0,2], 'top'],
25757         'top' : [ 'b-t', [0,-2], 'bottom']
25758     }
25759     
25760 });
25761
25762
25763 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25764     
25765     
25766     bindEl : false,
25767     
25768     delay : null, // can be { show : 300 , hide: 500}
25769     
25770     timeout : null,
25771     
25772     hoverState : null, //???
25773     
25774     placement : 'bottom', 
25775     
25776     alignment : false,
25777     
25778     getAutoCreate : function(){
25779     
25780         var cfg = {
25781            cls : 'tooltip',
25782            role : 'tooltip',
25783            cn : [
25784                 {
25785                     cls : 'tooltip-arrow'
25786                 },
25787                 {
25788                     cls : 'tooltip-inner'
25789                 }
25790            ]
25791         };
25792         
25793         return cfg;
25794     },
25795     bind : function(el)
25796     {
25797         this.bindEl = el;
25798     },
25799       
25800     
25801     enter : function () {
25802        
25803         if (this.timeout != null) {
25804             clearTimeout(this.timeout);
25805         }
25806         
25807         this.hoverState = 'in';
25808          //Roo.log("enter - show");
25809         if (!this.delay || !this.delay.show) {
25810             this.show();
25811             return;
25812         }
25813         var _t = this;
25814         this.timeout = setTimeout(function () {
25815             if (_t.hoverState == 'in') {
25816                 _t.show();
25817             }
25818         }, this.delay.show);
25819     },
25820     leave : function()
25821     {
25822         clearTimeout(this.timeout);
25823     
25824         this.hoverState = 'out';
25825          if (!this.delay || !this.delay.hide) {
25826             this.hide();
25827             return;
25828         }
25829        
25830         var _t = this;
25831         this.timeout = setTimeout(function () {
25832             //Roo.log("leave - timeout");
25833             
25834             if (_t.hoverState == 'out') {
25835                 _t.hide();
25836                 Roo.bootstrap.Tooltip.currentEl = false;
25837             }
25838         }, delay);
25839     },
25840     
25841     show : function (msg)
25842     {
25843         if (!this.el) {
25844             this.render(document.body);
25845         }
25846         // set content.
25847         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25848         
25849         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25850         
25851         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25852         
25853         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25854         
25855         var placement = typeof this.placement == 'function' ?
25856             this.placement.call(this, this.el, on_el) :
25857             this.placement;
25858             
25859         var autoToken = /\s?auto?\s?/i;
25860         var autoPlace = autoToken.test(placement);
25861         if (autoPlace) {
25862             placement = placement.replace(autoToken, '') || 'top';
25863         }
25864         
25865         //this.el.detach()
25866         //this.el.setXY([0,0]);
25867         this.el.show();
25868         //this.el.dom.style.display='block';
25869         
25870         //this.el.appendTo(on_el);
25871         
25872         var p = this.getPosition();
25873         var box = this.el.getBox();
25874         
25875         if (autoPlace) {
25876             // fixme..
25877         }
25878         
25879         var align = this.alignment[placement];
25880         
25881         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25882         
25883         if(placement == 'top' || placement == 'bottom'){
25884             if(xy[0] < 0){
25885                 placement = 'right';
25886             }
25887             
25888             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25889                 placement = 'left';
25890             }
25891             
25892             var scroll = Roo.select('body', true).first().getScroll();
25893             
25894             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25895                 placement = 'top';
25896             }
25897             
25898         }
25899         
25900         this.el.alignTo(this.bindEl, align[0],align[1]);
25901         //var arrow = this.el.select('.arrow',true).first();
25902         //arrow.set(align[2], 
25903         
25904         this.el.addClass(placement);
25905         
25906         this.el.addClass('in fade');
25907         
25908         this.hoverState = null;
25909         
25910         if (this.el.hasClass('fade')) {
25911             // fade it?
25912         }
25913         
25914     },
25915     hide : function()
25916     {
25917          
25918         if (!this.el) {
25919             return;
25920         }
25921         //this.el.setXY([0,0]);
25922         this.el.removeClass('in');
25923         //this.el.hide();
25924         
25925     }
25926     
25927 });
25928  
25929
25930  /*
25931  * - LGPL
25932  *
25933  * Location Picker
25934  * 
25935  */
25936
25937 /**
25938  * @class Roo.bootstrap.LocationPicker
25939  * @extends Roo.bootstrap.Component
25940  * Bootstrap LocationPicker class
25941  * @cfg {Number} latitude Position when init default 0
25942  * @cfg {Number} longitude Position when init default 0
25943  * @cfg {Number} zoom default 15
25944  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25945  * @cfg {Boolean} mapTypeControl default false
25946  * @cfg {Boolean} disableDoubleClickZoom default false
25947  * @cfg {Boolean} scrollwheel default true
25948  * @cfg {Boolean} streetViewControl default false
25949  * @cfg {Number} radius default 0
25950  * @cfg {String} locationName
25951  * @cfg {Boolean} draggable default true
25952  * @cfg {Boolean} enableAutocomplete default false
25953  * @cfg {Boolean} enableReverseGeocode default true
25954  * @cfg {String} markerTitle
25955  * 
25956  * @constructor
25957  * Create a new LocationPicker
25958  * @param {Object} config The config object
25959  */
25960
25961
25962 Roo.bootstrap.LocationPicker = function(config){
25963     
25964     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25965     
25966     this.addEvents({
25967         /**
25968          * @event initial
25969          * Fires when the picker initialized.
25970          * @param {Roo.bootstrap.LocationPicker} this
25971          * @param {Google Location} location
25972          */
25973         initial : true,
25974         /**
25975          * @event positionchanged
25976          * Fires when the picker position changed.
25977          * @param {Roo.bootstrap.LocationPicker} this
25978          * @param {Google Location} location
25979          */
25980         positionchanged : true,
25981         /**
25982          * @event resize
25983          * Fires when the map resize.
25984          * @param {Roo.bootstrap.LocationPicker} this
25985          */
25986         resize : true,
25987         /**
25988          * @event show
25989          * Fires when the map show.
25990          * @param {Roo.bootstrap.LocationPicker} this
25991          */
25992         show : true,
25993         /**
25994          * @event hide
25995          * Fires when the map hide.
25996          * @param {Roo.bootstrap.LocationPicker} this
25997          */
25998         hide : true,
25999         /**
26000          * @event mapClick
26001          * Fires when click the map.
26002          * @param {Roo.bootstrap.LocationPicker} this
26003          * @param {Map event} e
26004          */
26005         mapClick : true,
26006         /**
26007          * @event mapRightClick
26008          * Fires when right click the map.
26009          * @param {Roo.bootstrap.LocationPicker} this
26010          * @param {Map event} e
26011          */
26012         mapRightClick : true,
26013         /**
26014          * @event markerClick
26015          * Fires when click the marker.
26016          * @param {Roo.bootstrap.LocationPicker} this
26017          * @param {Map event} e
26018          */
26019         markerClick : true,
26020         /**
26021          * @event markerRightClick
26022          * Fires when right click the marker.
26023          * @param {Roo.bootstrap.LocationPicker} this
26024          * @param {Map event} e
26025          */
26026         markerRightClick : true,
26027         /**
26028          * @event OverlayViewDraw
26029          * Fires when OverlayView Draw
26030          * @param {Roo.bootstrap.LocationPicker} this
26031          */
26032         OverlayViewDraw : true,
26033         /**
26034          * @event OverlayViewOnAdd
26035          * Fires when OverlayView Draw
26036          * @param {Roo.bootstrap.LocationPicker} this
26037          */
26038         OverlayViewOnAdd : true,
26039         /**
26040          * @event OverlayViewOnRemove
26041          * Fires when OverlayView Draw
26042          * @param {Roo.bootstrap.LocationPicker} this
26043          */
26044         OverlayViewOnRemove : true,
26045         /**
26046          * @event OverlayViewShow
26047          * Fires when OverlayView Draw
26048          * @param {Roo.bootstrap.LocationPicker} this
26049          * @param {Pixel} cpx
26050          */
26051         OverlayViewShow : true,
26052         /**
26053          * @event OverlayViewHide
26054          * Fires when OverlayView Draw
26055          * @param {Roo.bootstrap.LocationPicker} this
26056          */
26057         OverlayViewHide : true,
26058         /**
26059          * @event loadexception
26060          * Fires when load google lib failed.
26061          * @param {Roo.bootstrap.LocationPicker} this
26062          */
26063         loadexception : true
26064     });
26065         
26066 };
26067
26068 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26069     
26070     gMapContext: false,
26071     
26072     latitude: 0,
26073     longitude: 0,
26074     zoom: 15,
26075     mapTypeId: false,
26076     mapTypeControl: false,
26077     disableDoubleClickZoom: false,
26078     scrollwheel: true,
26079     streetViewControl: false,
26080     radius: 0,
26081     locationName: '',
26082     draggable: true,
26083     enableAutocomplete: false,
26084     enableReverseGeocode: true,
26085     markerTitle: '',
26086     
26087     getAutoCreate: function()
26088     {
26089
26090         var cfg = {
26091             tag: 'div',
26092             cls: 'roo-location-picker'
26093         };
26094         
26095         return cfg
26096     },
26097     
26098     initEvents: function(ct, position)
26099     {       
26100         if(!this.el.getWidth() || this.isApplied()){
26101             return;
26102         }
26103         
26104         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26105         
26106         this.initial();
26107     },
26108     
26109     initial: function()
26110     {
26111         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26112             this.fireEvent('loadexception', this);
26113             return;
26114         }
26115         
26116         if(!this.mapTypeId){
26117             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26118         }
26119         
26120         this.gMapContext = this.GMapContext();
26121         
26122         this.initOverlayView();
26123         
26124         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26125         
26126         var _this = this;
26127                 
26128         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26129             _this.setPosition(_this.gMapContext.marker.position);
26130         });
26131         
26132         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26133             _this.fireEvent('mapClick', this, event);
26134             
26135         });
26136
26137         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26138             _this.fireEvent('mapRightClick', this, event);
26139             
26140         });
26141         
26142         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26143             _this.fireEvent('markerClick', this, event);
26144             
26145         });
26146
26147         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26148             _this.fireEvent('markerRightClick', this, event);
26149             
26150         });
26151         
26152         this.setPosition(this.gMapContext.location);
26153         
26154         this.fireEvent('initial', this, this.gMapContext.location);
26155     },
26156     
26157     initOverlayView: function()
26158     {
26159         var _this = this;
26160         
26161         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26162             
26163             draw: function()
26164             {
26165                 _this.fireEvent('OverlayViewDraw', _this);
26166             },
26167             
26168             onAdd: function()
26169             {
26170                 _this.fireEvent('OverlayViewOnAdd', _this);
26171             },
26172             
26173             onRemove: function()
26174             {
26175                 _this.fireEvent('OverlayViewOnRemove', _this);
26176             },
26177             
26178             show: function(cpx)
26179             {
26180                 _this.fireEvent('OverlayViewShow', _this, cpx);
26181             },
26182             
26183             hide: function()
26184             {
26185                 _this.fireEvent('OverlayViewHide', _this);
26186             }
26187             
26188         });
26189     },
26190     
26191     fromLatLngToContainerPixel: function(event)
26192     {
26193         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26194     },
26195     
26196     isApplied: function() 
26197     {
26198         return this.getGmapContext() == false ? false : true;
26199     },
26200     
26201     getGmapContext: function() 
26202     {
26203         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26204     },
26205     
26206     GMapContext: function() 
26207     {
26208         var position = new google.maps.LatLng(this.latitude, this.longitude);
26209         
26210         var _map = new google.maps.Map(this.el.dom, {
26211             center: position,
26212             zoom: this.zoom,
26213             mapTypeId: this.mapTypeId,
26214             mapTypeControl: this.mapTypeControl,
26215             disableDoubleClickZoom: this.disableDoubleClickZoom,
26216             scrollwheel: this.scrollwheel,
26217             streetViewControl: this.streetViewControl,
26218             locationName: this.locationName,
26219             draggable: this.draggable,
26220             enableAutocomplete: this.enableAutocomplete,
26221             enableReverseGeocode: this.enableReverseGeocode
26222         });
26223         
26224         var _marker = new google.maps.Marker({
26225             position: position,
26226             map: _map,
26227             title: this.markerTitle,
26228             draggable: this.draggable
26229         });
26230         
26231         return {
26232             map: _map,
26233             marker: _marker,
26234             circle: null,
26235             location: position,
26236             radius: this.radius,
26237             locationName: this.locationName,
26238             addressComponents: {
26239                 formatted_address: null,
26240                 addressLine1: null,
26241                 addressLine2: null,
26242                 streetName: null,
26243                 streetNumber: null,
26244                 city: null,
26245                 district: null,
26246                 state: null,
26247                 stateOrProvince: null
26248             },
26249             settings: this,
26250             domContainer: this.el.dom,
26251             geodecoder: new google.maps.Geocoder()
26252         };
26253     },
26254     
26255     drawCircle: function(center, radius, options) 
26256     {
26257         if (this.gMapContext.circle != null) {
26258             this.gMapContext.circle.setMap(null);
26259         }
26260         if (radius > 0) {
26261             radius *= 1;
26262             options = Roo.apply({}, options, {
26263                 strokeColor: "#0000FF",
26264                 strokeOpacity: .35,
26265                 strokeWeight: 2,
26266                 fillColor: "#0000FF",
26267                 fillOpacity: .2
26268             });
26269             
26270             options.map = this.gMapContext.map;
26271             options.radius = radius;
26272             options.center = center;
26273             this.gMapContext.circle = new google.maps.Circle(options);
26274             return this.gMapContext.circle;
26275         }
26276         
26277         return null;
26278     },
26279     
26280     setPosition: function(location) 
26281     {
26282         this.gMapContext.location = location;
26283         this.gMapContext.marker.setPosition(location);
26284         this.gMapContext.map.panTo(location);
26285         this.drawCircle(location, this.gMapContext.radius, {});
26286         
26287         var _this = this;
26288         
26289         if (this.gMapContext.settings.enableReverseGeocode) {
26290             this.gMapContext.geodecoder.geocode({
26291                 latLng: this.gMapContext.location
26292             }, function(results, status) {
26293                 
26294                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26295                     _this.gMapContext.locationName = results[0].formatted_address;
26296                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26297                     
26298                     _this.fireEvent('positionchanged', this, location);
26299                 }
26300             });
26301             
26302             return;
26303         }
26304         
26305         this.fireEvent('positionchanged', this, location);
26306     },
26307     
26308     resize: function()
26309     {
26310         google.maps.event.trigger(this.gMapContext.map, "resize");
26311         
26312         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26313         
26314         this.fireEvent('resize', this);
26315     },
26316     
26317     setPositionByLatLng: function(latitude, longitude)
26318     {
26319         this.setPosition(new google.maps.LatLng(latitude, longitude));
26320     },
26321     
26322     getCurrentPosition: function() 
26323     {
26324         return {
26325             latitude: this.gMapContext.location.lat(),
26326             longitude: this.gMapContext.location.lng()
26327         };
26328     },
26329     
26330     getAddressName: function() 
26331     {
26332         return this.gMapContext.locationName;
26333     },
26334     
26335     getAddressComponents: function() 
26336     {
26337         return this.gMapContext.addressComponents;
26338     },
26339     
26340     address_component_from_google_geocode: function(address_components) 
26341     {
26342         var result = {};
26343         
26344         for (var i = 0; i < address_components.length; i++) {
26345             var component = address_components[i];
26346             if (component.types.indexOf("postal_code") >= 0) {
26347                 result.postalCode = component.short_name;
26348             } else if (component.types.indexOf("street_number") >= 0) {
26349                 result.streetNumber = component.short_name;
26350             } else if (component.types.indexOf("route") >= 0) {
26351                 result.streetName = component.short_name;
26352             } else if (component.types.indexOf("neighborhood") >= 0) {
26353                 result.city = component.short_name;
26354             } else if (component.types.indexOf("locality") >= 0) {
26355                 result.city = component.short_name;
26356             } else if (component.types.indexOf("sublocality") >= 0) {
26357                 result.district = component.short_name;
26358             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26359                 result.stateOrProvince = component.short_name;
26360             } else if (component.types.indexOf("country") >= 0) {
26361                 result.country = component.short_name;
26362             }
26363         }
26364         
26365         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26366         result.addressLine2 = "";
26367         return result;
26368     },
26369     
26370     setZoomLevel: function(zoom)
26371     {
26372         this.gMapContext.map.setZoom(zoom);
26373     },
26374     
26375     show: function()
26376     {
26377         if(!this.el){
26378             return;
26379         }
26380         
26381         this.el.show();
26382         
26383         this.resize();
26384         
26385         this.fireEvent('show', this);
26386     },
26387     
26388     hide: function()
26389     {
26390         if(!this.el){
26391             return;
26392         }
26393         
26394         this.el.hide();
26395         
26396         this.fireEvent('hide', this);
26397     }
26398     
26399 });
26400
26401 Roo.apply(Roo.bootstrap.LocationPicker, {
26402     
26403     OverlayView : function(map, options)
26404     {
26405         options = options || {};
26406         
26407         this.setMap(map);
26408     }
26409     
26410     
26411 });/*
26412  * - LGPL
26413  *
26414  * Alert
26415  * 
26416  */
26417
26418 /**
26419  * @class Roo.bootstrap.Alert
26420  * @extends Roo.bootstrap.Component
26421  * Bootstrap Alert class
26422  * @cfg {String} title The title of alert
26423  * @cfg {String} html The content of alert
26424  * @cfg {String} weight (  success | info | warning | danger )
26425  * @cfg {String} faicon font-awesomeicon
26426  * 
26427  * @constructor
26428  * Create a new alert
26429  * @param {Object} config The config object
26430  */
26431
26432
26433 Roo.bootstrap.Alert = function(config){
26434     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26435     
26436 };
26437
26438 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26439     
26440     title: '',
26441     html: '',
26442     weight: false,
26443     faicon: false,
26444     
26445     getAutoCreate : function()
26446     {
26447         
26448         var cfg = {
26449             tag : 'div',
26450             cls : 'alert',
26451             cn : [
26452                 {
26453                     tag : 'i',
26454                     cls : 'roo-alert-icon'
26455                     
26456                 },
26457                 {
26458                     tag : 'b',
26459                     cls : 'roo-alert-title',
26460                     html : this.title
26461                 },
26462                 {
26463                     tag : 'span',
26464                     cls : 'roo-alert-text',
26465                     html : this.html
26466                 }
26467             ]
26468         };
26469         
26470         if(this.faicon){
26471             cfg.cn[0].cls += ' fa ' + this.faicon;
26472         }
26473         
26474         if(this.weight){
26475             cfg.cls += ' alert-' + this.weight;
26476         }
26477         
26478         return cfg;
26479     },
26480     
26481     initEvents: function() 
26482     {
26483         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26484     },
26485     
26486     setTitle : function(str)
26487     {
26488         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26489     },
26490     
26491     setText : function(str)
26492     {
26493         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26494     },
26495     
26496     setWeight : function(weight)
26497     {
26498         if(this.weight){
26499             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26500         }
26501         
26502         this.weight = weight;
26503         
26504         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26505     },
26506     
26507     setIcon : function(icon)
26508     {
26509         if(this.faicon){
26510             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26511         }
26512         
26513         this.faicon = icon;
26514         
26515         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26516     },
26517     
26518     hide: function() 
26519     {
26520         this.el.hide();   
26521     },
26522     
26523     show: function() 
26524     {  
26525         this.el.show();   
26526     }
26527     
26528 });
26529
26530  
26531 /*
26532 * Licence: LGPL
26533 */
26534
26535 /**
26536  * @class Roo.bootstrap.UploadCropbox
26537  * @extends Roo.bootstrap.Component
26538  * Bootstrap UploadCropbox class
26539  * @cfg {String} emptyText show when image has been loaded
26540  * @cfg {String} rotateNotify show when image too small to rotate
26541  * @cfg {Number} errorTimeout default 3000
26542  * @cfg {Number} minWidth default 300
26543  * @cfg {Number} minHeight default 300
26544  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26545  * @cfg {Boolean} isDocument (true|false) default false
26546  * @cfg {String} url action url
26547  * @cfg {String} paramName default 'imageUpload'
26548  * @cfg {String} method default POST
26549  * @cfg {Boolean} loadMask (true|false) default true
26550  * @cfg {Boolean} loadingText default 'Loading...'
26551  * 
26552  * @constructor
26553  * Create a new UploadCropbox
26554  * @param {Object} config The config object
26555  */
26556
26557 Roo.bootstrap.UploadCropbox = function(config){
26558     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26559     
26560     this.addEvents({
26561         /**
26562          * @event beforeselectfile
26563          * Fire before select file
26564          * @param {Roo.bootstrap.UploadCropbox} this
26565          */
26566         "beforeselectfile" : true,
26567         /**
26568          * @event initial
26569          * Fire after initEvent
26570          * @param {Roo.bootstrap.UploadCropbox} this
26571          */
26572         "initial" : true,
26573         /**
26574          * @event crop
26575          * Fire after initEvent
26576          * @param {Roo.bootstrap.UploadCropbox} this
26577          * @param {String} data
26578          */
26579         "crop" : true,
26580         /**
26581          * @event prepare
26582          * Fire when preparing the file data
26583          * @param {Roo.bootstrap.UploadCropbox} this
26584          * @param {Object} file
26585          */
26586         "prepare" : true,
26587         /**
26588          * @event exception
26589          * Fire when get exception
26590          * @param {Roo.bootstrap.UploadCropbox} this
26591          * @param {XMLHttpRequest} xhr
26592          */
26593         "exception" : true,
26594         /**
26595          * @event beforeloadcanvas
26596          * Fire before load the canvas
26597          * @param {Roo.bootstrap.UploadCropbox} this
26598          * @param {String} src
26599          */
26600         "beforeloadcanvas" : true,
26601         /**
26602          * @event trash
26603          * Fire when trash image
26604          * @param {Roo.bootstrap.UploadCropbox} this
26605          */
26606         "trash" : true,
26607         /**
26608          * @event download
26609          * Fire when download the image
26610          * @param {Roo.bootstrap.UploadCropbox} this
26611          */
26612         "download" : true,
26613         /**
26614          * @event footerbuttonclick
26615          * Fire when footerbuttonclick
26616          * @param {Roo.bootstrap.UploadCropbox} this
26617          * @param {String} type
26618          */
26619         "footerbuttonclick" : true,
26620         /**
26621          * @event resize
26622          * Fire when resize
26623          * @param {Roo.bootstrap.UploadCropbox} this
26624          */
26625         "resize" : true,
26626         /**
26627          * @event rotate
26628          * Fire when rotate the image
26629          * @param {Roo.bootstrap.UploadCropbox} this
26630          * @param {String} pos
26631          */
26632         "rotate" : true,
26633         /**
26634          * @event inspect
26635          * Fire when inspect the file
26636          * @param {Roo.bootstrap.UploadCropbox} this
26637          * @param {Object} file
26638          */
26639         "inspect" : true,
26640         /**
26641          * @event upload
26642          * Fire when xhr upload the file
26643          * @param {Roo.bootstrap.UploadCropbox} this
26644          * @param {Object} data
26645          */
26646         "upload" : true,
26647         /**
26648          * @event arrange
26649          * Fire when arrange the file data
26650          * @param {Roo.bootstrap.UploadCropbox} this
26651          * @param {Object} formData
26652          */
26653         "arrange" : true
26654     });
26655     
26656     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26657 };
26658
26659 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26660     
26661     emptyText : 'Click to upload image',
26662     rotateNotify : 'Image is too small to rotate',
26663     errorTimeout : 3000,
26664     scale : 0,
26665     baseScale : 1,
26666     rotate : 0,
26667     dragable : false,
26668     pinching : false,
26669     mouseX : 0,
26670     mouseY : 0,
26671     cropData : false,
26672     minWidth : 300,
26673     minHeight : 300,
26674     file : false,
26675     exif : {},
26676     baseRotate : 1,
26677     cropType : 'image/jpeg',
26678     buttons : false,
26679     canvasLoaded : false,
26680     isDocument : false,
26681     method : 'POST',
26682     paramName : 'imageUpload',
26683     loadMask : true,
26684     loadingText : 'Loading...',
26685     maskEl : false,
26686     
26687     getAutoCreate : function()
26688     {
26689         var cfg = {
26690             tag : 'div',
26691             cls : 'roo-upload-cropbox',
26692             cn : [
26693                 {
26694                     tag : 'input',
26695                     cls : 'roo-upload-cropbox-selector',
26696                     type : 'file'
26697                 },
26698                 {
26699                     tag : 'div',
26700                     cls : 'roo-upload-cropbox-body',
26701                     style : 'cursor:pointer',
26702                     cn : [
26703                         {
26704                             tag : 'div',
26705                             cls : 'roo-upload-cropbox-preview'
26706                         },
26707                         {
26708                             tag : 'div',
26709                             cls : 'roo-upload-cropbox-thumb'
26710                         },
26711                         {
26712                             tag : 'div',
26713                             cls : 'roo-upload-cropbox-empty-notify',
26714                             html : this.emptyText
26715                         },
26716                         {
26717                             tag : 'div',
26718                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26719                             html : this.rotateNotify
26720                         }
26721                     ]
26722                 },
26723                 {
26724                     tag : 'div',
26725                     cls : 'roo-upload-cropbox-footer',
26726                     cn : {
26727                         tag : 'div',
26728                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26729                         cn : []
26730                     }
26731                 }
26732             ]
26733         };
26734         
26735         return cfg;
26736     },
26737     
26738     onRender : function(ct, position)
26739     {
26740         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26741         
26742         if (this.buttons.length) {
26743             
26744             Roo.each(this.buttons, function(bb) {
26745                 
26746                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26747                 
26748                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26749                 
26750             }, this);
26751         }
26752         
26753         if(this.loadMask){
26754             this.maskEl = this.el;
26755         }
26756     },
26757     
26758     initEvents : function()
26759     {
26760         this.urlAPI = (window.createObjectURL && window) || 
26761                                 (window.URL && URL.revokeObjectURL && URL) || 
26762                                 (window.webkitURL && webkitURL);
26763                         
26764         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26765         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26766         
26767         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26768         this.selectorEl.hide();
26769         
26770         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26771         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26772         
26773         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26774         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26775         this.thumbEl.hide();
26776         
26777         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26778         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26779         
26780         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26781         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26782         this.errorEl.hide();
26783         
26784         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26785         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26786         this.footerEl.hide();
26787         
26788         this.setThumbBoxSize();
26789         
26790         this.bind();
26791         
26792         this.resize();
26793         
26794         this.fireEvent('initial', this);
26795     },
26796
26797     bind : function()
26798     {
26799         var _this = this;
26800         
26801         window.addEventListener("resize", function() { _this.resize(); } );
26802         
26803         this.bodyEl.on('click', this.beforeSelectFile, this);
26804         
26805         if(Roo.isTouch){
26806             this.bodyEl.on('touchstart', this.onTouchStart, this);
26807             this.bodyEl.on('touchmove', this.onTouchMove, this);
26808             this.bodyEl.on('touchend', this.onTouchEnd, this);
26809         }
26810         
26811         if(!Roo.isTouch){
26812             this.bodyEl.on('mousedown', this.onMouseDown, this);
26813             this.bodyEl.on('mousemove', this.onMouseMove, this);
26814             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26815             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26816             Roo.get(document).on('mouseup', this.onMouseUp, this);
26817         }
26818         
26819         this.selectorEl.on('change', this.onFileSelected, this);
26820     },
26821     
26822     reset : function()
26823     {    
26824         this.scale = 0;
26825         this.baseScale = 1;
26826         this.rotate = 0;
26827         this.baseRotate = 1;
26828         this.dragable = false;
26829         this.pinching = false;
26830         this.mouseX = 0;
26831         this.mouseY = 0;
26832         this.cropData = false;
26833         this.notifyEl.dom.innerHTML = this.emptyText;
26834         
26835         this.selectorEl.dom.value = '';
26836         
26837     },
26838     
26839     resize : function()
26840     {
26841         if(this.fireEvent('resize', this) != false){
26842             this.setThumbBoxPosition();
26843             this.setCanvasPosition();
26844         }
26845     },
26846     
26847     onFooterButtonClick : function(e, el, o, type)
26848     {
26849         switch (type) {
26850             case 'rotate-left' :
26851                 this.onRotateLeft(e);
26852                 break;
26853             case 'rotate-right' :
26854                 this.onRotateRight(e);
26855                 break;
26856             case 'picture' :
26857                 this.beforeSelectFile(e);
26858                 break;
26859             case 'trash' :
26860                 this.trash(e);
26861                 break;
26862             case 'crop' :
26863                 this.crop(e);
26864                 break;
26865             case 'download' :
26866                 this.download(e);
26867                 break;
26868             default :
26869                 break;
26870         }
26871         
26872         this.fireEvent('footerbuttonclick', this, type);
26873     },
26874     
26875     beforeSelectFile : function(e)
26876     {
26877         e.preventDefault();
26878         
26879         if(this.fireEvent('beforeselectfile', this) != false){
26880             this.selectorEl.dom.click();
26881         }
26882     },
26883     
26884     onFileSelected : function(e)
26885     {
26886         e.preventDefault();
26887         
26888         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26889             return;
26890         }
26891         
26892         var file = this.selectorEl.dom.files[0];
26893         
26894         if(this.fireEvent('inspect', this, file) != false){
26895             this.prepare(file);
26896         }
26897         
26898     },
26899     
26900     trash : function(e)
26901     {
26902         this.fireEvent('trash', this);
26903     },
26904     
26905     download : function(e)
26906     {
26907         this.fireEvent('download', this);
26908     },
26909     
26910     loadCanvas : function(src)
26911     {   
26912         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26913             
26914             this.reset();
26915             
26916             this.imageEl = document.createElement('img');
26917             
26918             var _this = this;
26919             
26920             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26921             
26922             this.imageEl.src = src;
26923         }
26924     },
26925     
26926     onLoadCanvas : function()
26927     {   
26928         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26929         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26930         
26931         this.bodyEl.un('click', this.beforeSelectFile, this);
26932         
26933         this.notifyEl.hide();
26934         this.thumbEl.show();
26935         this.footerEl.show();
26936         
26937         this.baseRotateLevel();
26938         
26939         if(this.isDocument){
26940             this.setThumbBoxSize();
26941         }
26942         
26943         this.setThumbBoxPosition();
26944         
26945         this.baseScaleLevel();
26946         
26947         this.draw();
26948         
26949         this.resize();
26950         
26951         this.canvasLoaded = true;
26952         
26953         if(this.loadMask){
26954             this.maskEl.unmask();
26955         }
26956         
26957     },
26958     
26959     setCanvasPosition : function()
26960     {   
26961         if(!this.canvasEl){
26962             return;
26963         }
26964         
26965         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26966         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26967         
26968         this.previewEl.setLeft(pw);
26969         this.previewEl.setTop(ph);
26970         
26971     },
26972     
26973     onMouseDown : function(e)
26974     {   
26975         e.stopEvent();
26976         
26977         this.dragable = true;
26978         this.pinching = false;
26979         
26980         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26981             this.dragable = false;
26982             return;
26983         }
26984         
26985         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26986         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26987         
26988     },
26989     
26990     onMouseMove : function(e)
26991     {   
26992         e.stopEvent();
26993         
26994         if(!this.canvasLoaded){
26995             return;
26996         }
26997         
26998         if (!this.dragable){
26999             return;
27000         }
27001         
27002         var minX = Math.ceil(this.thumbEl.getLeft(true));
27003         var minY = Math.ceil(this.thumbEl.getTop(true));
27004         
27005         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27006         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27007         
27008         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27009         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27010         
27011         x = x - this.mouseX;
27012         y = y - this.mouseY;
27013         
27014         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27015         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27016         
27017         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27018         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27019         
27020         this.previewEl.setLeft(bgX);
27021         this.previewEl.setTop(bgY);
27022         
27023         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27024         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27025     },
27026     
27027     onMouseUp : function(e)
27028     {   
27029         e.stopEvent();
27030         
27031         this.dragable = false;
27032     },
27033     
27034     onMouseWheel : function(e)
27035     {   
27036         e.stopEvent();
27037         
27038         this.startScale = this.scale;
27039         
27040         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27041         
27042         if(!this.zoomable()){
27043             this.scale = this.startScale;
27044             return;
27045         }
27046         
27047         this.draw();
27048         
27049         return;
27050     },
27051     
27052     zoomable : function()
27053     {
27054         var minScale = this.thumbEl.getWidth() / this.minWidth;
27055         
27056         if(this.minWidth < this.minHeight){
27057             minScale = this.thumbEl.getHeight() / this.minHeight;
27058         }
27059         
27060         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27061         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27062         
27063         if(
27064                 this.isDocument &&
27065                 (this.rotate == 0 || this.rotate == 180) && 
27066                 (
27067                     width > this.imageEl.OriginWidth || 
27068                     height > this.imageEl.OriginHeight ||
27069                     (width < this.minWidth && height < this.minHeight)
27070                 )
27071         ){
27072             return false;
27073         }
27074         
27075         if(
27076                 this.isDocument &&
27077                 (this.rotate == 90 || this.rotate == 270) && 
27078                 (
27079                     width > this.imageEl.OriginWidth || 
27080                     height > this.imageEl.OriginHeight ||
27081                     (width < this.minHeight && height < this.minWidth)
27082                 )
27083         ){
27084             return false;
27085         }
27086         
27087         if(
27088                 !this.isDocument &&
27089                 (this.rotate == 0 || this.rotate == 180) && 
27090                 (
27091                     width < this.minWidth || 
27092                     width > this.imageEl.OriginWidth || 
27093                     height < this.minHeight || 
27094                     height > this.imageEl.OriginHeight
27095                 )
27096         ){
27097             return false;
27098         }
27099         
27100         if(
27101                 !this.isDocument &&
27102                 (this.rotate == 90 || this.rotate == 270) && 
27103                 (
27104                     width < this.minHeight || 
27105                     width > this.imageEl.OriginWidth || 
27106                     height < this.minWidth || 
27107                     height > this.imageEl.OriginHeight
27108                 )
27109         ){
27110             return false;
27111         }
27112         
27113         return true;
27114         
27115     },
27116     
27117     onRotateLeft : function(e)
27118     {   
27119         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27120             
27121             var minScale = this.thumbEl.getWidth() / this.minWidth;
27122             
27123             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27124             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27125             
27126             this.startScale = this.scale;
27127             
27128             while (this.getScaleLevel() < minScale){
27129             
27130                 this.scale = this.scale + 1;
27131                 
27132                 if(!this.zoomable()){
27133                     break;
27134                 }
27135                 
27136                 if(
27137                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27138                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27139                 ){
27140                     continue;
27141                 }
27142                 
27143                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27144
27145                 this.draw();
27146                 
27147                 return;
27148             }
27149             
27150             this.scale = this.startScale;
27151             
27152             this.onRotateFail();
27153             
27154             return false;
27155         }
27156         
27157         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27158
27159         if(this.isDocument){
27160             this.setThumbBoxSize();
27161             this.setThumbBoxPosition();
27162             this.setCanvasPosition();
27163         }
27164         
27165         this.draw();
27166         
27167         this.fireEvent('rotate', this, 'left');
27168         
27169     },
27170     
27171     onRotateRight : function(e)
27172     {
27173         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27174             
27175             var minScale = this.thumbEl.getWidth() / this.minWidth;
27176         
27177             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27178             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27179             
27180             this.startScale = this.scale;
27181             
27182             while (this.getScaleLevel() < minScale){
27183             
27184                 this.scale = this.scale + 1;
27185                 
27186                 if(!this.zoomable()){
27187                     break;
27188                 }
27189                 
27190                 if(
27191                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27192                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27193                 ){
27194                     continue;
27195                 }
27196                 
27197                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27198
27199                 this.draw();
27200                 
27201                 return;
27202             }
27203             
27204             this.scale = this.startScale;
27205             
27206             this.onRotateFail();
27207             
27208             return false;
27209         }
27210         
27211         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27212
27213         if(this.isDocument){
27214             this.setThumbBoxSize();
27215             this.setThumbBoxPosition();
27216             this.setCanvasPosition();
27217         }
27218         
27219         this.draw();
27220         
27221         this.fireEvent('rotate', this, 'right');
27222     },
27223     
27224     onRotateFail : function()
27225     {
27226         this.errorEl.show(true);
27227         
27228         var _this = this;
27229         
27230         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27231     },
27232     
27233     draw : function()
27234     {
27235         this.previewEl.dom.innerHTML = '';
27236         
27237         var canvasEl = document.createElement("canvas");
27238         
27239         var contextEl = canvasEl.getContext("2d");
27240         
27241         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27242         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27243         var center = this.imageEl.OriginWidth / 2;
27244         
27245         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27246             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27247             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27248             center = this.imageEl.OriginHeight / 2;
27249         }
27250         
27251         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27252         
27253         contextEl.translate(center, center);
27254         contextEl.rotate(this.rotate * Math.PI / 180);
27255
27256         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27257         
27258         this.canvasEl = document.createElement("canvas");
27259         
27260         this.contextEl = this.canvasEl.getContext("2d");
27261         
27262         switch (this.rotate) {
27263             case 0 :
27264                 
27265                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27266                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27267                 
27268                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27269                 
27270                 break;
27271             case 90 : 
27272                 
27273                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27274                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27275                 
27276                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27277                     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);
27278                     break;
27279                 }
27280                 
27281                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27282                 
27283                 break;
27284             case 180 :
27285                 
27286                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27287                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27288                 
27289                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27290                     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);
27291                     break;
27292                 }
27293                 
27294                 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);
27295                 
27296                 break;
27297             case 270 :
27298                 
27299                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27300                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27301         
27302                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27303                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27304                     break;
27305                 }
27306                 
27307                 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);
27308                 
27309                 break;
27310             default : 
27311                 break;
27312         }
27313         
27314         this.previewEl.appendChild(this.canvasEl);
27315         
27316         this.setCanvasPosition();
27317     },
27318     
27319     crop : function()
27320     {
27321         if(!this.canvasLoaded){
27322             return;
27323         }
27324         
27325         var imageCanvas = document.createElement("canvas");
27326         
27327         var imageContext = imageCanvas.getContext("2d");
27328         
27329         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27330         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27331         
27332         var center = imageCanvas.width / 2;
27333         
27334         imageContext.translate(center, center);
27335         
27336         imageContext.rotate(this.rotate * Math.PI / 180);
27337         
27338         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27339         
27340         var canvas = document.createElement("canvas");
27341         
27342         var context = canvas.getContext("2d");
27343                 
27344         canvas.width = this.minWidth;
27345         canvas.height = this.minHeight;
27346
27347         switch (this.rotate) {
27348             case 0 :
27349                 
27350                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27351                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27352                 
27353                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27354                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27355                 
27356                 var targetWidth = this.minWidth - 2 * x;
27357                 var targetHeight = this.minHeight - 2 * y;
27358                 
27359                 var scale = 1;
27360                 
27361                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27362                     scale = targetWidth / width;
27363                 }
27364                 
27365                 if(x > 0 && y == 0){
27366                     scale = targetHeight / height;
27367                 }
27368                 
27369                 if(x > 0 && y > 0){
27370                     scale = targetWidth / width;
27371                     
27372                     if(width < height){
27373                         scale = targetHeight / height;
27374                     }
27375                 }
27376                 
27377                 context.scale(scale, scale);
27378                 
27379                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27380                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27381
27382                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27383                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27384
27385                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27386                 
27387                 break;
27388             case 90 : 
27389                 
27390                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27391                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27392                 
27393                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27394                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27395                 
27396                 var targetWidth = this.minWidth - 2 * x;
27397                 var targetHeight = this.minHeight - 2 * y;
27398                 
27399                 var scale = 1;
27400                 
27401                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27402                     scale = targetWidth / width;
27403                 }
27404                 
27405                 if(x > 0 && y == 0){
27406                     scale = targetHeight / height;
27407                 }
27408                 
27409                 if(x > 0 && y > 0){
27410                     scale = targetWidth / width;
27411                     
27412                     if(width < height){
27413                         scale = targetHeight / height;
27414                     }
27415                 }
27416                 
27417                 context.scale(scale, scale);
27418                 
27419                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27420                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27421
27422                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27423                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27424                 
27425                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27426                 
27427                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27428                 
27429                 break;
27430             case 180 :
27431                 
27432                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27433                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27434                 
27435                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27436                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27437                 
27438                 var targetWidth = this.minWidth - 2 * x;
27439                 var targetHeight = this.minHeight - 2 * y;
27440                 
27441                 var scale = 1;
27442                 
27443                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27444                     scale = targetWidth / width;
27445                 }
27446                 
27447                 if(x > 0 && y == 0){
27448                     scale = targetHeight / height;
27449                 }
27450                 
27451                 if(x > 0 && y > 0){
27452                     scale = targetWidth / width;
27453                     
27454                     if(width < height){
27455                         scale = targetHeight / height;
27456                     }
27457                 }
27458                 
27459                 context.scale(scale, scale);
27460                 
27461                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27462                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27463
27464                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27465                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27466
27467                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27468                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27469                 
27470                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27471                 
27472                 break;
27473             case 270 :
27474                 
27475                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27476                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27477                 
27478                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27479                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27480                 
27481                 var targetWidth = this.minWidth - 2 * x;
27482                 var targetHeight = this.minHeight - 2 * y;
27483                 
27484                 var scale = 1;
27485                 
27486                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27487                     scale = targetWidth / width;
27488                 }
27489                 
27490                 if(x > 0 && y == 0){
27491                     scale = targetHeight / height;
27492                 }
27493                 
27494                 if(x > 0 && y > 0){
27495                     scale = targetWidth / width;
27496                     
27497                     if(width < height){
27498                         scale = targetHeight / height;
27499                     }
27500                 }
27501                 
27502                 context.scale(scale, scale);
27503                 
27504                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27505                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27506
27507                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27508                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27509                 
27510                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27511                 
27512                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27513                 
27514                 break;
27515             default : 
27516                 break;
27517         }
27518         
27519         this.cropData = canvas.toDataURL(this.cropType);
27520         
27521         if(this.fireEvent('crop', this, this.cropData) !== false){
27522             this.process(this.file, this.cropData);
27523         }
27524         
27525         return;
27526         
27527     },
27528     
27529     setThumbBoxSize : function()
27530     {
27531         var width, height;
27532         
27533         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27534             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27535             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27536             
27537             this.minWidth = width;
27538             this.minHeight = height;
27539             
27540             if(this.rotate == 90 || this.rotate == 270){
27541                 this.minWidth = height;
27542                 this.minHeight = width;
27543             }
27544         }
27545         
27546         height = 300;
27547         width = Math.ceil(this.minWidth * height / this.minHeight);
27548         
27549         if(this.minWidth > this.minHeight){
27550             width = 300;
27551             height = Math.ceil(this.minHeight * width / this.minWidth);
27552         }
27553         
27554         this.thumbEl.setStyle({
27555             width : width + 'px',
27556             height : height + 'px'
27557         });
27558
27559         return;
27560             
27561     },
27562     
27563     setThumbBoxPosition : function()
27564     {
27565         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27566         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27567         
27568         this.thumbEl.setLeft(x);
27569         this.thumbEl.setTop(y);
27570         
27571     },
27572     
27573     baseRotateLevel : function()
27574     {
27575         this.baseRotate = 1;
27576         
27577         if(
27578                 typeof(this.exif) != 'undefined' &&
27579                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27580                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27581         ){
27582             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27583         }
27584         
27585         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27586         
27587     },
27588     
27589     baseScaleLevel : function()
27590     {
27591         var width, height;
27592         
27593         if(this.isDocument){
27594             
27595             if(this.baseRotate == 6 || this.baseRotate == 8){
27596             
27597                 height = this.thumbEl.getHeight();
27598                 this.baseScale = height / this.imageEl.OriginWidth;
27599
27600                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27601                     width = this.thumbEl.getWidth();
27602                     this.baseScale = width / this.imageEl.OriginHeight;
27603                 }
27604
27605                 return;
27606             }
27607
27608             height = this.thumbEl.getHeight();
27609             this.baseScale = height / this.imageEl.OriginHeight;
27610
27611             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27612                 width = this.thumbEl.getWidth();
27613                 this.baseScale = width / this.imageEl.OriginWidth;
27614             }
27615
27616             return;
27617         }
27618         
27619         if(this.baseRotate == 6 || this.baseRotate == 8){
27620             
27621             width = this.thumbEl.getHeight();
27622             this.baseScale = width / this.imageEl.OriginHeight;
27623             
27624             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27625                 height = this.thumbEl.getWidth();
27626                 this.baseScale = height / this.imageEl.OriginHeight;
27627             }
27628             
27629             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27630                 height = this.thumbEl.getWidth();
27631                 this.baseScale = height / this.imageEl.OriginHeight;
27632                 
27633                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27634                     width = this.thumbEl.getHeight();
27635                     this.baseScale = width / this.imageEl.OriginWidth;
27636                 }
27637             }
27638             
27639             return;
27640         }
27641         
27642         width = this.thumbEl.getWidth();
27643         this.baseScale = width / this.imageEl.OriginWidth;
27644         
27645         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27646             height = this.thumbEl.getHeight();
27647             this.baseScale = height / this.imageEl.OriginHeight;
27648         }
27649         
27650         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27651             
27652             height = this.thumbEl.getHeight();
27653             this.baseScale = height / this.imageEl.OriginHeight;
27654             
27655             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27656                 width = this.thumbEl.getWidth();
27657                 this.baseScale = width / this.imageEl.OriginWidth;
27658             }
27659             
27660         }
27661         
27662         return;
27663     },
27664     
27665     getScaleLevel : function()
27666     {
27667         return this.baseScale * Math.pow(1.1, this.scale);
27668     },
27669     
27670     onTouchStart : function(e)
27671     {
27672         if(!this.canvasLoaded){
27673             this.beforeSelectFile(e);
27674             return;
27675         }
27676         
27677         var touches = e.browserEvent.touches;
27678         
27679         if(!touches){
27680             return;
27681         }
27682         
27683         if(touches.length == 1){
27684             this.onMouseDown(e);
27685             return;
27686         }
27687         
27688         if(touches.length != 2){
27689             return;
27690         }
27691         
27692         var coords = [];
27693         
27694         for(var i = 0, finger; finger = touches[i]; i++){
27695             coords.push(finger.pageX, finger.pageY);
27696         }
27697         
27698         var x = Math.pow(coords[0] - coords[2], 2);
27699         var y = Math.pow(coords[1] - coords[3], 2);
27700         
27701         this.startDistance = Math.sqrt(x + y);
27702         
27703         this.startScale = this.scale;
27704         
27705         this.pinching = true;
27706         this.dragable = false;
27707         
27708     },
27709     
27710     onTouchMove : function(e)
27711     {
27712         if(!this.pinching && !this.dragable){
27713             return;
27714         }
27715         
27716         var touches = e.browserEvent.touches;
27717         
27718         if(!touches){
27719             return;
27720         }
27721         
27722         if(this.dragable){
27723             this.onMouseMove(e);
27724             return;
27725         }
27726         
27727         var coords = [];
27728         
27729         for(var i = 0, finger; finger = touches[i]; i++){
27730             coords.push(finger.pageX, finger.pageY);
27731         }
27732         
27733         var x = Math.pow(coords[0] - coords[2], 2);
27734         var y = Math.pow(coords[1] - coords[3], 2);
27735         
27736         this.endDistance = Math.sqrt(x + y);
27737         
27738         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27739         
27740         if(!this.zoomable()){
27741             this.scale = this.startScale;
27742             return;
27743         }
27744         
27745         this.draw();
27746         
27747     },
27748     
27749     onTouchEnd : function(e)
27750     {
27751         this.pinching = false;
27752         this.dragable = false;
27753         
27754     },
27755     
27756     process : function(file, crop)
27757     {
27758         if(this.loadMask){
27759             this.maskEl.mask(this.loadingText);
27760         }
27761         
27762         this.xhr = new XMLHttpRequest();
27763         
27764         file.xhr = this.xhr;
27765
27766         this.xhr.open(this.method, this.url, true);
27767         
27768         var headers = {
27769             "Accept": "application/json",
27770             "Cache-Control": "no-cache",
27771             "X-Requested-With": "XMLHttpRequest"
27772         };
27773         
27774         for (var headerName in headers) {
27775             var headerValue = headers[headerName];
27776             if (headerValue) {
27777                 this.xhr.setRequestHeader(headerName, headerValue);
27778             }
27779         }
27780         
27781         var _this = this;
27782         
27783         this.xhr.onload = function()
27784         {
27785             _this.xhrOnLoad(_this.xhr);
27786         }
27787         
27788         this.xhr.onerror = function()
27789         {
27790             _this.xhrOnError(_this.xhr);
27791         }
27792         
27793         var formData = new FormData();
27794
27795         formData.append('returnHTML', 'NO');
27796         
27797         if(crop){
27798             formData.append('crop', crop);
27799         }
27800         
27801         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27802             formData.append(this.paramName, file, file.name);
27803         }
27804         
27805         if(typeof(file.filename) != 'undefined'){
27806             formData.append('filename', file.filename);
27807         }
27808         
27809         if(typeof(file.mimetype) != 'undefined'){
27810             formData.append('mimetype', file.mimetype);
27811         }
27812         
27813         if(this.fireEvent('arrange', this, formData) != false){
27814             this.xhr.send(formData);
27815         };
27816     },
27817     
27818     xhrOnLoad : function(xhr)
27819     {
27820         if(this.loadMask){
27821             this.maskEl.unmask();
27822         }
27823         
27824         if (xhr.readyState !== 4) {
27825             this.fireEvent('exception', this, xhr);
27826             return;
27827         }
27828
27829         var response = Roo.decode(xhr.responseText);
27830         
27831         if(!response.success){
27832             this.fireEvent('exception', this, xhr);
27833             return;
27834         }
27835         
27836         var response = Roo.decode(xhr.responseText);
27837         
27838         this.fireEvent('upload', this, response);
27839         
27840     },
27841     
27842     xhrOnError : function()
27843     {
27844         if(this.loadMask){
27845             this.maskEl.unmask();
27846         }
27847         
27848         Roo.log('xhr on error');
27849         
27850         var response = Roo.decode(xhr.responseText);
27851           
27852         Roo.log(response);
27853         
27854     },
27855     
27856     prepare : function(file)
27857     {   
27858         if(this.loadMask){
27859             this.maskEl.mask(this.loadingText);
27860         }
27861         
27862         this.file = false;
27863         this.exif = {};
27864         
27865         if(typeof(file) === 'string'){
27866             this.loadCanvas(file);
27867             return;
27868         }
27869         
27870         if(!file || !this.urlAPI){
27871             return;
27872         }
27873         
27874         this.file = file;
27875         this.cropType = file.type;
27876         
27877         var _this = this;
27878         
27879         if(this.fireEvent('prepare', this, this.file) != false){
27880             
27881             var reader = new FileReader();
27882             
27883             reader.onload = function (e) {
27884                 if (e.target.error) {
27885                     Roo.log(e.target.error);
27886                     return;
27887                 }
27888                 
27889                 var buffer = e.target.result,
27890                     dataView = new DataView(buffer),
27891                     offset = 2,
27892                     maxOffset = dataView.byteLength - 4,
27893                     markerBytes,
27894                     markerLength;
27895                 
27896                 if (dataView.getUint16(0) === 0xffd8) {
27897                     while (offset < maxOffset) {
27898                         markerBytes = dataView.getUint16(offset);
27899                         
27900                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27901                             markerLength = dataView.getUint16(offset + 2) + 2;
27902                             if (offset + markerLength > dataView.byteLength) {
27903                                 Roo.log('Invalid meta data: Invalid segment size.');
27904                                 break;
27905                             }
27906                             
27907                             if(markerBytes == 0xffe1){
27908                                 _this.parseExifData(
27909                                     dataView,
27910                                     offset,
27911                                     markerLength
27912                                 );
27913                             }
27914                             
27915                             offset += markerLength;
27916                             
27917                             continue;
27918                         }
27919                         
27920                         break;
27921                     }
27922                     
27923                 }
27924                 
27925                 var url = _this.urlAPI.createObjectURL(_this.file);
27926                 
27927                 _this.loadCanvas(url);
27928                 
27929                 return;
27930             }
27931             
27932             reader.readAsArrayBuffer(this.file);
27933             
27934         }
27935         
27936     },
27937     
27938     parseExifData : function(dataView, offset, length)
27939     {
27940         var tiffOffset = offset + 10,
27941             littleEndian,
27942             dirOffset;
27943     
27944         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27945             // No Exif data, might be XMP data instead
27946             return;
27947         }
27948         
27949         // Check for the ASCII code for "Exif" (0x45786966):
27950         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27951             // No Exif data, might be XMP data instead
27952             return;
27953         }
27954         if (tiffOffset + 8 > dataView.byteLength) {
27955             Roo.log('Invalid Exif data: Invalid segment size.');
27956             return;
27957         }
27958         // Check for the two null bytes:
27959         if (dataView.getUint16(offset + 8) !== 0x0000) {
27960             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27961             return;
27962         }
27963         // Check the byte alignment:
27964         switch (dataView.getUint16(tiffOffset)) {
27965         case 0x4949:
27966             littleEndian = true;
27967             break;
27968         case 0x4D4D:
27969             littleEndian = false;
27970             break;
27971         default:
27972             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27973             return;
27974         }
27975         // Check for the TIFF tag marker (0x002A):
27976         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27977             Roo.log('Invalid Exif data: Missing TIFF marker.');
27978             return;
27979         }
27980         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27981         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27982         
27983         this.parseExifTags(
27984             dataView,
27985             tiffOffset,
27986             tiffOffset + dirOffset,
27987             littleEndian
27988         );
27989     },
27990     
27991     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27992     {
27993         var tagsNumber,
27994             dirEndOffset,
27995             i;
27996         if (dirOffset + 6 > dataView.byteLength) {
27997             Roo.log('Invalid Exif data: Invalid directory offset.');
27998             return;
27999         }
28000         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28001         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28002         if (dirEndOffset + 4 > dataView.byteLength) {
28003             Roo.log('Invalid Exif data: Invalid directory size.');
28004             return;
28005         }
28006         for (i = 0; i < tagsNumber; i += 1) {
28007             this.parseExifTag(
28008                 dataView,
28009                 tiffOffset,
28010                 dirOffset + 2 + 12 * i, // tag offset
28011                 littleEndian
28012             );
28013         }
28014         // Return the offset to the next directory:
28015         return dataView.getUint32(dirEndOffset, littleEndian);
28016     },
28017     
28018     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28019     {
28020         var tag = dataView.getUint16(offset, littleEndian);
28021         
28022         this.exif[tag] = this.getExifValue(
28023             dataView,
28024             tiffOffset,
28025             offset,
28026             dataView.getUint16(offset + 2, littleEndian), // tag type
28027             dataView.getUint32(offset + 4, littleEndian), // tag length
28028             littleEndian
28029         );
28030     },
28031     
28032     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28033     {
28034         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28035             tagSize,
28036             dataOffset,
28037             values,
28038             i,
28039             str,
28040             c;
28041     
28042         if (!tagType) {
28043             Roo.log('Invalid Exif data: Invalid tag type.');
28044             return;
28045         }
28046         
28047         tagSize = tagType.size * length;
28048         // Determine if the value is contained in the dataOffset bytes,
28049         // or if the value at the dataOffset is a pointer to the actual data:
28050         dataOffset = tagSize > 4 ?
28051                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28052         if (dataOffset + tagSize > dataView.byteLength) {
28053             Roo.log('Invalid Exif data: Invalid data offset.');
28054             return;
28055         }
28056         if (length === 1) {
28057             return tagType.getValue(dataView, dataOffset, littleEndian);
28058         }
28059         values = [];
28060         for (i = 0; i < length; i += 1) {
28061             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28062         }
28063         
28064         if (tagType.ascii) {
28065             str = '';
28066             // Concatenate the chars:
28067             for (i = 0; i < values.length; i += 1) {
28068                 c = values[i];
28069                 // Ignore the terminating NULL byte(s):
28070                 if (c === '\u0000') {
28071                     break;
28072                 }
28073                 str += c;
28074             }
28075             return str;
28076         }
28077         return values;
28078     }
28079     
28080 });
28081
28082 Roo.apply(Roo.bootstrap.UploadCropbox, {
28083     tags : {
28084         'Orientation': 0x0112
28085     },
28086     
28087     Orientation: {
28088             1: 0, //'top-left',
28089 //            2: 'top-right',
28090             3: 180, //'bottom-right',
28091 //            4: 'bottom-left',
28092 //            5: 'left-top',
28093             6: 90, //'right-top',
28094 //            7: 'right-bottom',
28095             8: 270 //'left-bottom'
28096     },
28097     
28098     exifTagTypes : {
28099         // byte, 8-bit unsigned int:
28100         1: {
28101             getValue: function (dataView, dataOffset) {
28102                 return dataView.getUint8(dataOffset);
28103             },
28104             size: 1
28105         },
28106         // ascii, 8-bit byte:
28107         2: {
28108             getValue: function (dataView, dataOffset) {
28109                 return String.fromCharCode(dataView.getUint8(dataOffset));
28110             },
28111             size: 1,
28112             ascii: true
28113         },
28114         // short, 16 bit int:
28115         3: {
28116             getValue: function (dataView, dataOffset, littleEndian) {
28117                 return dataView.getUint16(dataOffset, littleEndian);
28118             },
28119             size: 2
28120         },
28121         // long, 32 bit int:
28122         4: {
28123             getValue: function (dataView, dataOffset, littleEndian) {
28124                 return dataView.getUint32(dataOffset, littleEndian);
28125             },
28126             size: 4
28127         },
28128         // rational = two long values, first is numerator, second is denominator:
28129         5: {
28130             getValue: function (dataView, dataOffset, littleEndian) {
28131                 return dataView.getUint32(dataOffset, littleEndian) /
28132                     dataView.getUint32(dataOffset + 4, littleEndian);
28133             },
28134             size: 8
28135         },
28136         // slong, 32 bit signed int:
28137         9: {
28138             getValue: function (dataView, dataOffset, littleEndian) {
28139                 return dataView.getInt32(dataOffset, littleEndian);
28140             },
28141             size: 4
28142         },
28143         // srational, two slongs, first is numerator, second is denominator:
28144         10: {
28145             getValue: function (dataView, dataOffset, littleEndian) {
28146                 return dataView.getInt32(dataOffset, littleEndian) /
28147                     dataView.getInt32(dataOffset + 4, littleEndian);
28148             },
28149             size: 8
28150         }
28151     },
28152     
28153     footer : {
28154         STANDARD : [
28155             {
28156                 tag : 'div',
28157                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28158                 action : 'rotate-left',
28159                 cn : [
28160                     {
28161                         tag : 'button',
28162                         cls : 'btn btn-default',
28163                         html : '<i class="fa fa-undo"></i>'
28164                     }
28165                 ]
28166             },
28167             {
28168                 tag : 'div',
28169                 cls : 'btn-group roo-upload-cropbox-picture',
28170                 action : 'picture',
28171                 cn : [
28172                     {
28173                         tag : 'button',
28174                         cls : 'btn btn-default',
28175                         html : '<i class="fa fa-picture-o"></i>'
28176                     }
28177                 ]
28178             },
28179             {
28180                 tag : 'div',
28181                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28182                 action : 'rotate-right',
28183                 cn : [
28184                     {
28185                         tag : 'button',
28186                         cls : 'btn btn-default',
28187                         html : '<i class="fa fa-repeat"></i>'
28188                     }
28189                 ]
28190             }
28191         ],
28192         DOCUMENT : [
28193             {
28194                 tag : 'div',
28195                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28196                 action : 'rotate-left',
28197                 cn : [
28198                     {
28199                         tag : 'button',
28200                         cls : 'btn btn-default',
28201                         html : '<i class="fa fa-undo"></i>'
28202                     }
28203                 ]
28204             },
28205             {
28206                 tag : 'div',
28207                 cls : 'btn-group roo-upload-cropbox-download',
28208                 action : 'download',
28209                 cn : [
28210                     {
28211                         tag : 'button',
28212                         cls : 'btn btn-default',
28213                         html : '<i class="fa fa-download"></i>'
28214                     }
28215                 ]
28216             },
28217             {
28218                 tag : 'div',
28219                 cls : 'btn-group roo-upload-cropbox-crop',
28220                 action : 'crop',
28221                 cn : [
28222                     {
28223                         tag : 'button',
28224                         cls : 'btn btn-default',
28225                         html : '<i class="fa fa-crop"></i>'
28226                     }
28227                 ]
28228             },
28229             {
28230                 tag : 'div',
28231                 cls : 'btn-group roo-upload-cropbox-trash',
28232                 action : 'trash',
28233                 cn : [
28234                     {
28235                         tag : 'button',
28236                         cls : 'btn btn-default',
28237                         html : '<i class="fa fa-trash"></i>'
28238                     }
28239                 ]
28240             },
28241             {
28242                 tag : 'div',
28243                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28244                 action : 'rotate-right',
28245                 cn : [
28246                     {
28247                         tag : 'button',
28248                         cls : 'btn btn-default',
28249                         html : '<i class="fa fa-repeat"></i>'
28250                     }
28251                 ]
28252             }
28253         ],
28254         ROTATOR : [
28255             {
28256                 tag : 'div',
28257                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28258                 action : 'rotate-left',
28259                 cn : [
28260                     {
28261                         tag : 'button',
28262                         cls : 'btn btn-default',
28263                         html : '<i class="fa fa-undo"></i>'
28264                     }
28265                 ]
28266             },
28267             {
28268                 tag : 'div',
28269                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28270                 action : 'rotate-right',
28271                 cn : [
28272                     {
28273                         tag : 'button',
28274                         cls : 'btn btn-default',
28275                         html : '<i class="fa fa-repeat"></i>'
28276                     }
28277                 ]
28278             }
28279         ]
28280     }
28281 });
28282
28283 /*
28284 * Licence: LGPL
28285 */
28286
28287 /**
28288  * @class Roo.bootstrap.DocumentManager
28289  * @extends Roo.bootstrap.Component
28290  * Bootstrap DocumentManager class
28291  * @cfg {String} paramName default 'imageUpload'
28292  * @cfg {String} toolTipName default 'filename'
28293  * @cfg {String} method default POST
28294  * @cfg {String} url action url
28295  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28296  * @cfg {Boolean} multiple multiple upload default true
28297  * @cfg {Number} thumbSize default 300
28298  * @cfg {String} fieldLabel
28299  * @cfg {Number} labelWidth default 4
28300  * @cfg {String} labelAlign (left|top) default left
28301  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28302 * @cfg {Number} labellg set the width of label (1-12)
28303  * @cfg {Number} labelmd set the width of label (1-12)
28304  * @cfg {Number} labelsm set the width of label (1-12)
28305  * @cfg {Number} labelxs set the width of label (1-12)
28306  * 
28307  * @constructor
28308  * Create a new DocumentManager
28309  * @param {Object} config The config object
28310  */
28311
28312 Roo.bootstrap.DocumentManager = function(config){
28313     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28314     
28315     this.files = [];
28316     this.delegates = [];
28317     
28318     this.addEvents({
28319         /**
28320          * @event initial
28321          * Fire when initial the DocumentManager
28322          * @param {Roo.bootstrap.DocumentManager} this
28323          */
28324         "initial" : true,
28325         /**
28326          * @event inspect
28327          * inspect selected file
28328          * @param {Roo.bootstrap.DocumentManager} this
28329          * @param {File} file
28330          */
28331         "inspect" : true,
28332         /**
28333          * @event exception
28334          * Fire when xhr load exception
28335          * @param {Roo.bootstrap.DocumentManager} this
28336          * @param {XMLHttpRequest} xhr
28337          */
28338         "exception" : true,
28339         /**
28340          * @event afterupload
28341          * Fire when xhr load exception
28342          * @param {Roo.bootstrap.DocumentManager} this
28343          * @param {XMLHttpRequest} xhr
28344          */
28345         "afterupload" : true,
28346         /**
28347          * @event prepare
28348          * prepare the form data
28349          * @param {Roo.bootstrap.DocumentManager} this
28350          * @param {Object} formData
28351          */
28352         "prepare" : true,
28353         /**
28354          * @event remove
28355          * Fire when remove the file
28356          * @param {Roo.bootstrap.DocumentManager} this
28357          * @param {Object} file
28358          */
28359         "remove" : true,
28360         /**
28361          * @event refresh
28362          * Fire after refresh the file
28363          * @param {Roo.bootstrap.DocumentManager} this
28364          */
28365         "refresh" : true,
28366         /**
28367          * @event click
28368          * Fire after click the image
28369          * @param {Roo.bootstrap.DocumentManager} this
28370          * @param {Object} file
28371          */
28372         "click" : true,
28373         /**
28374          * @event edit
28375          * Fire when upload a image and editable set to true
28376          * @param {Roo.bootstrap.DocumentManager} this
28377          * @param {Object} file
28378          */
28379         "edit" : true,
28380         /**
28381          * @event beforeselectfile
28382          * Fire before select file
28383          * @param {Roo.bootstrap.DocumentManager} this
28384          */
28385         "beforeselectfile" : true,
28386         /**
28387          * @event process
28388          * Fire before process file
28389          * @param {Roo.bootstrap.DocumentManager} this
28390          * @param {Object} file
28391          */
28392         "process" : true
28393         
28394     });
28395 };
28396
28397 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28398     
28399     boxes : 0,
28400     inputName : '',
28401     thumbSize : 300,
28402     multiple : true,
28403     files : false,
28404     method : 'POST',
28405     url : '',
28406     paramName : 'imageUpload',
28407     toolTipName : 'filename',
28408     fieldLabel : '',
28409     labelWidth : 4,
28410     labelAlign : 'left',
28411     editable : true,
28412     delegates : false,
28413     xhr : false, 
28414     
28415     labellg : 0,
28416     labelmd : 0,
28417     labelsm : 0,
28418     labelxs : 0,
28419     
28420     getAutoCreate : function()
28421     {   
28422         var managerWidget = {
28423             tag : 'div',
28424             cls : 'roo-document-manager',
28425             cn : [
28426                 {
28427                     tag : 'input',
28428                     cls : 'roo-document-manager-selector',
28429                     type : 'file'
28430                 },
28431                 {
28432                     tag : 'div',
28433                     cls : 'roo-document-manager-uploader',
28434                     cn : [
28435                         {
28436                             tag : 'div',
28437                             cls : 'roo-document-manager-upload-btn',
28438                             html : '<i class="fa fa-plus"></i>'
28439                         }
28440                     ]
28441                     
28442                 }
28443             ]
28444         };
28445         
28446         var content = [
28447             {
28448                 tag : 'div',
28449                 cls : 'column col-md-12',
28450                 cn : managerWidget
28451             }
28452         ];
28453         
28454         if(this.fieldLabel.length){
28455             
28456             content = [
28457                 {
28458                     tag : 'div',
28459                     cls : 'column col-md-12',
28460                     html : this.fieldLabel
28461                 },
28462                 {
28463                     tag : 'div',
28464                     cls : 'column col-md-12',
28465                     cn : managerWidget
28466                 }
28467             ];
28468
28469             if(this.labelAlign == 'left'){
28470                 content = [
28471                     {
28472                         tag : 'div',
28473                         cls : 'column',
28474                         html : this.fieldLabel
28475                     },
28476                     {
28477                         tag : 'div',
28478                         cls : 'column',
28479                         cn : managerWidget
28480                     }
28481                 ];
28482                 
28483                 if(this.labelWidth > 12){
28484                     content[0].style = "width: " + this.labelWidth + 'px';
28485                 }
28486
28487                 if(this.labelWidth < 13 && this.labelmd == 0){
28488                     this.labelmd = this.labelWidth;
28489                 }
28490
28491                 if(this.labellg > 0){
28492                     content[0].cls += ' col-lg-' + this.labellg;
28493                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28494                 }
28495
28496                 if(this.labelmd > 0){
28497                     content[0].cls += ' col-md-' + this.labelmd;
28498                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28499                 }
28500
28501                 if(this.labelsm > 0){
28502                     content[0].cls += ' col-sm-' + this.labelsm;
28503                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28504                 }
28505
28506                 if(this.labelxs > 0){
28507                     content[0].cls += ' col-xs-' + this.labelxs;
28508                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28509                 }
28510                 
28511             }
28512         }
28513         
28514         var cfg = {
28515             tag : 'div',
28516             cls : 'row clearfix',
28517             cn : content
28518         };
28519         
28520         return cfg;
28521         
28522     },
28523     
28524     initEvents : function()
28525     {
28526         this.managerEl = this.el.select('.roo-document-manager', true).first();
28527         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28528         
28529         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28530         this.selectorEl.hide();
28531         
28532         if(this.multiple){
28533             this.selectorEl.attr('multiple', 'multiple');
28534         }
28535         
28536         this.selectorEl.on('change', this.onFileSelected, this);
28537         
28538         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28539         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28540         
28541         this.uploader.on('click', this.onUploaderClick, this);
28542         
28543         this.renderProgressDialog();
28544         
28545         var _this = this;
28546         
28547         window.addEventListener("resize", function() { _this.refresh(); } );
28548         
28549         this.fireEvent('initial', this);
28550     },
28551     
28552     renderProgressDialog : function()
28553     {
28554         var _this = this;
28555         
28556         this.progressDialog = new Roo.bootstrap.Modal({
28557             cls : 'roo-document-manager-progress-dialog',
28558             allow_close : false,
28559             title : '',
28560             buttons : [
28561                 {
28562                     name  :'cancel',
28563                     weight : 'danger',
28564                     html : 'Cancel'
28565                 }
28566             ], 
28567             listeners : { 
28568                 btnclick : function() {
28569                     _this.uploadCancel();
28570                     this.hide();
28571                 }
28572             }
28573         });
28574          
28575         this.progressDialog.render(Roo.get(document.body));
28576          
28577         this.progress = new Roo.bootstrap.Progress({
28578             cls : 'roo-document-manager-progress',
28579             active : true,
28580             striped : true
28581         });
28582         
28583         this.progress.render(this.progressDialog.getChildContainer());
28584         
28585         this.progressBar = new Roo.bootstrap.ProgressBar({
28586             cls : 'roo-document-manager-progress-bar',
28587             aria_valuenow : 0,
28588             aria_valuemin : 0,
28589             aria_valuemax : 12,
28590             panel : 'success'
28591         });
28592         
28593         this.progressBar.render(this.progress.getChildContainer());
28594     },
28595     
28596     onUploaderClick : function(e)
28597     {
28598         e.preventDefault();
28599      
28600         if(this.fireEvent('beforeselectfile', this) != false){
28601             this.selectorEl.dom.click();
28602         }
28603         
28604     },
28605     
28606     onFileSelected : function(e)
28607     {
28608         e.preventDefault();
28609         
28610         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28611             return;
28612         }
28613         
28614         Roo.each(this.selectorEl.dom.files, function(file){
28615             if(this.fireEvent('inspect', this, file) != false){
28616                 this.files.push(file);
28617             }
28618         }, this);
28619         
28620         this.queue();
28621         
28622     },
28623     
28624     queue : function()
28625     {
28626         this.selectorEl.dom.value = '';
28627         
28628         if(!this.files.length){
28629             return;
28630         }
28631         
28632         if(this.boxes > 0 && this.files.length > this.boxes){
28633             this.files = this.files.slice(0, this.boxes);
28634         }
28635         
28636         this.uploader.show();
28637         
28638         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28639             this.uploader.hide();
28640         }
28641         
28642         var _this = this;
28643         
28644         var files = [];
28645         
28646         var docs = [];
28647         
28648         Roo.each(this.files, function(file){
28649             
28650             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28651                 var f = this.renderPreview(file);
28652                 files.push(f);
28653                 return;
28654             }
28655             
28656             if(file.type.indexOf('image') != -1){
28657                 this.delegates.push(
28658                     (function(){
28659                         _this.process(file);
28660                     }).createDelegate(this)
28661                 );
28662         
28663                 return;
28664             }
28665             
28666             docs.push(
28667                 (function(){
28668                     _this.process(file);
28669                 }).createDelegate(this)
28670             );
28671             
28672         }, this);
28673         
28674         this.files = files;
28675         
28676         this.delegates = this.delegates.concat(docs);
28677         
28678         if(!this.delegates.length){
28679             this.refresh();
28680             return;
28681         }
28682         
28683         this.progressBar.aria_valuemax = this.delegates.length;
28684         
28685         this.arrange();
28686         
28687         return;
28688     },
28689     
28690     arrange : function()
28691     {
28692         if(!this.delegates.length){
28693             this.progressDialog.hide();
28694             this.refresh();
28695             return;
28696         }
28697         
28698         var delegate = this.delegates.shift();
28699         
28700         this.progressDialog.show();
28701         
28702         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28703         
28704         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28705         
28706         delegate();
28707     },
28708     
28709     refresh : function()
28710     {
28711         this.uploader.show();
28712         
28713         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28714             this.uploader.hide();
28715         }
28716         
28717         Roo.isTouch ? this.closable(false) : this.closable(true);
28718         
28719         this.fireEvent('refresh', this);
28720     },
28721     
28722     onRemove : function(e, el, o)
28723     {
28724         e.preventDefault();
28725         
28726         this.fireEvent('remove', this, o);
28727         
28728     },
28729     
28730     remove : function(o)
28731     {
28732         var files = [];
28733         
28734         Roo.each(this.files, function(file){
28735             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28736                 files.push(file);
28737                 return;
28738             }
28739
28740             o.target.remove();
28741
28742         }, this);
28743         
28744         this.files = files;
28745         
28746         this.refresh();
28747     },
28748     
28749     clear : function()
28750     {
28751         Roo.each(this.files, function(file){
28752             if(!file.target){
28753                 return;
28754             }
28755             
28756             file.target.remove();
28757
28758         }, this);
28759         
28760         this.files = [];
28761         
28762         this.refresh();
28763     },
28764     
28765     onClick : function(e, el, o)
28766     {
28767         e.preventDefault();
28768         
28769         this.fireEvent('click', this, o);
28770         
28771     },
28772     
28773     closable : function(closable)
28774     {
28775         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28776             
28777             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28778             
28779             if(closable){
28780                 el.show();
28781                 return;
28782             }
28783             
28784             el.hide();
28785             
28786         }, this);
28787     },
28788     
28789     xhrOnLoad : function(xhr)
28790     {
28791         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28792             el.remove();
28793         }, this);
28794         
28795         if (xhr.readyState !== 4) {
28796             this.arrange();
28797             this.fireEvent('exception', this, xhr);
28798             return;
28799         }
28800
28801         var response = Roo.decode(xhr.responseText);
28802         
28803         if(!response.success){
28804             this.arrange();
28805             this.fireEvent('exception', this, xhr);
28806             return;
28807         }
28808         
28809         var file = this.renderPreview(response.data);
28810         
28811         this.files.push(file);
28812         
28813         this.arrange();
28814         
28815         this.fireEvent('afterupload', this, xhr);
28816         
28817     },
28818     
28819     xhrOnError : function(xhr)
28820     {
28821         Roo.log('xhr on error');
28822         
28823         var response = Roo.decode(xhr.responseText);
28824           
28825         Roo.log(response);
28826         
28827         this.arrange();
28828     },
28829     
28830     process : function(file)
28831     {
28832         if(this.fireEvent('process', this, file) !== false){
28833             if(this.editable && file.type.indexOf('image') != -1){
28834                 this.fireEvent('edit', this, file);
28835                 return;
28836             }
28837
28838             this.uploadStart(file, false);
28839
28840             return;
28841         }
28842         
28843     },
28844     
28845     uploadStart : function(file, crop)
28846     {
28847         this.xhr = new XMLHttpRequest();
28848         
28849         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28850             this.arrange();
28851             return;
28852         }
28853         
28854         file.xhr = this.xhr;
28855             
28856         this.managerEl.createChild({
28857             tag : 'div',
28858             cls : 'roo-document-manager-loading',
28859             cn : [
28860                 {
28861                     tag : 'div',
28862                     tooltip : file.name,
28863                     cls : 'roo-document-manager-thumb',
28864                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28865                 }
28866             ]
28867
28868         });
28869
28870         this.xhr.open(this.method, this.url, true);
28871         
28872         var headers = {
28873             "Accept": "application/json",
28874             "Cache-Control": "no-cache",
28875             "X-Requested-With": "XMLHttpRequest"
28876         };
28877         
28878         for (var headerName in headers) {
28879             var headerValue = headers[headerName];
28880             if (headerValue) {
28881                 this.xhr.setRequestHeader(headerName, headerValue);
28882             }
28883         }
28884         
28885         var _this = this;
28886         
28887         this.xhr.onload = function()
28888         {
28889             _this.xhrOnLoad(_this.xhr);
28890         }
28891         
28892         this.xhr.onerror = function()
28893         {
28894             _this.xhrOnError(_this.xhr);
28895         }
28896         
28897         var formData = new FormData();
28898
28899         formData.append('returnHTML', 'NO');
28900         
28901         if(crop){
28902             formData.append('crop', crop);
28903         }
28904         
28905         formData.append(this.paramName, file, file.name);
28906         
28907         var options = {
28908             file : file, 
28909             manually : false
28910         };
28911         
28912         if(this.fireEvent('prepare', this, formData, options) != false){
28913             
28914             if(options.manually){
28915                 return;
28916             }
28917             
28918             this.xhr.send(formData);
28919             return;
28920         };
28921         
28922         this.uploadCancel();
28923     },
28924     
28925     uploadCancel : function()
28926     {
28927         if (this.xhr) {
28928             this.xhr.abort();
28929         }
28930         
28931         this.delegates = [];
28932         
28933         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28934             el.remove();
28935         }, this);
28936         
28937         this.arrange();
28938     },
28939     
28940     renderPreview : function(file)
28941     {
28942         if(typeof(file.target) != 'undefined' && file.target){
28943             return file;
28944         }
28945         
28946         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28947         
28948         var previewEl = this.managerEl.createChild({
28949             tag : 'div',
28950             cls : 'roo-document-manager-preview',
28951             cn : [
28952                 {
28953                     tag : 'div',
28954                     tooltip : file[this.toolTipName],
28955                     cls : 'roo-document-manager-thumb',
28956                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28957                 },
28958                 {
28959                     tag : 'button',
28960                     cls : 'close',
28961                     html : '<i class="fa fa-times-circle"></i>'
28962                 }
28963             ]
28964         });
28965
28966         var close = previewEl.select('button.close', true).first();
28967
28968         close.on('click', this.onRemove, this, file);
28969
28970         file.target = previewEl;
28971
28972         var image = previewEl.select('img', true).first();
28973         
28974         var _this = this;
28975         
28976         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28977         
28978         image.on('click', this.onClick, this, file);
28979         
28980         return file;
28981         
28982     },
28983     
28984     onPreviewLoad : function(file, image)
28985     {
28986         if(typeof(file.target) == 'undefined' || !file.target){
28987             return;
28988         }
28989         
28990         var width = image.dom.naturalWidth || image.dom.width;
28991         var height = image.dom.naturalHeight || image.dom.height;
28992         
28993         if(width > height){
28994             file.target.addClass('wide');
28995             return;
28996         }
28997         
28998         file.target.addClass('tall');
28999         return;
29000         
29001     },
29002     
29003     uploadFromSource : function(file, crop)
29004     {
29005         this.xhr = new XMLHttpRequest();
29006         
29007         this.managerEl.createChild({
29008             tag : 'div',
29009             cls : 'roo-document-manager-loading',
29010             cn : [
29011                 {
29012                     tag : 'div',
29013                     tooltip : file.name,
29014                     cls : 'roo-document-manager-thumb',
29015                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29016                 }
29017             ]
29018
29019         });
29020
29021         this.xhr.open(this.method, this.url, true);
29022         
29023         var headers = {
29024             "Accept": "application/json",
29025             "Cache-Control": "no-cache",
29026             "X-Requested-With": "XMLHttpRequest"
29027         };
29028         
29029         for (var headerName in headers) {
29030             var headerValue = headers[headerName];
29031             if (headerValue) {
29032                 this.xhr.setRequestHeader(headerName, headerValue);
29033             }
29034         }
29035         
29036         var _this = this;
29037         
29038         this.xhr.onload = function()
29039         {
29040             _this.xhrOnLoad(_this.xhr);
29041         }
29042         
29043         this.xhr.onerror = function()
29044         {
29045             _this.xhrOnError(_this.xhr);
29046         }
29047         
29048         var formData = new FormData();
29049
29050         formData.append('returnHTML', 'NO');
29051         
29052         formData.append('crop', crop);
29053         
29054         if(typeof(file.filename) != 'undefined'){
29055             formData.append('filename', file.filename);
29056         }
29057         
29058         if(typeof(file.mimetype) != 'undefined'){
29059             formData.append('mimetype', file.mimetype);
29060         }
29061         
29062         Roo.log(formData);
29063         
29064         if(this.fireEvent('prepare', this, formData) != false){
29065             this.xhr.send(formData);
29066         };
29067     }
29068 });
29069
29070 /*
29071 * Licence: LGPL
29072 */
29073
29074 /**
29075  * @class Roo.bootstrap.DocumentViewer
29076  * @extends Roo.bootstrap.Component
29077  * Bootstrap DocumentViewer class
29078  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29079  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29080  * 
29081  * @constructor
29082  * Create a new DocumentViewer
29083  * @param {Object} config The config object
29084  */
29085
29086 Roo.bootstrap.DocumentViewer = function(config){
29087     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29088     
29089     this.addEvents({
29090         /**
29091          * @event initial
29092          * Fire after initEvent
29093          * @param {Roo.bootstrap.DocumentViewer} this
29094          */
29095         "initial" : true,
29096         /**
29097          * @event click
29098          * Fire after click
29099          * @param {Roo.bootstrap.DocumentViewer} this
29100          */
29101         "click" : true,
29102         /**
29103          * @event download
29104          * Fire after download button
29105          * @param {Roo.bootstrap.DocumentViewer} this
29106          */
29107         "download" : true,
29108         /**
29109          * @event trash
29110          * Fire after trash button
29111          * @param {Roo.bootstrap.DocumentViewer} this
29112          */
29113         "trash" : true
29114         
29115     });
29116 };
29117
29118 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29119     
29120     showDownload : true,
29121     
29122     showTrash : true,
29123     
29124     getAutoCreate : function()
29125     {
29126         var cfg = {
29127             tag : 'div',
29128             cls : 'roo-document-viewer',
29129             cn : [
29130                 {
29131                     tag : 'div',
29132                     cls : 'roo-document-viewer-body',
29133                     cn : [
29134                         {
29135                             tag : 'div',
29136                             cls : 'roo-document-viewer-thumb',
29137                             cn : [
29138                                 {
29139                                     tag : 'img',
29140                                     cls : 'roo-document-viewer-image'
29141                                 }
29142                             ]
29143                         }
29144                     ]
29145                 },
29146                 {
29147                     tag : 'div',
29148                     cls : 'roo-document-viewer-footer',
29149                     cn : {
29150                         tag : 'div',
29151                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29152                         cn : [
29153                             {
29154                                 tag : 'div',
29155                                 cls : 'btn-group roo-document-viewer-download',
29156                                 cn : [
29157                                     {
29158                                         tag : 'button',
29159                                         cls : 'btn btn-default',
29160                                         html : '<i class="fa fa-download"></i>'
29161                                     }
29162                                 ]
29163                             },
29164                             {
29165                                 tag : 'div',
29166                                 cls : 'btn-group roo-document-viewer-trash',
29167                                 cn : [
29168                                     {
29169                                         tag : 'button',
29170                                         cls : 'btn btn-default',
29171                                         html : '<i class="fa fa-trash"></i>'
29172                                     }
29173                                 ]
29174                             }
29175                         ]
29176                     }
29177                 }
29178             ]
29179         };
29180         
29181         return cfg;
29182     },
29183     
29184     initEvents : function()
29185     {
29186         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29187         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29188         
29189         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29190         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29191         
29192         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29193         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29194         
29195         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29196         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29197         
29198         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29199         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29200         
29201         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29202         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29203         
29204         this.bodyEl.on('click', this.onClick, this);
29205         this.downloadBtn.on('click', this.onDownload, this);
29206         this.trashBtn.on('click', this.onTrash, this);
29207         
29208         this.downloadBtn.hide();
29209         this.trashBtn.hide();
29210         
29211         if(this.showDownload){
29212             this.downloadBtn.show();
29213         }
29214         
29215         if(this.showTrash){
29216             this.trashBtn.show();
29217         }
29218         
29219         if(!this.showDownload && !this.showTrash) {
29220             this.footerEl.hide();
29221         }
29222         
29223     },
29224     
29225     initial : function()
29226     {
29227         this.fireEvent('initial', this);
29228         
29229     },
29230     
29231     onClick : function(e)
29232     {
29233         e.preventDefault();
29234         
29235         this.fireEvent('click', this);
29236     },
29237     
29238     onDownload : function(e)
29239     {
29240         e.preventDefault();
29241         
29242         this.fireEvent('download', this);
29243     },
29244     
29245     onTrash : function(e)
29246     {
29247         e.preventDefault();
29248         
29249         this.fireEvent('trash', this);
29250     }
29251     
29252 });
29253 /*
29254  * - LGPL
29255  *
29256  * nav progress bar
29257  * 
29258  */
29259
29260 /**
29261  * @class Roo.bootstrap.NavProgressBar
29262  * @extends Roo.bootstrap.Component
29263  * Bootstrap NavProgressBar class
29264  * 
29265  * @constructor
29266  * Create a new nav progress bar
29267  * @param {Object} config The config object
29268  */
29269
29270 Roo.bootstrap.NavProgressBar = function(config){
29271     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29272
29273     this.bullets = this.bullets || [];
29274    
29275 //    Roo.bootstrap.NavProgressBar.register(this);
29276      this.addEvents({
29277         /**
29278              * @event changed
29279              * Fires when the active item changes
29280              * @param {Roo.bootstrap.NavProgressBar} this
29281              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29282              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29283          */
29284         'changed': true
29285      });
29286     
29287 };
29288
29289 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29290     
29291     bullets : [],
29292     barItems : [],
29293     
29294     getAutoCreate : function()
29295     {
29296         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29297         
29298         cfg = {
29299             tag : 'div',
29300             cls : 'roo-navigation-bar-group',
29301             cn : [
29302                 {
29303                     tag : 'div',
29304                     cls : 'roo-navigation-top-bar'
29305                 },
29306                 {
29307                     tag : 'div',
29308                     cls : 'roo-navigation-bullets-bar',
29309                     cn : [
29310                         {
29311                             tag : 'ul',
29312                             cls : 'roo-navigation-bar'
29313                         }
29314                     ]
29315                 },
29316                 
29317                 {
29318                     tag : 'div',
29319                     cls : 'roo-navigation-bottom-bar'
29320                 }
29321             ]
29322             
29323         };
29324         
29325         return cfg;
29326         
29327     },
29328     
29329     initEvents: function() 
29330     {
29331         
29332     },
29333     
29334     onRender : function(ct, position) 
29335     {
29336         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29337         
29338         if(this.bullets.length){
29339             Roo.each(this.bullets, function(b){
29340                this.addItem(b);
29341             }, this);
29342         }
29343         
29344         this.format();
29345         
29346     },
29347     
29348     addItem : function(cfg)
29349     {
29350         var item = new Roo.bootstrap.NavProgressItem(cfg);
29351         
29352         item.parentId = this.id;
29353         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29354         
29355         if(cfg.html){
29356             var top = new Roo.bootstrap.Element({
29357                 tag : 'div',
29358                 cls : 'roo-navigation-bar-text'
29359             });
29360             
29361             var bottom = new Roo.bootstrap.Element({
29362                 tag : 'div',
29363                 cls : 'roo-navigation-bar-text'
29364             });
29365             
29366             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29367             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29368             
29369             var topText = new Roo.bootstrap.Element({
29370                 tag : 'span',
29371                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29372             });
29373             
29374             var bottomText = new Roo.bootstrap.Element({
29375                 tag : 'span',
29376                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29377             });
29378             
29379             topText.onRender(top.el, null);
29380             bottomText.onRender(bottom.el, null);
29381             
29382             item.topEl = top;
29383             item.bottomEl = bottom;
29384         }
29385         
29386         this.barItems.push(item);
29387         
29388         return item;
29389     },
29390     
29391     getActive : function()
29392     {
29393         var active = false;
29394         
29395         Roo.each(this.barItems, function(v){
29396             
29397             if (!v.isActive()) {
29398                 return;
29399             }
29400             
29401             active = v;
29402             return false;
29403             
29404         });
29405         
29406         return active;
29407     },
29408     
29409     setActiveItem : function(item)
29410     {
29411         var prev = false;
29412         
29413         Roo.each(this.barItems, function(v){
29414             if (v.rid == item.rid) {
29415                 return ;
29416             }
29417             
29418             if (v.isActive()) {
29419                 v.setActive(false);
29420                 prev = v;
29421             }
29422         });
29423
29424         item.setActive(true);
29425         
29426         this.fireEvent('changed', this, item, prev);
29427     },
29428     
29429     getBarItem: function(rid)
29430     {
29431         var ret = false;
29432         
29433         Roo.each(this.barItems, function(e) {
29434             if (e.rid != rid) {
29435                 return;
29436             }
29437             
29438             ret =  e;
29439             return false;
29440         });
29441         
29442         return ret;
29443     },
29444     
29445     indexOfItem : function(item)
29446     {
29447         var index = false;
29448         
29449         Roo.each(this.barItems, function(v, i){
29450             
29451             if (v.rid != item.rid) {
29452                 return;
29453             }
29454             
29455             index = i;
29456             return false
29457         });
29458         
29459         return index;
29460     },
29461     
29462     setActiveNext : function()
29463     {
29464         var i = this.indexOfItem(this.getActive());
29465         
29466         if (i > this.barItems.length) {
29467             return;
29468         }
29469         
29470         this.setActiveItem(this.barItems[i+1]);
29471     },
29472     
29473     setActivePrev : function()
29474     {
29475         var i = this.indexOfItem(this.getActive());
29476         
29477         if (i  < 1) {
29478             return;
29479         }
29480         
29481         this.setActiveItem(this.barItems[i-1]);
29482     },
29483     
29484     format : function()
29485     {
29486         if(!this.barItems.length){
29487             return;
29488         }
29489      
29490         var width = 100 / this.barItems.length;
29491         
29492         Roo.each(this.barItems, function(i){
29493             i.el.setStyle('width', width + '%');
29494             i.topEl.el.setStyle('width', width + '%');
29495             i.bottomEl.el.setStyle('width', width + '%');
29496         }, this);
29497         
29498     }
29499     
29500 });
29501 /*
29502  * - LGPL
29503  *
29504  * Nav Progress Item
29505  * 
29506  */
29507
29508 /**
29509  * @class Roo.bootstrap.NavProgressItem
29510  * @extends Roo.bootstrap.Component
29511  * Bootstrap NavProgressItem class
29512  * @cfg {String} rid the reference id
29513  * @cfg {Boolean} active (true|false) Is item active default false
29514  * @cfg {Boolean} disabled (true|false) Is item active default false
29515  * @cfg {String} html
29516  * @cfg {String} position (top|bottom) text position default bottom
29517  * @cfg {String} icon show icon instead of number
29518  * 
29519  * @constructor
29520  * Create a new NavProgressItem
29521  * @param {Object} config The config object
29522  */
29523 Roo.bootstrap.NavProgressItem = function(config){
29524     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29525     this.addEvents({
29526         // raw events
29527         /**
29528          * @event click
29529          * The raw click event for the entire grid.
29530          * @param {Roo.bootstrap.NavProgressItem} this
29531          * @param {Roo.EventObject} e
29532          */
29533         "click" : true
29534     });
29535    
29536 };
29537
29538 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29539     
29540     rid : '',
29541     active : false,
29542     disabled : false,
29543     html : '',
29544     position : 'bottom',
29545     icon : false,
29546     
29547     getAutoCreate : function()
29548     {
29549         var iconCls = 'roo-navigation-bar-item-icon';
29550         
29551         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29552         
29553         var cfg = {
29554             tag: 'li',
29555             cls: 'roo-navigation-bar-item',
29556             cn : [
29557                 {
29558                     tag : 'i',
29559                     cls : iconCls
29560                 }
29561             ]
29562         };
29563         
29564         if(this.active){
29565             cfg.cls += ' active';
29566         }
29567         if(this.disabled){
29568             cfg.cls += ' disabled';
29569         }
29570         
29571         return cfg;
29572     },
29573     
29574     disable : function()
29575     {
29576         this.setDisabled(true);
29577     },
29578     
29579     enable : function()
29580     {
29581         this.setDisabled(false);
29582     },
29583     
29584     initEvents: function() 
29585     {
29586         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29587         
29588         this.iconEl.on('click', this.onClick, this);
29589     },
29590     
29591     onClick : function(e)
29592     {
29593         e.preventDefault();
29594         
29595         if(this.disabled){
29596             return;
29597         }
29598         
29599         if(this.fireEvent('click', this, e) === false){
29600             return;
29601         };
29602         
29603         this.parent().setActiveItem(this);
29604     },
29605     
29606     isActive: function () 
29607     {
29608         return this.active;
29609     },
29610     
29611     setActive : function(state)
29612     {
29613         if(this.active == state){
29614             return;
29615         }
29616         
29617         this.active = state;
29618         
29619         if (state) {
29620             this.el.addClass('active');
29621             return;
29622         }
29623         
29624         this.el.removeClass('active');
29625         
29626         return;
29627     },
29628     
29629     setDisabled : function(state)
29630     {
29631         if(this.disabled == state){
29632             return;
29633         }
29634         
29635         this.disabled = state;
29636         
29637         if (state) {
29638             this.el.addClass('disabled');
29639             return;
29640         }
29641         
29642         this.el.removeClass('disabled');
29643     },
29644     
29645     tooltipEl : function()
29646     {
29647         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29648     }
29649 });
29650  
29651
29652  /*
29653  * - LGPL
29654  *
29655  * FieldLabel
29656  * 
29657  */
29658
29659 /**
29660  * @class Roo.bootstrap.FieldLabel
29661  * @extends Roo.bootstrap.Component
29662  * Bootstrap FieldLabel class
29663  * @cfg {String} html contents of the element
29664  * @cfg {String} tag tag of the element default label
29665  * @cfg {String} cls class of the element
29666  * @cfg {String} target label target 
29667  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29668  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29669  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29670  * @cfg {String} iconTooltip default "This field is required"
29671  * 
29672  * @constructor
29673  * Create a new FieldLabel
29674  * @param {Object} config The config object
29675  */
29676
29677 Roo.bootstrap.FieldLabel = function(config){
29678     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29679     
29680     this.addEvents({
29681             /**
29682              * @event invalid
29683              * Fires after the field has been marked as invalid.
29684              * @param {Roo.form.FieldLabel} this
29685              * @param {String} msg The validation message
29686              */
29687             invalid : true,
29688             /**
29689              * @event valid
29690              * Fires after the field has been validated with no errors.
29691              * @param {Roo.form.FieldLabel} this
29692              */
29693             valid : true
29694         });
29695 };
29696
29697 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29698     
29699     tag: 'label',
29700     cls: '',
29701     html: '',
29702     target: '',
29703     allowBlank : true,
29704     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29705     validClass : 'text-success fa fa-lg fa-check',
29706     iconTooltip : 'This field is required',
29707     
29708     getAutoCreate : function(){
29709         
29710         var cfg = {
29711             tag : this.tag,
29712             cls : 'roo-bootstrap-field-label ' + this.cls,
29713             for : this.target,
29714             cn : [
29715                 {
29716                     tag : 'i',
29717                     cls : '',
29718                     tooltip : this.iconTooltip
29719                 },
29720                 {
29721                     tag : 'span',
29722                     html : this.html
29723                 }
29724             ] 
29725         };
29726         
29727         return cfg;
29728     },
29729     
29730     initEvents: function() 
29731     {
29732         Roo.bootstrap.Element.superclass.initEvents.call(this);
29733         
29734         this.iconEl = this.el.select('i', true).first();
29735         
29736         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29737         
29738         Roo.bootstrap.FieldLabel.register(this);
29739     },
29740     
29741     /**
29742      * Mark this field as valid
29743      */
29744     markValid : function()
29745     {
29746         this.iconEl.show();
29747         
29748         this.iconEl.removeClass(this.invalidClass);
29749         
29750         this.iconEl.addClass(this.validClass);
29751         
29752         this.fireEvent('valid', this);
29753     },
29754     
29755     /**
29756      * Mark this field as invalid
29757      * @param {String} msg The validation message
29758      */
29759     markInvalid : function(msg)
29760     {
29761         this.iconEl.show();
29762         
29763         this.iconEl.removeClass(this.validClass);
29764         
29765         this.iconEl.addClass(this.invalidClass);
29766         
29767         this.fireEvent('invalid', this, msg);
29768     }
29769     
29770    
29771 });
29772
29773 Roo.apply(Roo.bootstrap.FieldLabel, {
29774     
29775     groups: {},
29776     
29777      /**
29778     * register a FieldLabel Group
29779     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29780     */
29781     register : function(label)
29782     {
29783         if(this.groups.hasOwnProperty(label.target)){
29784             return;
29785         }
29786      
29787         this.groups[label.target] = label;
29788         
29789     },
29790     /**
29791     * fetch a FieldLabel Group based on the target
29792     * @param {string} target
29793     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29794     */
29795     get: function(target) {
29796         if (typeof(this.groups[target]) == 'undefined') {
29797             return false;
29798         }
29799         
29800         return this.groups[target] ;
29801     }
29802 });
29803
29804  
29805
29806  /*
29807  * - LGPL
29808  *
29809  * page DateSplitField.
29810  * 
29811  */
29812
29813
29814 /**
29815  * @class Roo.bootstrap.DateSplitField
29816  * @extends Roo.bootstrap.Component
29817  * Bootstrap DateSplitField class
29818  * @cfg {string} fieldLabel - the label associated
29819  * @cfg {Number} labelWidth set the width of label (0-12)
29820  * @cfg {String} labelAlign (top|left)
29821  * @cfg {Boolean} dayAllowBlank (true|false) default false
29822  * @cfg {Boolean} monthAllowBlank (true|false) default false
29823  * @cfg {Boolean} yearAllowBlank (true|false) default false
29824  * @cfg {string} dayPlaceholder 
29825  * @cfg {string} monthPlaceholder
29826  * @cfg {string} yearPlaceholder
29827  * @cfg {string} dayFormat default 'd'
29828  * @cfg {string} monthFormat default 'm'
29829  * @cfg {string} yearFormat default 'Y'
29830  * @cfg {Number} labellg set the width of label (1-12)
29831  * @cfg {Number} labelmd set the width of label (1-12)
29832  * @cfg {Number} labelsm set the width of label (1-12)
29833  * @cfg {Number} labelxs set the width of label (1-12)
29834
29835  *     
29836  * @constructor
29837  * Create a new DateSplitField
29838  * @param {Object} config The config object
29839  */
29840
29841 Roo.bootstrap.DateSplitField = function(config){
29842     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29843     
29844     this.addEvents({
29845         // raw events
29846          /**
29847          * @event years
29848          * getting the data of years
29849          * @param {Roo.bootstrap.DateSplitField} this
29850          * @param {Object} years
29851          */
29852         "years" : true,
29853         /**
29854          * @event days
29855          * getting the data of days
29856          * @param {Roo.bootstrap.DateSplitField} this
29857          * @param {Object} days
29858          */
29859         "days" : true,
29860         /**
29861          * @event invalid
29862          * Fires after the field has been marked as invalid.
29863          * @param {Roo.form.Field} this
29864          * @param {String} msg The validation message
29865          */
29866         invalid : true,
29867        /**
29868          * @event valid
29869          * Fires after the field has been validated with no errors.
29870          * @param {Roo.form.Field} this
29871          */
29872         valid : true
29873     });
29874 };
29875
29876 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29877     
29878     fieldLabel : '',
29879     labelAlign : 'top',
29880     labelWidth : 3,
29881     dayAllowBlank : false,
29882     monthAllowBlank : false,
29883     yearAllowBlank : false,
29884     dayPlaceholder : '',
29885     monthPlaceholder : '',
29886     yearPlaceholder : '',
29887     dayFormat : 'd',
29888     monthFormat : 'm',
29889     yearFormat : 'Y',
29890     isFormField : true,
29891     labellg : 0,
29892     labelmd : 0,
29893     labelsm : 0,
29894     labelxs : 0,
29895     
29896     getAutoCreate : function()
29897     {
29898         var cfg = {
29899             tag : 'div',
29900             cls : 'row roo-date-split-field-group',
29901             cn : [
29902                 {
29903                     tag : 'input',
29904                     type : 'hidden',
29905                     cls : 'form-hidden-field roo-date-split-field-group-value',
29906                     name : this.name
29907                 }
29908             ]
29909         };
29910         
29911         var labelCls = 'col-md-12';
29912         var contentCls = 'col-md-4';
29913         
29914         if(this.fieldLabel){
29915             
29916             var label = {
29917                 tag : 'div',
29918                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29919                 cn : [
29920                     {
29921                         tag : 'label',
29922                         html : this.fieldLabel
29923                     }
29924                 ]
29925             };
29926             
29927             if(this.labelAlign == 'left'){
29928             
29929                 if(this.labelWidth > 12){
29930                     label.style = "width: " + this.labelWidth + 'px';
29931                 }
29932
29933                 if(this.labelWidth < 13 && this.labelmd == 0){
29934                     this.labelmd = this.labelWidth;
29935                 }
29936
29937                 if(this.labellg > 0){
29938                     labelCls = ' col-lg-' + this.labellg;
29939                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29940                 }
29941
29942                 if(this.labelmd > 0){
29943                     labelCls = ' col-md-' + this.labelmd;
29944                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29945                 }
29946
29947                 if(this.labelsm > 0){
29948                     labelCls = ' col-sm-' + this.labelsm;
29949                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29950                 }
29951
29952                 if(this.labelxs > 0){
29953                     labelCls = ' col-xs-' + this.labelxs;
29954                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29955                 }
29956             }
29957             
29958             label.cls += ' ' + labelCls;
29959             
29960             cfg.cn.push(label);
29961         }
29962         
29963         Roo.each(['day', 'month', 'year'], function(t){
29964             cfg.cn.push({
29965                 tag : 'div',
29966                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29967             });
29968         }, this);
29969         
29970         return cfg;
29971     },
29972     
29973     inputEl: function ()
29974     {
29975         return this.el.select('.roo-date-split-field-group-value', true).first();
29976     },
29977     
29978     onRender : function(ct, position) 
29979     {
29980         var _this = this;
29981         
29982         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29983         
29984         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29985         
29986         this.dayField = new Roo.bootstrap.ComboBox({
29987             allowBlank : this.dayAllowBlank,
29988             alwaysQuery : true,
29989             displayField : 'value',
29990             editable : false,
29991             fieldLabel : '',
29992             forceSelection : true,
29993             mode : 'local',
29994             placeholder : this.dayPlaceholder,
29995             selectOnFocus : true,
29996             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29997             triggerAction : 'all',
29998             typeAhead : true,
29999             valueField : 'value',
30000             store : new Roo.data.SimpleStore({
30001                 data : (function() {    
30002                     var days = [];
30003                     _this.fireEvent('days', _this, days);
30004                     return days;
30005                 })(),
30006                 fields : [ 'value' ]
30007             }),
30008             listeners : {
30009                 select : function (_self, record, index)
30010                 {
30011                     _this.setValue(_this.getValue());
30012                 }
30013             }
30014         });
30015
30016         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30017         
30018         this.monthField = new Roo.bootstrap.MonthField({
30019             after : '<i class=\"fa fa-calendar\"></i>',
30020             allowBlank : this.monthAllowBlank,
30021             placeholder : this.monthPlaceholder,
30022             readOnly : true,
30023             listeners : {
30024                 render : function (_self)
30025                 {
30026                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30027                         e.preventDefault();
30028                         _self.focus();
30029                     });
30030                 },
30031                 select : function (_self, oldvalue, newvalue)
30032                 {
30033                     _this.setValue(_this.getValue());
30034                 }
30035             }
30036         });
30037         
30038         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30039         
30040         this.yearField = new Roo.bootstrap.ComboBox({
30041             allowBlank : this.yearAllowBlank,
30042             alwaysQuery : true,
30043             displayField : 'value',
30044             editable : false,
30045             fieldLabel : '',
30046             forceSelection : true,
30047             mode : 'local',
30048             placeholder : this.yearPlaceholder,
30049             selectOnFocus : true,
30050             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30051             triggerAction : 'all',
30052             typeAhead : true,
30053             valueField : 'value',
30054             store : new Roo.data.SimpleStore({
30055                 data : (function() {
30056                     var years = [];
30057                     _this.fireEvent('years', _this, years);
30058                     return years;
30059                 })(),
30060                 fields : [ 'value' ]
30061             }),
30062             listeners : {
30063                 select : function (_self, record, index)
30064                 {
30065                     _this.setValue(_this.getValue());
30066                 }
30067             }
30068         });
30069
30070         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30071     },
30072     
30073     setValue : function(v, format)
30074     {
30075         this.inputEl.dom.value = v;
30076         
30077         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30078         
30079         var d = Date.parseDate(v, f);
30080         
30081         if(!d){
30082             this.validate();
30083             return;
30084         }
30085         
30086         this.setDay(d.format(this.dayFormat));
30087         this.setMonth(d.format(this.monthFormat));
30088         this.setYear(d.format(this.yearFormat));
30089         
30090         this.validate();
30091         
30092         return;
30093     },
30094     
30095     setDay : function(v)
30096     {
30097         this.dayField.setValue(v);
30098         this.inputEl.dom.value = this.getValue();
30099         this.validate();
30100         return;
30101     },
30102     
30103     setMonth : function(v)
30104     {
30105         this.monthField.setValue(v, true);
30106         this.inputEl.dom.value = this.getValue();
30107         this.validate();
30108         return;
30109     },
30110     
30111     setYear : function(v)
30112     {
30113         this.yearField.setValue(v);
30114         this.inputEl.dom.value = this.getValue();
30115         this.validate();
30116         return;
30117     },
30118     
30119     getDay : function()
30120     {
30121         return this.dayField.getValue();
30122     },
30123     
30124     getMonth : function()
30125     {
30126         return this.monthField.getValue();
30127     },
30128     
30129     getYear : function()
30130     {
30131         return this.yearField.getValue();
30132     },
30133     
30134     getValue : function()
30135     {
30136         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30137         
30138         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30139         
30140         return date;
30141     },
30142     
30143     reset : function()
30144     {
30145         this.setDay('');
30146         this.setMonth('');
30147         this.setYear('');
30148         this.inputEl.dom.value = '';
30149         this.validate();
30150         return;
30151     },
30152     
30153     validate : function()
30154     {
30155         var d = this.dayField.validate();
30156         var m = this.monthField.validate();
30157         var y = this.yearField.validate();
30158         
30159         var valid = true;
30160         
30161         if(
30162                 (!this.dayAllowBlank && !d) ||
30163                 (!this.monthAllowBlank && !m) ||
30164                 (!this.yearAllowBlank && !y)
30165         ){
30166             valid = false;
30167         }
30168         
30169         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30170             return valid;
30171         }
30172         
30173         if(valid){
30174             this.markValid();
30175             return valid;
30176         }
30177         
30178         this.markInvalid();
30179         
30180         return valid;
30181     },
30182     
30183     markValid : function()
30184     {
30185         
30186         var label = this.el.select('label', true).first();
30187         var icon = this.el.select('i.fa-star', true).first();
30188
30189         if(label && icon){
30190             icon.remove();
30191         }
30192         
30193         this.fireEvent('valid', this);
30194     },
30195     
30196      /**
30197      * Mark this field as invalid
30198      * @param {String} msg The validation message
30199      */
30200     markInvalid : function(msg)
30201     {
30202         
30203         var label = this.el.select('label', true).first();
30204         var icon = this.el.select('i.fa-star', true).first();
30205
30206         if(label && !icon){
30207             this.el.select('.roo-date-split-field-label', true).createChild({
30208                 tag : 'i',
30209                 cls : 'text-danger fa fa-lg fa-star',
30210                 tooltip : 'This field is required',
30211                 style : 'margin-right:5px;'
30212             }, label, true);
30213         }
30214         
30215         this.fireEvent('invalid', this, msg);
30216     },
30217     
30218     clearInvalid : function()
30219     {
30220         var label = this.el.select('label', true).first();
30221         var icon = this.el.select('i.fa-star', true).first();
30222
30223         if(label && icon){
30224             icon.remove();
30225         }
30226         
30227         this.fireEvent('valid', this);
30228     },
30229     
30230     getName: function()
30231     {
30232         return this.name;
30233     }
30234     
30235 });
30236
30237  /**
30238  *
30239  * This is based on 
30240  * http://masonry.desandro.com
30241  *
30242  * The idea is to render all the bricks based on vertical width...
30243  *
30244  * The original code extends 'outlayer' - we might need to use that....
30245  * 
30246  */
30247
30248
30249 /**
30250  * @class Roo.bootstrap.LayoutMasonry
30251  * @extends Roo.bootstrap.Component
30252  * Bootstrap Layout Masonry class
30253  * 
30254  * @constructor
30255  * Create a new Element
30256  * @param {Object} config The config object
30257  */
30258
30259 Roo.bootstrap.LayoutMasonry = function(config){
30260     
30261     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30262     
30263     this.bricks = [];
30264     
30265     Roo.bootstrap.LayoutMasonry.register(this);
30266     
30267     this.addEvents({
30268         // raw events
30269         /**
30270          * @event layout
30271          * Fire after layout the items
30272          * @param {Roo.bootstrap.LayoutMasonry} this
30273          * @param {Roo.EventObject} e
30274          */
30275         "layout" : true
30276     });
30277     
30278 };
30279
30280 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30281     
30282     /**
30283      * @cfg {Boolean} isLayoutInstant = no animation?
30284      */   
30285     isLayoutInstant : false, // needed?
30286    
30287     /**
30288      * @cfg {Number} boxWidth  width of the columns
30289      */   
30290     boxWidth : 450,
30291     
30292       /**
30293      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30294      */   
30295     boxHeight : 0,
30296     
30297     /**
30298      * @cfg {Number} padWidth padding below box..
30299      */   
30300     padWidth : 10, 
30301     
30302     /**
30303      * @cfg {Number} gutter gutter width..
30304      */   
30305     gutter : 10,
30306     
30307      /**
30308      * @cfg {Number} maxCols maximum number of columns
30309      */   
30310     
30311     maxCols: 0,
30312     
30313     /**
30314      * @cfg {Boolean} isAutoInitial defalut true
30315      */   
30316     isAutoInitial : true, 
30317     
30318     containerWidth: 0,
30319     
30320     /**
30321      * @cfg {Boolean} isHorizontal defalut false
30322      */   
30323     isHorizontal : false, 
30324
30325     currentSize : null,
30326     
30327     tag: 'div',
30328     
30329     cls: '',
30330     
30331     bricks: null, //CompositeElement
30332     
30333     cols : 1,
30334     
30335     _isLayoutInited : false,
30336     
30337 //    isAlternative : false, // only use for vertical layout...
30338     
30339     /**
30340      * @cfg {Number} alternativePadWidth padding below box..
30341      */   
30342     alternativePadWidth : 50,
30343     
30344     selectedBrick : [],
30345     
30346     getAutoCreate : function(){
30347         
30348         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30349         
30350         var cfg = {
30351             tag: this.tag,
30352             cls: 'blog-masonary-wrapper ' + this.cls,
30353             cn : {
30354                 cls : 'mas-boxes masonary'
30355             }
30356         };
30357         
30358         return cfg;
30359     },
30360     
30361     getChildContainer: function( )
30362     {
30363         if (this.boxesEl) {
30364             return this.boxesEl;
30365         }
30366         
30367         this.boxesEl = this.el.select('.mas-boxes').first();
30368         
30369         return this.boxesEl;
30370     },
30371     
30372     
30373     initEvents : function()
30374     {
30375         var _this = this;
30376         
30377         if(this.isAutoInitial){
30378             Roo.log('hook children rendered');
30379             this.on('childrenrendered', function() {
30380                 Roo.log('children rendered');
30381                 _this.initial();
30382             } ,this);
30383         }
30384     },
30385     
30386     initial : function()
30387     {
30388         this.selectedBrick = [];
30389         
30390         this.currentSize = this.el.getBox(true);
30391         
30392         Roo.EventManager.onWindowResize(this.resize, this); 
30393
30394         if(!this.isAutoInitial){
30395             this.layout();
30396             return;
30397         }
30398         
30399         this.layout();
30400         
30401         return;
30402         //this.layout.defer(500,this);
30403         
30404     },
30405     
30406     resize : function()
30407     {
30408         var cs = this.el.getBox(true);
30409         
30410         if (
30411                 this.currentSize.width == cs.width && 
30412                 this.currentSize.x == cs.x && 
30413                 this.currentSize.height == cs.height && 
30414                 this.currentSize.y == cs.y 
30415         ) {
30416             Roo.log("no change in with or X or Y");
30417             return;
30418         }
30419         
30420         this.currentSize = cs;
30421         
30422         this.layout();
30423         
30424     },
30425     
30426     layout : function()
30427     {   
30428         this._resetLayout();
30429         
30430         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30431         
30432         this.layoutItems( isInstant );
30433       
30434         this._isLayoutInited = true;
30435         
30436         this.fireEvent('layout', this);
30437         
30438     },
30439     
30440     _resetLayout : function()
30441     {
30442         if(this.isHorizontal){
30443             this.horizontalMeasureColumns();
30444             return;
30445         }
30446         
30447         this.verticalMeasureColumns();
30448         
30449     },
30450     
30451     verticalMeasureColumns : function()
30452     {
30453         this.getContainerWidth();
30454         
30455 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30456 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30457 //            return;
30458 //        }
30459         
30460         var boxWidth = this.boxWidth + this.padWidth;
30461         
30462         if(this.containerWidth < this.boxWidth){
30463             boxWidth = this.containerWidth
30464         }
30465         
30466         var containerWidth = this.containerWidth;
30467         
30468         var cols = Math.floor(containerWidth / boxWidth);
30469         
30470         this.cols = Math.max( cols, 1 );
30471         
30472         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30473         
30474         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30475         
30476         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30477         
30478         this.colWidth = boxWidth + avail - this.padWidth;
30479         
30480         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30481         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30482     },
30483     
30484     horizontalMeasureColumns : function()
30485     {
30486         this.getContainerWidth();
30487         
30488         var boxWidth = this.boxWidth;
30489         
30490         if(this.containerWidth < boxWidth){
30491             boxWidth = this.containerWidth;
30492         }
30493         
30494         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30495         
30496         this.el.setHeight(boxWidth);
30497         
30498     },
30499     
30500     getContainerWidth : function()
30501     {
30502         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30503     },
30504     
30505     layoutItems : function( isInstant )
30506     {
30507         Roo.log(this.bricks);
30508         
30509         var items = Roo.apply([], this.bricks);
30510         
30511         if(this.isHorizontal){
30512             this._horizontalLayoutItems( items , isInstant );
30513             return;
30514         }
30515         
30516 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30517 //            this._verticalAlternativeLayoutItems( items , isInstant );
30518 //            return;
30519 //        }
30520         
30521         this._verticalLayoutItems( items , isInstant );
30522         
30523     },
30524     
30525     _verticalLayoutItems : function ( items , isInstant)
30526     {
30527         if ( !items || !items.length ) {
30528             return;
30529         }
30530         
30531         var standard = [
30532             ['xs', 'xs', 'xs', 'tall'],
30533             ['xs', 'xs', 'tall'],
30534             ['xs', 'xs', 'sm'],
30535             ['xs', 'xs', 'xs'],
30536             ['xs', 'tall'],
30537             ['xs', 'sm'],
30538             ['xs', 'xs'],
30539             ['xs'],
30540             
30541             ['sm', 'xs', 'xs'],
30542             ['sm', 'xs'],
30543             ['sm'],
30544             
30545             ['tall', 'xs', 'xs', 'xs'],
30546             ['tall', 'xs', 'xs'],
30547             ['tall', 'xs'],
30548             ['tall']
30549             
30550         ];
30551         
30552         var queue = [];
30553         
30554         var boxes = [];
30555         
30556         var box = [];
30557         
30558         Roo.each(items, function(item, k){
30559             
30560             switch (item.size) {
30561                 // these layouts take up a full box,
30562                 case 'md' :
30563                 case 'md-left' :
30564                 case 'md-right' :
30565                 case 'wide' :
30566                     
30567                     if(box.length){
30568                         boxes.push(box);
30569                         box = [];
30570                     }
30571                     
30572                     boxes.push([item]);
30573                     
30574                     break;
30575                     
30576                 case 'xs' :
30577                 case 'sm' :
30578                 case 'tall' :
30579                     
30580                     box.push(item);
30581                     
30582                     break;
30583                 default :
30584                     break;
30585                     
30586             }
30587             
30588         }, this);
30589         
30590         if(box.length){
30591             boxes.push(box);
30592             box = [];
30593         }
30594         
30595         var filterPattern = function(box, length)
30596         {
30597             if(!box.length){
30598                 return;
30599             }
30600             
30601             var match = false;
30602             
30603             var pattern = box.slice(0, length);
30604             
30605             var format = [];
30606             
30607             Roo.each(pattern, function(i){
30608                 format.push(i.size);
30609             }, this);
30610             
30611             Roo.each(standard, function(s){
30612                 
30613                 if(String(s) != String(format)){
30614                     return;
30615                 }
30616                 
30617                 match = true;
30618                 return false;
30619                 
30620             }, this);
30621             
30622             if(!match && length == 1){
30623                 return;
30624             }
30625             
30626             if(!match){
30627                 filterPattern(box, length - 1);
30628                 return;
30629             }
30630                 
30631             queue.push(pattern);
30632
30633             box = box.slice(length, box.length);
30634
30635             filterPattern(box, 4);
30636
30637             return;
30638             
30639         }
30640         
30641         Roo.each(boxes, function(box, k){
30642             
30643             if(!box.length){
30644                 return;
30645             }
30646             
30647             if(box.length == 1){
30648                 queue.push(box);
30649                 return;
30650             }
30651             
30652             filterPattern(box, 4);
30653             
30654         }, this);
30655         
30656         this._processVerticalLayoutQueue( queue, isInstant );
30657         
30658     },
30659     
30660 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30661 //    {
30662 //        if ( !items || !items.length ) {
30663 //            return;
30664 //        }
30665 //
30666 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30667 //        
30668 //    },
30669     
30670     _horizontalLayoutItems : function ( items , isInstant)
30671     {
30672         if ( !items || !items.length || items.length < 3) {
30673             return;
30674         }
30675         
30676         items.reverse();
30677         
30678         var eItems = items.slice(0, 3);
30679         
30680         items = items.slice(3, items.length);
30681         
30682         var standard = [
30683             ['xs', 'xs', 'xs', 'wide'],
30684             ['xs', 'xs', 'wide'],
30685             ['xs', 'xs', 'sm'],
30686             ['xs', 'xs', 'xs'],
30687             ['xs', 'wide'],
30688             ['xs', 'sm'],
30689             ['xs', 'xs'],
30690             ['xs'],
30691             
30692             ['sm', 'xs', 'xs'],
30693             ['sm', 'xs'],
30694             ['sm'],
30695             
30696             ['wide', 'xs', 'xs', 'xs'],
30697             ['wide', 'xs', 'xs'],
30698             ['wide', 'xs'],
30699             ['wide'],
30700             
30701             ['wide-thin']
30702         ];
30703         
30704         var queue = [];
30705         
30706         var boxes = [];
30707         
30708         var box = [];
30709         
30710         Roo.each(items, function(item, k){
30711             
30712             switch (item.size) {
30713                 case 'md' :
30714                 case 'md-left' :
30715                 case 'md-right' :
30716                 case 'tall' :
30717                     
30718                     if(box.length){
30719                         boxes.push(box);
30720                         box = [];
30721                     }
30722                     
30723                     boxes.push([item]);
30724                     
30725                     break;
30726                     
30727                 case 'xs' :
30728                 case 'sm' :
30729                 case 'wide' :
30730                 case 'wide-thin' :
30731                     
30732                     box.push(item);
30733                     
30734                     break;
30735                 default :
30736                     break;
30737                     
30738             }
30739             
30740         }, this);
30741         
30742         if(box.length){
30743             boxes.push(box);
30744             box = [];
30745         }
30746         
30747         var filterPattern = function(box, length)
30748         {
30749             if(!box.length){
30750                 return;
30751             }
30752             
30753             var match = false;
30754             
30755             var pattern = box.slice(0, length);
30756             
30757             var format = [];
30758             
30759             Roo.each(pattern, function(i){
30760                 format.push(i.size);
30761             }, this);
30762             
30763             Roo.each(standard, function(s){
30764                 
30765                 if(String(s) != String(format)){
30766                     return;
30767                 }
30768                 
30769                 match = true;
30770                 return false;
30771                 
30772             }, this);
30773             
30774             if(!match && length == 1){
30775                 return;
30776             }
30777             
30778             if(!match){
30779                 filterPattern(box, length - 1);
30780                 return;
30781             }
30782                 
30783             queue.push(pattern);
30784
30785             box = box.slice(length, box.length);
30786
30787             filterPattern(box, 4);
30788
30789             return;
30790             
30791         }
30792         
30793         Roo.each(boxes, function(box, k){
30794             
30795             if(!box.length){
30796                 return;
30797             }
30798             
30799             if(box.length == 1){
30800                 queue.push(box);
30801                 return;
30802             }
30803             
30804             filterPattern(box, 4);
30805             
30806         }, this);
30807         
30808         
30809         var prune = [];
30810         
30811         var pos = this.el.getBox(true);
30812         
30813         var minX = pos.x;
30814         
30815         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30816         
30817         var hit_end = false;
30818         
30819         Roo.each(queue, function(box){
30820             
30821             if(hit_end){
30822                 
30823                 Roo.each(box, function(b){
30824                 
30825                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30826                     b.el.hide();
30827
30828                 }, this);
30829
30830                 return;
30831             }
30832             
30833             var mx = 0;
30834             
30835             Roo.each(box, function(b){
30836                 
30837                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30838                 b.el.show();
30839
30840                 mx = Math.max(mx, b.x);
30841                 
30842             }, this);
30843             
30844             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30845             
30846             if(maxX < minX){
30847                 
30848                 Roo.each(box, function(b){
30849                 
30850                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30851                     b.el.hide();
30852                     
30853                 }, this);
30854                 
30855                 hit_end = true;
30856                 
30857                 return;
30858             }
30859             
30860             prune.push(box);
30861             
30862         }, this);
30863         
30864         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30865     },
30866     
30867     /** Sets position of item in DOM
30868     * @param {Element} item
30869     * @param {Number} x - horizontal position
30870     * @param {Number} y - vertical position
30871     * @param {Boolean} isInstant - disables transitions
30872     */
30873     _processVerticalLayoutQueue : function( queue, isInstant )
30874     {
30875         var pos = this.el.getBox(true);
30876         var x = pos.x;
30877         var y = pos.y;
30878         var maxY = [];
30879         
30880         for (var i = 0; i < this.cols; i++){
30881             maxY[i] = pos.y;
30882         }
30883         
30884         Roo.each(queue, function(box, k){
30885             
30886             var col = k % this.cols;
30887             
30888             Roo.each(box, function(b,kk){
30889                 
30890                 b.el.position('absolute');
30891                 
30892                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30893                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30894                 
30895                 if(b.size == 'md-left' || b.size == 'md-right'){
30896                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30897                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30898                 }
30899                 
30900                 b.el.setWidth(width);
30901                 b.el.setHeight(height);
30902                 // iframe?
30903                 b.el.select('iframe',true).setSize(width,height);
30904                 
30905             }, this);
30906             
30907             for (var i = 0; i < this.cols; i++){
30908                 
30909                 if(maxY[i] < maxY[col]){
30910                     col = i;
30911                     continue;
30912                 }
30913                 
30914                 col = Math.min(col, i);
30915                 
30916             }
30917             
30918             x = pos.x + col * (this.colWidth + this.padWidth);
30919             
30920             y = maxY[col];
30921             
30922             var positions = [];
30923             
30924             switch (box.length){
30925                 case 1 :
30926                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30927                     break;
30928                 case 2 :
30929                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30930                     break;
30931                 case 3 :
30932                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30933                     break;
30934                 case 4 :
30935                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30936                     break;
30937                 default :
30938                     break;
30939             }
30940             
30941             Roo.each(box, function(b,kk){
30942                 
30943                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30944                 
30945                 var sz = b.el.getSize();
30946                 
30947                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30948                 
30949             }, this);
30950             
30951         }, this);
30952         
30953         var mY = 0;
30954         
30955         for (var i = 0; i < this.cols; i++){
30956             mY = Math.max(mY, maxY[i]);
30957         }
30958         
30959         this.el.setHeight(mY - pos.y);
30960         
30961     },
30962     
30963 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30964 //    {
30965 //        var pos = this.el.getBox(true);
30966 //        var x = pos.x;
30967 //        var y = pos.y;
30968 //        var maxX = pos.right;
30969 //        
30970 //        var maxHeight = 0;
30971 //        
30972 //        Roo.each(items, function(item, k){
30973 //            
30974 //            var c = k % 2;
30975 //            
30976 //            item.el.position('absolute');
30977 //                
30978 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30979 //
30980 //            item.el.setWidth(width);
30981 //
30982 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30983 //
30984 //            item.el.setHeight(height);
30985 //            
30986 //            if(c == 0){
30987 //                item.el.setXY([x, y], isInstant ? false : true);
30988 //            } else {
30989 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30990 //            }
30991 //            
30992 //            y = y + height + this.alternativePadWidth;
30993 //            
30994 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30995 //            
30996 //        }, this);
30997 //        
30998 //        this.el.setHeight(maxHeight);
30999 //        
31000 //    },
31001     
31002     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31003     {
31004         var pos = this.el.getBox(true);
31005         
31006         var minX = pos.x;
31007         var minY = pos.y;
31008         
31009         var maxX = pos.right;
31010         
31011         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31012         
31013         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31014         
31015         Roo.each(queue, function(box, k){
31016             
31017             Roo.each(box, function(b, kk){
31018                 
31019                 b.el.position('absolute');
31020                 
31021                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31022                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31023                 
31024                 if(b.size == 'md-left' || b.size == 'md-right'){
31025                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31026                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31027                 }
31028                 
31029                 b.el.setWidth(width);
31030                 b.el.setHeight(height);
31031                 
31032             }, this);
31033             
31034             if(!box.length){
31035                 return;
31036             }
31037             
31038             var positions = [];
31039             
31040             switch (box.length){
31041                 case 1 :
31042                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31043                     break;
31044                 case 2 :
31045                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31046                     break;
31047                 case 3 :
31048                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31049                     break;
31050                 case 4 :
31051                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31052                     break;
31053                 default :
31054                     break;
31055             }
31056             
31057             Roo.each(box, function(b,kk){
31058                 
31059                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31060                 
31061                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31062                 
31063             }, this);
31064             
31065         }, this);
31066         
31067     },
31068     
31069     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31070     {
31071         Roo.each(eItems, function(b,k){
31072             
31073             b.size = (k == 0) ? 'sm' : 'xs';
31074             b.x = (k == 0) ? 2 : 1;
31075             b.y = (k == 0) ? 2 : 1;
31076             
31077             b.el.position('absolute');
31078             
31079             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31080                 
31081             b.el.setWidth(width);
31082             
31083             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31084             
31085             b.el.setHeight(height);
31086             
31087         }, this);
31088
31089         var positions = [];
31090         
31091         positions.push({
31092             x : maxX - this.unitWidth * 2 - this.gutter,
31093             y : minY
31094         });
31095         
31096         positions.push({
31097             x : maxX - this.unitWidth,
31098             y : minY + (this.unitWidth + this.gutter) * 2
31099         });
31100         
31101         positions.push({
31102             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31103             y : minY
31104         });
31105         
31106         Roo.each(eItems, function(b,k){
31107             
31108             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31109
31110         }, this);
31111         
31112     },
31113     
31114     getVerticalOneBoxColPositions : function(x, y, box)
31115     {
31116         var pos = [];
31117         
31118         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31119         
31120         if(box[0].size == 'md-left'){
31121             rand = 0;
31122         }
31123         
31124         if(box[0].size == 'md-right'){
31125             rand = 1;
31126         }
31127         
31128         pos.push({
31129             x : x + (this.unitWidth + this.gutter) * rand,
31130             y : y
31131         });
31132         
31133         return pos;
31134     },
31135     
31136     getVerticalTwoBoxColPositions : function(x, y, box)
31137     {
31138         var pos = [];
31139         
31140         if(box[0].size == 'xs'){
31141             
31142             pos.push({
31143                 x : x,
31144                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31145             });
31146
31147             pos.push({
31148                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31149                 y : y
31150             });
31151             
31152             return pos;
31153             
31154         }
31155         
31156         pos.push({
31157             x : x,
31158             y : y
31159         });
31160
31161         pos.push({
31162             x : x + (this.unitWidth + this.gutter) * 2,
31163             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31164         });
31165         
31166         return pos;
31167         
31168     },
31169     
31170     getVerticalThreeBoxColPositions : function(x, y, box)
31171     {
31172         var pos = [];
31173         
31174         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31175             
31176             pos.push({
31177                 x : x,
31178                 y : y
31179             });
31180
31181             pos.push({
31182                 x : x + (this.unitWidth + this.gutter) * 1,
31183                 y : y
31184             });
31185             
31186             pos.push({
31187                 x : x + (this.unitWidth + this.gutter) * 2,
31188                 y : y
31189             });
31190             
31191             return pos;
31192             
31193         }
31194         
31195         if(box[0].size == 'xs' && box[1].size == 'xs'){
31196             
31197             pos.push({
31198                 x : x,
31199                 y : y
31200             });
31201
31202             pos.push({
31203                 x : x,
31204                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31205             });
31206             
31207             pos.push({
31208                 x : x + (this.unitWidth + this.gutter) * 1,
31209                 y : y
31210             });
31211             
31212             return pos;
31213             
31214         }
31215         
31216         pos.push({
31217             x : x,
31218             y : y
31219         });
31220
31221         pos.push({
31222             x : x + (this.unitWidth + this.gutter) * 2,
31223             y : y
31224         });
31225
31226         pos.push({
31227             x : x + (this.unitWidth + this.gutter) * 2,
31228             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31229         });
31230             
31231         return pos;
31232         
31233     },
31234     
31235     getVerticalFourBoxColPositions : function(x, y, box)
31236     {
31237         var pos = [];
31238         
31239         if(box[0].size == 'xs'){
31240             
31241             pos.push({
31242                 x : x,
31243                 y : y
31244             });
31245
31246             pos.push({
31247                 x : x,
31248                 y : y + (this.unitHeight + this.gutter) * 1
31249             });
31250             
31251             pos.push({
31252                 x : x,
31253                 y : y + (this.unitHeight + this.gutter) * 2
31254             });
31255             
31256             pos.push({
31257                 x : x + (this.unitWidth + this.gutter) * 1,
31258                 y : y
31259             });
31260             
31261             return pos;
31262             
31263         }
31264         
31265         pos.push({
31266             x : x,
31267             y : y
31268         });
31269
31270         pos.push({
31271             x : x + (this.unitWidth + this.gutter) * 2,
31272             y : y
31273         });
31274
31275         pos.push({
31276             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31277             y : y + (this.unitHeight + this.gutter) * 1
31278         });
31279
31280         pos.push({
31281             x : x + (this.unitWidth + this.gutter) * 2,
31282             y : y + (this.unitWidth + this.gutter) * 2
31283         });
31284
31285         return pos;
31286         
31287     },
31288     
31289     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31290     {
31291         var pos = [];
31292         
31293         if(box[0].size == 'md-left'){
31294             pos.push({
31295                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31296                 y : minY
31297             });
31298             
31299             return pos;
31300         }
31301         
31302         if(box[0].size == 'md-right'){
31303             pos.push({
31304                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31305                 y : minY + (this.unitWidth + this.gutter) * 1
31306             });
31307             
31308             return pos;
31309         }
31310         
31311         var rand = Math.floor(Math.random() * (4 - box[0].y));
31312         
31313         pos.push({
31314             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31315             y : minY + (this.unitWidth + this.gutter) * rand
31316         });
31317         
31318         return pos;
31319         
31320     },
31321     
31322     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31323     {
31324         var pos = [];
31325         
31326         if(box[0].size == 'xs'){
31327             
31328             pos.push({
31329                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31330                 y : minY
31331             });
31332
31333             pos.push({
31334                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31335                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31336             });
31337             
31338             return pos;
31339             
31340         }
31341         
31342         pos.push({
31343             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31344             y : minY
31345         });
31346
31347         pos.push({
31348             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31349             y : minY + (this.unitWidth + this.gutter) * 2
31350         });
31351         
31352         return pos;
31353         
31354     },
31355     
31356     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31357     {
31358         var pos = [];
31359         
31360         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31361             
31362             pos.push({
31363                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31364                 y : minY
31365             });
31366
31367             pos.push({
31368                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31369                 y : minY + (this.unitWidth + this.gutter) * 1
31370             });
31371             
31372             pos.push({
31373                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31374                 y : minY + (this.unitWidth + this.gutter) * 2
31375             });
31376             
31377             return pos;
31378             
31379         }
31380         
31381         if(box[0].size == 'xs' && box[1].size == 'xs'){
31382             
31383             pos.push({
31384                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31385                 y : minY
31386             });
31387
31388             pos.push({
31389                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31390                 y : minY
31391             });
31392             
31393             pos.push({
31394                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31395                 y : minY + (this.unitWidth + this.gutter) * 1
31396             });
31397             
31398             return pos;
31399             
31400         }
31401         
31402         pos.push({
31403             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31404             y : minY
31405         });
31406
31407         pos.push({
31408             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31409             y : minY + (this.unitWidth + this.gutter) * 2
31410         });
31411
31412         pos.push({
31413             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31414             y : minY + (this.unitWidth + this.gutter) * 2
31415         });
31416             
31417         return pos;
31418         
31419     },
31420     
31421     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31422     {
31423         var pos = [];
31424         
31425         if(box[0].size == 'xs'){
31426             
31427             pos.push({
31428                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31429                 y : minY
31430             });
31431
31432             pos.push({
31433                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31434                 y : minY
31435             });
31436             
31437             pos.push({
31438                 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),
31439                 y : minY
31440             });
31441             
31442             pos.push({
31443                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31444                 y : minY + (this.unitWidth + this.gutter) * 1
31445             });
31446             
31447             return pos;
31448             
31449         }
31450         
31451         pos.push({
31452             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31453             y : minY
31454         });
31455         
31456         pos.push({
31457             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31458             y : minY + (this.unitWidth + this.gutter) * 2
31459         });
31460         
31461         pos.push({
31462             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31463             y : minY + (this.unitWidth + this.gutter) * 2
31464         });
31465         
31466         pos.push({
31467             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),
31468             y : minY + (this.unitWidth + this.gutter) * 2
31469         });
31470
31471         return pos;
31472         
31473     },
31474     
31475     /**
31476     * remove a Masonry Brick
31477     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31478     */
31479     removeBrick : function(brick_id)
31480     {
31481         if (!brick_id) {
31482             return;
31483         }
31484         
31485         for (var i = 0; i<this.bricks.length; i++) {
31486             if (this.bricks[i].id == brick_id) {
31487                 this.bricks.splice(i,1);
31488                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31489                 this.initial();
31490             }
31491         }
31492     },
31493     
31494     /**
31495     * adds a Masonry Brick
31496     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31497     */
31498     addBrick : function(cfg)
31499     {
31500         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31501         //this.register(cn);
31502         cn.parentId = this.id;
31503         cn.onRender(this.el, null);
31504         return cn;
31505     },
31506     
31507     /**
31508     * register a Masonry Brick
31509     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31510     */
31511     
31512     register : function(brick)
31513     {
31514         this.bricks.push(brick);
31515         brick.masonryId = this.id;
31516     },
31517     
31518     /**
31519     * clear all the Masonry Brick
31520     */
31521     clearAll : function()
31522     {
31523         this.bricks = [];
31524         //this.getChildContainer().dom.innerHTML = "";
31525         this.el.dom.innerHTML = '';
31526     },
31527     
31528     getSelected : function()
31529     {
31530         if (!this.selectedBrick) {
31531             return false;
31532         }
31533         
31534         return this.selectedBrick;
31535     }
31536 });
31537
31538 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31539     
31540     groups: {},
31541      /**
31542     * register a Masonry Layout
31543     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31544     */
31545     
31546     register : function(layout)
31547     {
31548         this.groups[layout.id] = layout;
31549     },
31550     /**
31551     * fetch a  Masonry Layout based on the masonry layout ID
31552     * @param {string} the masonry layout to add
31553     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31554     */
31555     
31556     get: function(layout_id) {
31557         if (typeof(this.groups[layout_id]) == 'undefined') {
31558             return false;
31559         }
31560         return this.groups[layout_id] ;
31561     }
31562     
31563     
31564     
31565 });
31566
31567  
31568
31569  /**
31570  *
31571  * This is based on 
31572  * http://masonry.desandro.com
31573  *
31574  * The idea is to render all the bricks based on vertical width...
31575  *
31576  * The original code extends 'outlayer' - we might need to use that....
31577  * 
31578  */
31579
31580
31581 /**
31582  * @class Roo.bootstrap.LayoutMasonryAuto
31583  * @extends Roo.bootstrap.Component
31584  * Bootstrap Layout Masonry class
31585  * 
31586  * @constructor
31587  * Create a new Element
31588  * @param {Object} config The config object
31589  */
31590
31591 Roo.bootstrap.LayoutMasonryAuto = function(config){
31592     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31593 };
31594
31595 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31596     
31597       /**
31598      * @cfg {Boolean} isFitWidth  - resize the width..
31599      */   
31600     isFitWidth : false,  // options..
31601     /**
31602      * @cfg {Boolean} isOriginLeft = left align?
31603      */   
31604     isOriginLeft : true,
31605     /**
31606      * @cfg {Boolean} isOriginTop = top align?
31607      */   
31608     isOriginTop : false,
31609     /**
31610      * @cfg {Boolean} isLayoutInstant = no animation?
31611      */   
31612     isLayoutInstant : false, // needed?
31613     /**
31614      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31615      */   
31616     isResizingContainer : true,
31617     /**
31618      * @cfg {Number} columnWidth  width of the columns 
31619      */   
31620     
31621     columnWidth : 0,
31622     
31623     /**
31624      * @cfg {Number} maxCols maximum number of columns
31625      */   
31626     
31627     maxCols: 0,
31628     /**
31629      * @cfg {Number} padHeight padding below box..
31630      */   
31631     
31632     padHeight : 10, 
31633     
31634     /**
31635      * @cfg {Boolean} isAutoInitial defalut true
31636      */   
31637     
31638     isAutoInitial : true, 
31639     
31640     // private?
31641     gutter : 0,
31642     
31643     containerWidth: 0,
31644     initialColumnWidth : 0,
31645     currentSize : null,
31646     
31647     colYs : null, // array.
31648     maxY : 0,
31649     padWidth: 10,
31650     
31651     
31652     tag: 'div',
31653     cls: '',
31654     bricks: null, //CompositeElement
31655     cols : 0, // array?
31656     // element : null, // wrapped now this.el
31657     _isLayoutInited : null, 
31658     
31659     
31660     getAutoCreate : function(){
31661         
31662         var cfg = {
31663             tag: this.tag,
31664             cls: 'blog-masonary-wrapper ' + this.cls,
31665             cn : {
31666                 cls : 'mas-boxes masonary'
31667             }
31668         };
31669         
31670         return cfg;
31671     },
31672     
31673     getChildContainer: function( )
31674     {
31675         if (this.boxesEl) {
31676             return this.boxesEl;
31677         }
31678         
31679         this.boxesEl = this.el.select('.mas-boxes').first();
31680         
31681         return this.boxesEl;
31682     },
31683     
31684     
31685     initEvents : function()
31686     {
31687         var _this = this;
31688         
31689         if(this.isAutoInitial){
31690             Roo.log('hook children rendered');
31691             this.on('childrenrendered', function() {
31692                 Roo.log('children rendered');
31693                 _this.initial();
31694             } ,this);
31695         }
31696         
31697     },
31698     
31699     initial : function()
31700     {
31701         this.reloadItems();
31702
31703         this.currentSize = this.el.getBox(true);
31704
31705         /// was window resize... - let's see if this works..
31706         Roo.EventManager.onWindowResize(this.resize, this); 
31707
31708         if(!this.isAutoInitial){
31709             this.layout();
31710             return;
31711         }
31712         
31713         this.layout.defer(500,this);
31714     },
31715     
31716     reloadItems: function()
31717     {
31718         this.bricks = this.el.select('.masonry-brick', true);
31719         
31720         this.bricks.each(function(b) {
31721             //Roo.log(b.getSize());
31722             if (!b.attr('originalwidth')) {
31723                 b.attr('originalwidth',  b.getSize().width);
31724             }
31725             
31726         });
31727         
31728         Roo.log(this.bricks.elements.length);
31729     },
31730     
31731     resize : function()
31732     {
31733         Roo.log('resize');
31734         var cs = this.el.getBox(true);
31735         
31736         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31737             Roo.log("no change in with or X");
31738             return;
31739         }
31740         this.currentSize = cs;
31741         this.layout();
31742     },
31743     
31744     layout : function()
31745     {
31746          Roo.log('layout');
31747         this._resetLayout();
31748         //this._manageStamps();
31749       
31750         // don't animate first layout
31751         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31752         this.layoutItems( isInstant );
31753       
31754         // flag for initalized
31755         this._isLayoutInited = true;
31756     },
31757     
31758     layoutItems : function( isInstant )
31759     {
31760         //var items = this._getItemsForLayout( this.items );
31761         // original code supports filtering layout items.. we just ignore it..
31762         
31763         this._layoutItems( this.bricks , isInstant );
31764       
31765         this._postLayout();
31766     },
31767     _layoutItems : function ( items , isInstant)
31768     {
31769        //this.fireEvent( 'layout', this, items );
31770     
31771
31772         if ( !items || !items.elements.length ) {
31773           // no items, emit event with empty array
31774             return;
31775         }
31776
31777         var queue = [];
31778         items.each(function(item) {
31779             Roo.log("layout item");
31780             Roo.log(item);
31781             // get x/y object from method
31782             var position = this._getItemLayoutPosition( item );
31783             // enqueue
31784             position.item = item;
31785             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31786             queue.push( position );
31787         }, this);
31788       
31789         this._processLayoutQueue( queue );
31790     },
31791     /** Sets position of item in DOM
31792     * @param {Element} item
31793     * @param {Number} x - horizontal position
31794     * @param {Number} y - vertical position
31795     * @param {Boolean} isInstant - disables transitions
31796     */
31797     _processLayoutQueue : function( queue )
31798     {
31799         for ( var i=0, len = queue.length; i < len; i++ ) {
31800             var obj = queue[i];
31801             obj.item.position('absolute');
31802             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31803         }
31804     },
31805       
31806     
31807     /**
31808     * Any logic you want to do after each layout,
31809     * i.e. size the container
31810     */
31811     _postLayout : function()
31812     {
31813         this.resizeContainer();
31814     },
31815     
31816     resizeContainer : function()
31817     {
31818         if ( !this.isResizingContainer ) {
31819             return;
31820         }
31821         var size = this._getContainerSize();
31822         if ( size ) {
31823             this.el.setSize(size.width,size.height);
31824             this.boxesEl.setSize(size.width,size.height);
31825         }
31826     },
31827     
31828     
31829     
31830     _resetLayout : function()
31831     {
31832         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31833         this.colWidth = this.el.getWidth();
31834         //this.gutter = this.el.getWidth(); 
31835         
31836         this.measureColumns();
31837
31838         // reset column Y
31839         var i = this.cols;
31840         this.colYs = [];
31841         while (i--) {
31842             this.colYs.push( 0 );
31843         }
31844     
31845         this.maxY = 0;
31846     },
31847
31848     measureColumns : function()
31849     {
31850         this.getContainerWidth();
31851       // if columnWidth is 0, default to outerWidth of first item
31852         if ( !this.columnWidth ) {
31853             var firstItem = this.bricks.first();
31854             Roo.log(firstItem);
31855             this.columnWidth  = this.containerWidth;
31856             if (firstItem && firstItem.attr('originalwidth') ) {
31857                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31858             }
31859             // columnWidth fall back to item of first element
31860             Roo.log("set column width?");
31861                         this.initialColumnWidth = this.columnWidth  ;
31862
31863             // if first elem has no width, default to size of container
31864             
31865         }
31866         
31867         
31868         if (this.initialColumnWidth) {
31869             this.columnWidth = this.initialColumnWidth;
31870         }
31871         
31872         
31873             
31874         // column width is fixed at the top - however if container width get's smaller we should
31875         // reduce it...
31876         
31877         // this bit calcs how man columns..
31878             
31879         var columnWidth = this.columnWidth += this.gutter;
31880       
31881         // calculate columns
31882         var containerWidth = this.containerWidth + this.gutter;
31883         
31884         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31885         // fix rounding errors, typically with gutters
31886         var excess = columnWidth - containerWidth % columnWidth;
31887         
31888         
31889         // if overshoot is less than a pixel, round up, otherwise floor it
31890         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31891         cols = Math[ mathMethod ]( cols );
31892         this.cols = Math.max( cols, 1 );
31893         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31894         
31895          // padding positioning..
31896         var totalColWidth = this.cols * this.columnWidth;
31897         var padavail = this.containerWidth - totalColWidth;
31898         // so for 2 columns - we need 3 'pads'
31899         
31900         var padNeeded = (1+this.cols) * this.padWidth;
31901         
31902         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31903         
31904         this.columnWidth += padExtra
31905         //this.padWidth = Math.floor(padavail /  ( this.cols));
31906         
31907         // adjust colum width so that padding is fixed??
31908         
31909         // we have 3 columns ... total = width * 3
31910         // we have X left over... that should be used by 
31911         
31912         //if (this.expandC) {
31913             
31914         //}
31915         
31916         
31917         
31918     },
31919     
31920     getContainerWidth : function()
31921     {
31922        /* // container is parent if fit width
31923         var container = this.isFitWidth ? this.element.parentNode : this.element;
31924         // check that this.size and size are there
31925         // IE8 triggers resize on body size change, so they might not be
31926         
31927         var size = getSize( container );  //FIXME
31928         this.containerWidth = size && size.innerWidth; //FIXME
31929         */
31930          
31931         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31932         
31933     },
31934     
31935     _getItemLayoutPosition : function( item )  // what is item?
31936     {
31937         // we resize the item to our columnWidth..
31938       
31939         item.setWidth(this.columnWidth);
31940         item.autoBoxAdjust  = false;
31941         
31942         var sz = item.getSize();
31943  
31944         // how many columns does this brick span
31945         var remainder = this.containerWidth % this.columnWidth;
31946         
31947         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31948         // round if off by 1 pixel, otherwise use ceil
31949         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31950         colSpan = Math.min( colSpan, this.cols );
31951         
31952         // normally this should be '1' as we dont' currently allow multi width columns..
31953         
31954         var colGroup = this._getColGroup( colSpan );
31955         // get the minimum Y value from the columns
31956         var minimumY = Math.min.apply( Math, colGroup );
31957         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31958         
31959         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31960          
31961         // position the brick
31962         var position = {
31963             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31964             y: this.currentSize.y + minimumY + this.padHeight
31965         };
31966         
31967         Roo.log(position);
31968         // apply setHeight to necessary columns
31969         var setHeight = minimumY + sz.height + this.padHeight;
31970         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31971         
31972         var setSpan = this.cols + 1 - colGroup.length;
31973         for ( var i = 0; i < setSpan; i++ ) {
31974           this.colYs[ shortColIndex + i ] = setHeight ;
31975         }
31976       
31977         return position;
31978     },
31979     
31980     /**
31981      * @param {Number} colSpan - number of columns the element spans
31982      * @returns {Array} colGroup
31983      */
31984     _getColGroup : function( colSpan )
31985     {
31986         if ( colSpan < 2 ) {
31987           // if brick spans only one column, use all the column Ys
31988           return this.colYs;
31989         }
31990       
31991         var colGroup = [];
31992         // how many different places could this brick fit horizontally
31993         var groupCount = this.cols + 1 - colSpan;
31994         // for each group potential horizontal position
31995         for ( var i = 0; i < groupCount; i++ ) {
31996           // make an array of colY values for that one group
31997           var groupColYs = this.colYs.slice( i, i + colSpan );
31998           // and get the max value of the array
31999           colGroup[i] = Math.max.apply( Math, groupColYs );
32000         }
32001         return colGroup;
32002     },
32003     /*
32004     _manageStamp : function( stamp )
32005     {
32006         var stampSize =  stamp.getSize();
32007         var offset = stamp.getBox();
32008         // get the columns that this stamp affects
32009         var firstX = this.isOriginLeft ? offset.x : offset.right;
32010         var lastX = firstX + stampSize.width;
32011         var firstCol = Math.floor( firstX / this.columnWidth );
32012         firstCol = Math.max( 0, firstCol );
32013         
32014         var lastCol = Math.floor( lastX / this.columnWidth );
32015         // lastCol should not go over if multiple of columnWidth #425
32016         lastCol -= lastX % this.columnWidth ? 0 : 1;
32017         lastCol = Math.min( this.cols - 1, lastCol );
32018         
32019         // set colYs to bottom of the stamp
32020         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32021             stampSize.height;
32022             
32023         for ( var i = firstCol; i <= lastCol; i++ ) {
32024           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32025         }
32026     },
32027     */
32028     
32029     _getContainerSize : function()
32030     {
32031         this.maxY = Math.max.apply( Math, this.colYs );
32032         var size = {
32033             height: this.maxY
32034         };
32035       
32036         if ( this.isFitWidth ) {
32037             size.width = this._getContainerFitWidth();
32038         }
32039       
32040         return size;
32041     },
32042     
32043     _getContainerFitWidth : function()
32044     {
32045         var unusedCols = 0;
32046         // count unused columns
32047         var i = this.cols;
32048         while ( --i ) {
32049           if ( this.colYs[i] !== 0 ) {
32050             break;
32051           }
32052           unusedCols++;
32053         }
32054         // fit container to columns that have been used
32055         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32056     },
32057     
32058     needsResizeLayout : function()
32059     {
32060         var previousWidth = this.containerWidth;
32061         this.getContainerWidth();
32062         return previousWidth !== this.containerWidth;
32063     }
32064  
32065 });
32066
32067  
32068
32069  /*
32070  * - LGPL
32071  *
32072  * element
32073  * 
32074  */
32075
32076 /**
32077  * @class Roo.bootstrap.MasonryBrick
32078  * @extends Roo.bootstrap.Component
32079  * Bootstrap MasonryBrick class
32080  * 
32081  * @constructor
32082  * Create a new MasonryBrick
32083  * @param {Object} config The config object
32084  */
32085
32086 Roo.bootstrap.MasonryBrick = function(config){
32087     
32088     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32089     
32090     Roo.bootstrap.MasonryBrick.register(this);
32091     
32092     this.addEvents({
32093         // raw events
32094         /**
32095          * @event click
32096          * When a MasonryBrick is clcik
32097          * @param {Roo.bootstrap.MasonryBrick} this
32098          * @param {Roo.EventObject} e
32099          */
32100         "click" : true
32101     });
32102 };
32103
32104 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32105     
32106     /**
32107      * @cfg {String} title
32108      */   
32109     title : '',
32110     /**
32111      * @cfg {String} html
32112      */   
32113     html : '',
32114     /**
32115      * @cfg {String} bgimage
32116      */   
32117     bgimage : '',
32118     /**
32119      * @cfg {String} videourl
32120      */   
32121     videourl : '',
32122     /**
32123      * @cfg {String} cls
32124      */   
32125     cls : '',
32126     /**
32127      * @cfg {String} href
32128      */   
32129     href : '',
32130     /**
32131      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32132      */   
32133     size : 'xs',
32134     
32135     /**
32136      * @cfg {String} placetitle (center|bottom)
32137      */   
32138     placetitle : '',
32139     
32140     /**
32141      * @cfg {Boolean} isFitContainer defalut true
32142      */   
32143     isFitContainer : true, 
32144     
32145     /**
32146      * @cfg {Boolean} preventDefault defalut false
32147      */   
32148     preventDefault : false, 
32149     
32150     /**
32151      * @cfg {Boolean} inverse defalut false
32152      */   
32153     maskInverse : false, 
32154     
32155     getAutoCreate : function()
32156     {
32157         if(!this.isFitContainer){
32158             return this.getSplitAutoCreate();
32159         }
32160         
32161         var cls = 'masonry-brick masonry-brick-full';
32162         
32163         if(this.href.length){
32164             cls += ' masonry-brick-link';
32165         }
32166         
32167         if(this.bgimage.length){
32168             cls += ' masonry-brick-image';
32169         }
32170         
32171         if(this.maskInverse){
32172             cls += ' mask-inverse';
32173         }
32174         
32175         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32176             cls += ' enable-mask';
32177         }
32178         
32179         if(this.size){
32180             cls += ' masonry-' + this.size + '-brick';
32181         }
32182         
32183         if(this.placetitle.length){
32184             
32185             switch (this.placetitle) {
32186                 case 'center' :
32187                     cls += ' masonry-center-title';
32188                     break;
32189                 case 'bottom' :
32190                     cls += ' masonry-bottom-title';
32191                     break;
32192                 default:
32193                     break;
32194             }
32195             
32196         } else {
32197             if(!this.html.length && !this.bgimage.length){
32198                 cls += ' masonry-center-title';
32199             }
32200
32201             if(!this.html.length && this.bgimage.length){
32202                 cls += ' masonry-bottom-title';
32203             }
32204         }
32205         
32206         if(this.cls){
32207             cls += ' ' + this.cls;
32208         }
32209         
32210         var cfg = {
32211             tag: (this.href.length) ? 'a' : 'div',
32212             cls: cls,
32213             cn: [
32214                 {
32215                     tag: 'div',
32216                     cls: 'masonry-brick-mask'
32217                 },
32218                 {
32219                     tag: 'div',
32220                     cls: 'masonry-brick-paragraph',
32221                     cn: []
32222                 }
32223             ]
32224         };
32225         
32226         if(this.href.length){
32227             cfg.href = this.href;
32228         }
32229         
32230         var cn = cfg.cn[1].cn;
32231         
32232         if(this.title.length){
32233             cn.push({
32234                 tag: 'h4',
32235                 cls: 'masonry-brick-title',
32236                 html: this.title
32237             });
32238         }
32239         
32240         if(this.html.length){
32241             cn.push({
32242                 tag: 'p',
32243                 cls: 'masonry-brick-text',
32244                 html: this.html
32245             });
32246         }
32247         
32248         if (!this.title.length && !this.html.length) {
32249             cfg.cn[1].cls += ' hide';
32250         }
32251         
32252         if(this.bgimage.length){
32253             cfg.cn.push({
32254                 tag: 'img',
32255                 cls: 'masonry-brick-image-view',
32256                 src: this.bgimage
32257             });
32258         }
32259         
32260         if(this.videourl.length){
32261             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32262             // youtube support only?
32263             cfg.cn.push({
32264                 tag: 'iframe',
32265                 cls: 'masonry-brick-image-view',
32266                 src: vurl,
32267                 frameborder : 0,
32268                 allowfullscreen : true
32269             });
32270         }
32271         
32272         return cfg;
32273         
32274     },
32275     
32276     getSplitAutoCreate : function()
32277     {
32278         var cls = 'masonry-brick masonry-brick-split';
32279         
32280         if(this.href.length){
32281             cls += ' masonry-brick-link';
32282         }
32283         
32284         if(this.bgimage.length){
32285             cls += ' masonry-brick-image';
32286         }
32287         
32288         if(this.size){
32289             cls += ' masonry-' + this.size + '-brick';
32290         }
32291         
32292         switch (this.placetitle) {
32293             case 'center' :
32294                 cls += ' masonry-center-title';
32295                 break;
32296             case 'bottom' :
32297                 cls += ' masonry-bottom-title';
32298                 break;
32299             default:
32300                 if(!this.bgimage.length){
32301                     cls += ' masonry-center-title';
32302                 }
32303
32304                 if(this.bgimage.length){
32305                     cls += ' masonry-bottom-title';
32306                 }
32307                 break;
32308         }
32309         
32310         if(this.cls){
32311             cls += ' ' + this.cls;
32312         }
32313         
32314         var cfg = {
32315             tag: (this.href.length) ? 'a' : 'div',
32316             cls: cls,
32317             cn: [
32318                 {
32319                     tag: 'div',
32320                     cls: 'masonry-brick-split-head',
32321                     cn: [
32322                         {
32323                             tag: 'div',
32324                             cls: 'masonry-brick-paragraph',
32325                             cn: []
32326                         }
32327                     ]
32328                 },
32329                 {
32330                     tag: 'div',
32331                     cls: 'masonry-brick-split-body',
32332                     cn: []
32333                 }
32334             ]
32335         };
32336         
32337         if(this.href.length){
32338             cfg.href = this.href;
32339         }
32340         
32341         if(this.title.length){
32342             cfg.cn[0].cn[0].cn.push({
32343                 tag: 'h4',
32344                 cls: 'masonry-brick-title',
32345                 html: this.title
32346             });
32347         }
32348         
32349         if(this.html.length){
32350             cfg.cn[1].cn.push({
32351                 tag: 'p',
32352                 cls: 'masonry-brick-text',
32353                 html: this.html
32354             });
32355         }
32356
32357         if(this.bgimage.length){
32358             cfg.cn[0].cn.push({
32359                 tag: 'img',
32360                 cls: 'masonry-brick-image-view',
32361                 src: this.bgimage
32362             });
32363         }
32364         
32365         if(this.videourl.length){
32366             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32367             // youtube support only?
32368             cfg.cn[0].cn.cn.push({
32369                 tag: 'iframe',
32370                 cls: 'masonry-brick-image-view',
32371                 src: vurl,
32372                 frameborder : 0,
32373                 allowfullscreen : true
32374             });
32375         }
32376         
32377         return cfg;
32378     },
32379     
32380     initEvents: function() 
32381     {
32382         switch (this.size) {
32383             case 'xs' :
32384                 this.x = 1;
32385                 this.y = 1;
32386                 break;
32387             case 'sm' :
32388                 this.x = 2;
32389                 this.y = 2;
32390                 break;
32391             case 'md' :
32392             case 'md-left' :
32393             case 'md-right' :
32394                 this.x = 3;
32395                 this.y = 3;
32396                 break;
32397             case 'tall' :
32398                 this.x = 2;
32399                 this.y = 3;
32400                 break;
32401             case 'wide' :
32402                 this.x = 3;
32403                 this.y = 2;
32404                 break;
32405             case 'wide-thin' :
32406                 this.x = 3;
32407                 this.y = 1;
32408                 break;
32409                         
32410             default :
32411                 break;
32412         }
32413         
32414         if(Roo.isTouch){
32415             this.el.on('touchstart', this.onTouchStart, this);
32416             this.el.on('touchmove', this.onTouchMove, this);
32417             this.el.on('touchend', this.onTouchEnd, this);
32418             this.el.on('contextmenu', this.onContextMenu, this);
32419         } else {
32420             this.el.on('mouseenter'  ,this.enter, this);
32421             this.el.on('mouseleave', this.leave, this);
32422             this.el.on('click', this.onClick, this);
32423         }
32424         
32425         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32426             this.parent().bricks.push(this);   
32427         }
32428         
32429     },
32430     
32431     onClick: function(e, el)
32432     {
32433         var time = this.endTimer - this.startTimer;
32434         // Roo.log(e.preventDefault());
32435         if(Roo.isTouch){
32436             if(time > 1000){
32437                 e.preventDefault();
32438                 return;
32439             }
32440         }
32441         
32442         if(!this.preventDefault){
32443             return;
32444         }
32445         
32446         e.preventDefault();
32447         
32448         if (this.activcClass != '') {
32449             this.selectBrick();
32450         }
32451         
32452         this.fireEvent('click', this);
32453     },
32454     
32455     enter: function(e, el)
32456     {
32457         e.preventDefault();
32458         
32459         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32460             return;
32461         }
32462         
32463         if(this.bgimage.length && this.html.length){
32464             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32465         }
32466     },
32467     
32468     leave: function(e, el)
32469     {
32470         e.preventDefault();
32471         
32472         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32473             return;
32474         }
32475         
32476         if(this.bgimage.length && this.html.length){
32477             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32478         }
32479     },
32480     
32481     onTouchStart: function(e, el)
32482     {
32483 //        e.preventDefault();
32484         
32485         this.touchmoved = false;
32486         
32487         if(!this.isFitContainer){
32488             return;
32489         }
32490         
32491         if(!this.bgimage.length || !this.html.length){
32492             return;
32493         }
32494         
32495         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32496         
32497         this.timer = new Date().getTime();
32498         
32499     },
32500     
32501     onTouchMove: function(e, el)
32502     {
32503         this.touchmoved = true;
32504     },
32505     
32506     onContextMenu : function(e,el)
32507     {
32508         e.preventDefault();
32509         e.stopPropagation();
32510         return false;
32511     },
32512     
32513     onTouchEnd: function(e, el)
32514     {
32515 //        e.preventDefault();
32516         
32517         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32518         
32519             this.leave(e,el);
32520             
32521             return;
32522         }
32523         
32524         if(!this.bgimage.length || !this.html.length){
32525             
32526             if(this.href.length){
32527                 window.location.href = this.href;
32528             }
32529             
32530             return;
32531         }
32532         
32533         if(!this.isFitContainer){
32534             return;
32535         }
32536         
32537         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32538         
32539         window.location.href = this.href;
32540     },
32541     
32542     //selection on single brick only
32543     selectBrick : function() {
32544         
32545         if (!this.parentId) {
32546             return;
32547         }
32548         
32549         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32550         var index = m.selectedBrick.indexOf(this.id);
32551         
32552         if ( index > -1) {
32553             m.selectedBrick.splice(index,1);
32554             this.el.removeClass(this.activeClass);
32555             return;
32556         }
32557         
32558         for(var i = 0; i < m.selectedBrick.length; i++) {
32559             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32560             b.el.removeClass(b.activeClass);
32561         }
32562         
32563         m.selectedBrick = [];
32564         
32565         m.selectedBrick.push(this.id);
32566         this.el.addClass(this.activeClass);
32567         return;
32568     }
32569     
32570 });
32571
32572 Roo.apply(Roo.bootstrap.MasonryBrick, {
32573     
32574     //groups: {},
32575     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32576      /**
32577     * register a Masonry Brick
32578     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32579     */
32580     
32581     register : function(brick)
32582     {
32583         //this.groups[brick.id] = brick;
32584         this.groups.add(brick.id, brick);
32585     },
32586     /**
32587     * fetch a  masonry brick based on the masonry brick ID
32588     * @param {string} the masonry brick to add
32589     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32590     */
32591     
32592     get: function(brick_id) 
32593     {
32594         // if (typeof(this.groups[brick_id]) == 'undefined') {
32595         //     return false;
32596         // }
32597         // return this.groups[brick_id] ;
32598         
32599         if(this.groups.key(brick_id)) {
32600             return this.groups.key(brick_id);
32601         }
32602         
32603         return false;
32604     }
32605     
32606     
32607     
32608 });
32609
32610  /*
32611  * - LGPL
32612  *
32613  * element
32614  * 
32615  */
32616
32617 /**
32618  * @class Roo.bootstrap.Brick
32619  * @extends Roo.bootstrap.Component
32620  * Bootstrap Brick class
32621  * 
32622  * @constructor
32623  * Create a new Brick
32624  * @param {Object} config The config object
32625  */
32626
32627 Roo.bootstrap.Brick = function(config){
32628     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32629     
32630     this.addEvents({
32631         // raw events
32632         /**
32633          * @event click
32634          * When a Brick is click
32635          * @param {Roo.bootstrap.Brick} this
32636          * @param {Roo.EventObject} e
32637          */
32638         "click" : true
32639     });
32640 };
32641
32642 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32643     
32644     /**
32645      * @cfg {String} title
32646      */   
32647     title : '',
32648     /**
32649      * @cfg {String} html
32650      */   
32651     html : '',
32652     /**
32653      * @cfg {String} bgimage
32654      */   
32655     bgimage : '',
32656     /**
32657      * @cfg {String} cls
32658      */   
32659     cls : '',
32660     /**
32661      * @cfg {String} href
32662      */   
32663     href : '',
32664     /**
32665      * @cfg {String} video
32666      */   
32667     video : '',
32668     /**
32669      * @cfg {Boolean} square
32670      */   
32671     square : true,
32672     
32673     getAutoCreate : function()
32674     {
32675         var cls = 'roo-brick';
32676         
32677         if(this.href.length){
32678             cls += ' roo-brick-link';
32679         }
32680         
32681         if(this.bgimage.length){
32682             cls += ' roo-brick-image';
32683         }
32684         
32685         if(!this.html.length && !this.bgimage.length){
32686             cls += ' roo-brick-center-title';
32687         }
32688         
32689         if(!this.html.length && this.bgimage.length){
32690             cls += ' roo-brick-bottom-title';
32691         }
32692         
32693         if(this.cls){
32694             cls += ' ' + this.cls;
32695         }
32696         
32697         var cfg = {
32698             tag: (this.href.length) ? 'a' : 'div',
32699             cls: cls,
32700             cn: [
32701                 {
32702                     tag: 'div',
32703                     cls: 'roo-brick-paragraph',
32704                     cn: []
32705                 }
32706             ]
32707         };
32708         
32709         if(this.href.length){
32710             cfg.href = this.href;
32711         }
32712         
32713         var cn = cfg.cn[0].cn;
32714         
32715         if(this.title.length){
32716             cn.push({
32717                 tag: 'h4',
32718                 cls: 'roo-brick-title',
32719                 html: this.title
32720             });
32721         }
32722         
32723         if(this.html.length){
32724             cn.push({
32725                 tag: 'p',
32726                 cls: 'roo-brick-text',
32727                 html: this.html
32728             });
32729         } else {
32730             cn.cls += ' hide';
32731         }
32732         
32733         if(this.bgimage.length){
32734             cfg.cn.push({
32735                 tag: 'img',
32736                 cls: 'roo-brick-image-view',
32737                 src: this.bgimage
32738             });
32739         }
32740         
32741         return cfg;
32742     },
32743     
32744     initEvents: function() 
32745     {
32746         if(this.title.length || this.html.length){
32747             this.el.on('mouseenter'  ,this.enter, this);
32748             this.el.on('mouseleave', this.leave, this);
32749         }
32750         
32751         Roo.EventManager.onWindowResize(this.resize, this); 
32752         
32753         if(this.bgimage.length){
32754             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32755             this.imageEl.on('load', this.onImageLoad, this);
32756             return;
32757         }
32758         
32759         this.resize();
32760     },
32761     
32762     onImageLoad : function()
32763     {
32764         this.resize();
32765     },
32766     
32767     resize : function()
32768     {
32769         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32770         
32771         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32772         
32773         if(this.bgimage.length){
32774             var image = this.el.select('.roo-brick-image-view', true).first();
32775             
32776             image.setWidth(paragraph.getWidth());
32777             
32778             if(this.square){
32779                 image.setHeight(paragraph.getWidth());
32780             }
32781             
32782             this.el.setHeight(image.getHeight());
32783             paragraph.setHeight(image.getHeight());
32784             
32785         }
32786         
32787     },
32788     
32789     enter: function(e, el)
32790     {
32791         e.preventDefault();
32792         
32793         if(this.bgimage.length){
32794             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32795             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32796         }
32797     },
32798     
32799     leave: function(e, el)
32800     {
32801         e.preventDefault();
32802         
32803         if(this.bgimage.length){
32804             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32805             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32806         }
32807     }
32808     
32809 });
32810
32811  
32812
32813  /*
32814  * - LGPL
32815  *
32816  * Input
32817  * 
32818  */
32819
32820 /**
32821  * @class Roo.bootstrap.NumberField
32822  * @extends Roo.bootstrap.Input
32823  * Bootstrap NumberField class
32824  * 
32825  * 
32826  * 
32827  * 
32828  * @constructor
32829  * Create a new NumberField
32830  * @param {Object} config The config object
32831  */
32832
32833 Roo.bootstrap.NumberField = function(config){
32834     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32835 };
32836
32837 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32838     
32839     /**
32840      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32841      */
32842     allowDecimals : true,
32843     /**
32844      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32845      */
32846     decimalSeparator : ".",
32847     /**
32848      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32849      */
32850     decimalPrecision : 2,
32851     /**
32852      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32853      */
32854     allowNegative : true,
32855     /**
32856      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32857      */
32858     minValue : Number.NEGATIVE_INFINITY,
32859     /**
32860      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32861      */
32862     maxValue : Number.MAX_VALUE,
32863     /**
32864      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32865      */
32866     minText : "The minimum value for this field is {0}",
32867     /**
32868      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32869      */
32870     maxText : "The maximum value for this field is {0}",
32871     /**
32872      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32873      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32874      */
32875     nanText : "{0} is not a valid number",
32876     /**
32877      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32878      */
32879     castInt : true,
32880
32881     // private
32882     initEvents : function()
32883     {   
32884         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32885         
32886         var allowed = "0123456789";
32887         
32888         if(this.allowDecimals){
32889             allowed += this.decimalSeparator;
32890         }
32891         
32892         if(this.allowNegative){
32893             allowed += "-";
32894         }
32895         
32896         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32897         
32898         var keyPress = function(e){
32899             
32900             var k = e.getKey();
32901             
32902             var c = e.getCharCode();
32903             
32904             if(
32905                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32906                     allowed.indexOf(String.fromCharCode(c)) === -1
32907             ){
32908                 e.stopEvent();
32909                 return;
32910             }
32911             
32912             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32913                 return;
32914             }
32915             
32916             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32917                 e.stopEvent();
32918             }
32919         };
32920         
32921         this.el.on("keypress", keyPress, this);
32922     },
32923     
32924     validateValue : function(value)
32925     {
32926         
32927         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32928             return false;
32929         }
32930         
32931         var num = this.parseValue(value);
32932         
32933         if(isNaN(num)){
32934             this.markInvalid(String.format(this.nanText, value));
32935             return false;
32936         }
32937         
32938         if(num < this.minValue){
32939             this.markInvalid(String.format(this.minText, this.minValue));
32940             return false;
32941         }
32942         
32943         if(num > this.maxValue){
32944             this.markInvalid(String.format(this.maxText, this.maxValue));
32945             return false;
32946         }
32947         
32948         return true;
32949     },
32950
32951     getValue : function()
32952     {
32953         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32954     },
32955
32956     parseValue : function(value)
32957     {
32958         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32959         return isNaN(value) ? '' : value;
32960     },
32961
32962     fixPrecision : function(value)
32963     {
32964         var nan = isNaN(value);
32965         
32966         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32967             return nan ? '' : value;
32968         }
32969         return parseFloat(value).toFixed(this.decimalPrecision);
32970     },
32971
32972     setValue : function(v)
32973     {
32974         v = this.fixPrecision(v);
32975         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32976     },
32977
32978     decimalPrecisionFcn : function(v)
32979     {
32980         return Math.floor(v);
32981     },
32982
32983     beforeBlur : function()
32984     {
32985         if(!this.castInt){
32986             return;
32987         }
32988         
32989         var v = this.parseValue(this.getRawValue());
32990         if(v){
32991             this.setValue(v);
32992         }
32993     }
32994     
32995 });
32996
32997  
32998
32999 /*
33000 * Licence: LGPL
33001 */
33002
33003 /**
33004  * @class Roo.bootstrap.DocumentSlider
33005  * @extends Roo.bootstrap.Component
33006  * Bootstrap DocumentSlider class
33007  * 
33008  * @constructor
33009  * Create a new DocumentViewer
33010  * @param {Object} config The config object
33011  */
33012
33013 Roo.bootstrap.DocumentSlider = function(config){
33014     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33015     
33016     this.files = [];
33017     
33018     this.addEvents({
33019         /**
33020          * @event initial
33021          * Fire after initEvent
33022          * @param {Roo.bootstrap.DocumentSlider} this
33023          */
33024         "initial" : true,
33025         /**
33026          * @event update
33027          * Fire after update
33028          * @param {Roo.bootstrap.DocumentSlider} this
33029          */
33030         "update" : true,
33031         /**
33032          * @event click
33033          * Fire after click
33034          * @param {Roo.bootstrap.DocumentSlider} this
33035          */
33036         "click" : true
33037     });
33038 };
33039
33040 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33041     
33042     files : false,
33043     
33044     indicator : 0,
33045     
33046     getAutoCreate : function()
33047     {
33048         var cfg = {
33049             tag : 'div',
33050             cls : 'roo-document-slider',
33051             cn : [
33052                 {
33053                     tag : 'div',
33054                     cls : 'roo-document-slider-header',
33055                     cn : [
33056                         {
33057                             tag : 'div',
33058                             cls : 'roo-document-slider-header-title'
33059                         }
33060                     ]
33061                 },
33062                 {
33063                     tag : 'div',
33064                     cls : 'roo-document-slider-body',
33065                     cn : [
33066                         {
33067                             tag : 'div',
33068                             cls : 'roo-document-slider-prev',
33069                             cn : [
33070                                 {
33071                                     tag : 'i',
33072                                     cls : 'fa fa-chevron-left'
33073                                 }
33074                             ]
33075                         },
33076                         {
33077                             tag : 'div',
33078                             cls : 'roo-document-slider-thumb',
33079                             cn : [
33080                                 {
33081                                     tag : 'img',
33082                                     cls : 'roo-document-slider-image'
33083                                 }
33084                             ]
33085                         },
33086                         {
33087                             tag : 'div',
33088                             cls : 'roo-document-slider-next',
33089                             cn : [
33090                                 {
33091                                     tag : 'i',
33092                                     cls : 'fa fa-chevron-right'
33093                                 }
33094                             ]
33095                         }
33096                     ]
33097                 }
33098             ]
33099         };
33100         
33101         return cfg;
33102     },
33103     
33104     initEvents : function()
33105     {
33106         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33107         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33108         
33109         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33110         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33111         
33112         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33113         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33114         
33115         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33116         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33117         
33118         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33119         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33120         
33121         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33122         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33123         
33124         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33125         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33126         
33127         this.thumbEl.on('click', this.onClick, this);
33128         
33129         this.prevIndicator.on('click', this.prev, this);
33130         
33131         this.nextIndicator.on('click', this.next, this);
33132         
33133     },
33134     
33135     initial : function()
33136     {
33137         if(this.files.length){
33138             this.indicator = 1;
33139             this.update()
33140         }
33141         
33142         this.fireEvent('initial', this);
33143     },
33144     
33145     update : function()
33146     {
33147         this.imageEl.attr('src', this.files[this.indicator - 1]);
33148         
33149         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33150         
33151         this.prevIndicator.show();
33152         
33153         if(this.indicator == 1){
33154             this.prevIndicator.hide();
33155         }
33156         
33157         this.nextIndicator.show();
33158         
33159         if(this.indicator == this.files.length){
33160             this.nextIndicator.hide();
33161         }
33162         
33163         this.thumbEl.scrollTo('top');
33164         
33165         this.fireEvent('update', this);
33166     },
33167     
33168     onClick : function(e)
33169     {
33170         e.preventDefault();
33171         
33172         this.fireEvent('click', this);
33173     },
33174     
33175     prev : function(e)
33176     {
33177         e.preventDefault();
33178         
33179         this.indicator = Math.max(1, this.indicator - 1);
33180         
33181         this.update();
33182     },
33183     
33184     next : function(e)
33185     {
33186         e.preventDefault();
33187         
33188         this.indicator = Math.min(this.files.length, this.indicator + 1);
33189         
33190         this.update();
33191     }
33192 });
33193 /*
33194  * - LGPL
33195  *
33196  * RadioSet
33197  *
33198  *
33199  */
33200
33201 /**
33202  * @class Roo.bootstrap.RadioSet
33203  * @extends Roo.bootstrap.Input
33204  * Bootstrap RadioSet class
33205  * @cfg {String} indicatorpos (left|right) default left
33206  * @cfg {Boolean} inline (true|false) inline the element (default true)
33207  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33208  * @constructor
33209  * Create a new RadioSet
33210  * @param {Object} config The config object
33211  */
33212
33213 Roo.bootstrap.RadioSet = function(config){
33214     
33215     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33216     
33217     this.radioes = [];
33218     
33219     Roo.bootstrap.RadioSet.register(this);
33220     
33221     this.addEvents({
33222         /**
33223         * @event check
33224         * Fires when the element is checked or unchecked.
33225         * @param {Roo.bootstrap.RadioSet} this This radio
33226         * @param {Roo.bootstrap.Radio} item The checked item
33227         */
33228        check : true
33229     });
33230     
33231 };
33232
33233 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33234
33235     radioes : false,
33236     
33237     inline : true,
33238     
33239     weight : '',
33240     
33241     indicatorpos : 'left',
33242     
33243     getAutoCreate : function()
33244     {
33245         var label = {
33246             tag : 'label',
33247             cls : 'roo-radio-set-label',
33248             cn : [
33249                 {
33250                     tag : 'span',
33251                     html : this.fieldLabel
33252                 }
33253             ]
33254         };
33255         
33256         if(this.indicatorpos == 'left'){
33257             label.cn.unshift({
33258                 tag : 'i',
33259                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33260                 tooltip : 'This field is required'
33261             });
33262         } else {
33263             label.cn.push({
33264                 tag : 'i',
33265                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33266                 tooltip : 'This field is required'
33267             });
33268         }
33269         
33270         var items = {
33271             tag : 'div',
33272             cls : 'roo-radio-set-items'
33273         };
33274         
33275         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33276         
33277         if (align === 'left' && this.fieldLabel.length) {
33278             
33279             items = {
33280                 cls : "roo-radio-set-right", 
33281                 cn: [
33282                     items
33283                 ]
33284             };
33285             
33286             if(this.labelWidth > 12){
33287                 label.style = "width: " + this.labelWidth + 'px';
33288             }
33289             
33290             if(this.labelWidth < 13 && this.labelmd == 0){
33291                 this.labelmd = this.labelWidth;
33292             }
33293             
33294             if(this.labellg > 0){
33295                 label.cls += ' col-lg-' + this.labellg;
33296                 items.cls += ' col-lg-' + (12 - this.labellg);
33297             }
33298             
33299             if(this.labelmd > 0){
33300                 label.cls += ' col-md-' + this.labelmd;
33301                 items.cls += ' col-md-' + (12 - this.labelmd);
33302             }
33303             
33304             if(this.labelsm > 0){
33305                 label.cls += ' col-sm-' + this.labelsm;
33306                 items.cls += ' col-sm-' + (12 - this.labelsm);
33307             }
33308             
33309             if(this.labelxs > 0){
33310                 label.cls += ' col-xs-' + this.labelxs;
33311                 items.cls += ' col-xs-' + (12 - this.labelxs);
33312             }
33313         }
33314         
33315         var cfg = {
33316             tag : 'div',
33317             cls : 'roo-radio-set',
33318             cn : [
33319                 {
33320                     tag : 'input',
33321                     cls : 'roo-radio-set-input',
33322                     type : 'hidden',
33323                     name : this.name,
33324                     value : this.value ? this.value :  ''
33325                 },
33326                 label,
33327                 items
33328             ]
33329         };
33330         
33331         if(this.weight.length){
33332             cfg.cls += ' roo-radio-' + this.weight;
33333         }
33334         
33335         if(this.inline) {
33336             cfg.cls += ' roo-radio-set-inline';
33337         }
33338         
33339         var settings=this;
33340         ['xs','sm','md','lg'].map(function(size){
33341             if (settings[size]) {
33342                 cfg.cls += ' col-' + size + '-' + settings[size];
33343             }
33344         });
33345         
33346         return cfg;
33347         
33348     },
33349
33350     initEvents : function()
33351     {
33352         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33353         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33354         
33355         if(!this.fieldLabel.length){
33356             this.labelEl.hide();
33357         }
33358         
33359         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33360         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33361         
33362         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33363         this.indicatorEl().hide();
33364         
33365         this.originalValue = this.getValue();
33366         
33367     },
33368     
33369     inputEl: function ()
33370     {
33371         return this.el.select('.roo-radio-set-input', true).first();
33372     },
33373     
33374     getChildContainer : function()
33375     {
33376         return this.itemsEl;
33377     },
33378     
33379     register : function(item)
33380     {
33381         this.radioes.push(item);
33382         
33383     },
33384     
33385     validate : function()
33386     {   
33387         var valid = false;
33388         
33389         Roo.each(this.radioes, function(i){
33390             if(!i.checked){
33391                 return;
33392             }
33393             
33394             valid = true;
33395             return false;
33396         });
33397         
33398         if(this.allowBlank) {
33399             return true;
33400         }
33401         
33402         if(this.disabled || valid){
33403             this.markValid();
33404             return true;
33405         }
33406         
33407         this.markInvalid();
33408         return false;
33409         
33410     },
33411     
33412     markValid : function()
33413     {
33414         if(this.labelEl.isVisible(true)){
33415             this.indicatorEl().hide();
33416         }
33417         
33418         this.el.removeClass([this.invalidClass, this.validClass]);
33419         this.el.addClass(this.validClass);
33420         
33421         this.fireEvent('valid', this);
33422     },
33423     
33424     markInvalid : function(msg)
33425     {
33426         if(this.allowBlank || this.disabled){
33427             return;
33428         }
33429         
33430         if(this.labelEl.isVisible(true)){
33431             this.indicatorEl().show();
33432         }
33433         
33434         this.el.removeClass([this.invalidClass, this.validClass]);
33435         this.el.addClass(this.invalidClass);
33436         
33437         this.fireEvent('invalid', this, msg);
33438         
33439     },
33440     
33441     setValue : function(v, suppressEvent)
33442     {   
33443         this.value = v;
33444         if(this.rendered){
33445             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33446         }
33447         
33448         Roo.each(this.radioes, function(i){
33449             
33450             i.checked = false;
33451             i.el.removeClass('checked');
33452             
33453             if(i.value === v || i.value.toString() === v.toString()){
33454                 i.checked = true;
33455                 i.el.addClass('checked');
33456                 
33457                 if(suppressEvent !== true){
33458                     this.fireEvent('check', this, i);
33459                 }
33460             }
33461             
33462         }, this);
33463         
33464         this.validate();
33465     },
33466     
33467     clearInvalid : function(){
33468         
33469         if(!this.el || this.preventMark){
33470             return;
33471         }
33472         
33473         this.el.removeClass([this.invalidClass]);
33474         
33475         this.fireEvent('valid', this);
33476     }
33477     
33478 });
33479
33480 Roo.apply(Roo.bootstrap.RadioSet, {
33481     
33482     groups: {},
33483     
33484     register : function(set)
33485     {
33486         this.groups[set.name] = set;
33487     },
33488     
33489     get: function(name) 
33490     {
33491         if (typeof(this.groups[name]) == 'undefined') {
33492             return false;
33493         }
33494         
33495         return this.groups[name] ;
33496     }
33497     
33498 });
33499 /*
33500  * Based on:
33501  * Ext JS Library 1.1.1
33502  * Copyright(c) 2006-2007, Ext JS, LLC.
33503  *
33504  * Originally Released Under LGPL - original licence link has changed is not relivant.
33505  *
33506  * Fork - LGPL
33507  * <script type="text/javascript">
33508  */
33509
33510
33511 /**
33512  * @class Roo.bootstrap.SplitBar
33513  * @extends Roo.util.Observable
33514  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33515  * <br><br>
33516  * Usage:
33517  * <pre><code>
33518 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33519                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33520 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33521 split.minSize = 100;
33522 split.maxSize = 600;
33523 split.animate = true;
33524 split.on('moved', splitterMoved);
33525 </code></pre>
33526  * @constructor
33527  * Create a new SplitBar
33528  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33529  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33530  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33531  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33532                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33533                         position of the SplitBar).
33534  */
33535 Roo.bootstrap.SplitBar = function(cfg){
33536     
33537     /** @private */
33538     
33539     //{
33540     //  dragElement : elm
33541     //  resizingElement: el,
33542         // optional..
33543     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33544     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33545         // existingProxy ???
33546     //}
33547     
33548     this.el = Roo.get(cfg.dragElement, true);
33549     this.el.dom.unselectable = "on";
33550     /** @private */
33551     this.resizingEl = Roo.get(cfg.resizingElement, true);
33552
33553     /**
33554      * @private
33555      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33556      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33557      * @type Number
33558      */
33559     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33560     
33561     /**
33562      * The minimum size of the resizing element. (Defaults to 0)
33563      * @type Number
33564      */
33565     this.minSize = 0;
33566     
33567     /**
33568      * The maximum size of the resizing element. (Defaults to 2000)
33569      * @type Number
33570      */
33571     this.maxSize = 2000;
33572     
33573     /**
33574      * Whether to animate the transition to the new size
33575      * @type Boolean
33576      */
33577     this.animate = false;
33578     
33579     /**
33580      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33581      * @type Boolean
33582      */
33583     this.useShim = false;
33584     
33585     /** @private */
33586     this.shim = null;
33587     
33588     if(!cfg.existingProxy){
33589         /** @private */
33590         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33591     }else{
33592         this.proxy = Roo.get(cfg.existingProxy).dom;
33593     }
33594     /** @private */
33595     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33596     
33597     /** @private */
33598     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33599     
33600     /** @private */
33601     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33602     
33603     /** @private */
33604     this.dragSpecs = {};
33605     
33606     /**
33607      * @private The adapter to use to positon and resize elements
33608      */
33609     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33610     this.adapter.init(this);
33611     
33612     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33613         /** @private */
33614         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33615         this.el.addClass("roo-splitbar-h");
33616     }else{
33617         /** @private */
33618         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33619         this.el.addClass("roo-splitbar-v");
33620     }
33621     
33622     this.addEvents({
33623         /**
33624          * @event resize
33625          * Fires when the splitter is moved (alias for {@link #event-moved})
33626          * @param {Roo.bootstrap.SplitBar} this
33627          * @param {Number} newSize the new width or height
33628          */
33629         "resize" : true,
33630         /**
33631          * @event moved
33632          * Fires when the splitter is moved
33633          * @param {Roo.bootstrap.SplitBar} this
33634          * @param {Number} newSize the new width or height
33635          */
33636         "moved" : true,
33637         /**
33638          * @event beforeresize
33639          * Fires before the splitter is dragged
33640          * @param {Roo.bootstrap.SplitBar} this
33641          */
33642         "beforeresize" : true,
33643
33644         "beforeapply" : true
33645     });
33646
33647     Roo.util.Observable.call(this);
33648 };
33649
33650 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33651     onStartProxyDrag : function(x, y){
33652         this.fireEvent("beforeresize", this);
33653         if(!this.overlay){
33654             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33655             o.unselectable();
33656             o.enableDisplayMode("block");
33657             // all splitbars share the same overlay
33658             Roo.bootstrap.SplitBar.prototype.overlay = o;
33659         }
33660         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33661         this.overlay.show();
33662         Roo.get(this.proxy).setDisplayed("block");
33663         var size = this.adapter.getElementSize(this);
33664         this.activeMinSize = this.getMinimumSize();;
33665         this.activeMaxSize = this.getMaximumSize();;
33666         var c1 = size - this.activeMinSize;
33667         var c2 = Math.max(this.activeMaxSize - size, 0);
33668         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33669             this.dd.resetConstraints();
33670             this.dd.setXConstraint(
33671                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33672                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33673             );
33674             this.dd.setYConstraint(0, 0);
33675         }else{
33676             this.dd.resetConstraints();
33677             this.dd.setXConstraint(0, 0);
33678             this.dd.setYConstraint(
33679                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33680                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33681             );
33682          }
33683         this.dragSpecs.startSize = size;
33684         this.dragSpecs.startPoint = [x, y];
33685         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33686     },
33687     
33688     /** 
33689      * @private Called after the drag operation by the DDProxy
33690      */
33691     onEndProxyDrag : function(e){
33692         Roo.get(this.proxy).setDisplayed(false);
33693         var endPoint = Roo.lib.Event.getXY(e);
33694         if(this.overlay){
33695             this.overlay.hide();
33696         }
33697         var newSize;
33698         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33699             newSize = this.dragSpecs.startSize + 
33700                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33701                     endPoint[0] - this.dragSpecs.startPoint[0] :
33702                     this.dragSpecs.startPoint[0] - endPoint[0]
33703                 );
33704         }else{
33705             newSize = this.dragSpecs.startSize + 
33706                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33707                     endPoint[1] - this.dragSpecs.startPoint[1] :
33708                     this.dragSpecs.startPoint[1] - endPoint[1]
33709                 );
33710         }
33711         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33712         if(newSize != this.dragSpecs.startSize){
33713             if(this.fireEvent('beforeapply', this, newSize) !== false){
33714                 this.adapter.setElementSize(this, newSize);
33715                 this.fireEvent("moved", this, newSize);
33716                 this.fireEvent("resize", this, newSize);
33717             }
33718         }
33719     },
33720     
33721     /**
33722      * Get the adapter this SplitBar uses
33723      * @return The adapter object
33724      */
33725     getAdapter : function(){
33726         return this.adapter;
33727     },
33728     
33729     /**
33730      * Set the adapter this SplitBar uses
33731      * @param {Object} adapter A SplitBar adapter object
33732      */
33733     setAdapter : function(adapter){
33734         this.adapter = adapter;
33735         this.adapter.init(this);
33736     },
33737     
33738     /**
33739      * Gets the minimum size for the resizing element
33740      * @return {Number} The minimum size
33741      */
33742     getMinimumSize : function(){
33743         return this.minSize;
33744     },
33745     
33746     /**
33747      * Sets the minimum size for the resizing element
33748      * @param {Number} minSize The minimum size
33749      */
33750     setMinimumSize : function(minSize){
33751         this.minSize = minSize;
33752     },
33753     
33754     /**
33755      * Gets the maximum size for the resizing element
33756      * @return {Number} The maximum size
33757      */
33758     getMaximumSize : function(){
33759         return this.maxSize;
33760     },
33761     
33762     /**
33763      * Sets the maximum size for the resizing element
33764      * @param {Number} maxSize The maximum size
33765      */
33766     setMaximumSize : function(maxSize){
33767         this.maxSize = maxSize;
33768     },
33769     
33770     /**
33771      * Sets the initialize size for the resizing element
33772      * @param {Number} size The initial size
33773      */
33774     setCurrentSize : function(size){
33775         var oldAnimate = this.animate;
33776         this.animate = false;
33777         this.adapter.setElementSize(this, size);
33778         this.animate = oldAnimate;
33779     },
33780     
33781     /**
33782      * Destroy this splitbar. 
33783      * @param {Boolean} removeEl True to remove the element
33784      */
33785     destroy : function(removeEl){
33786         if(this.shim){
33787             this.shim.remove();
33788         }
33789         this.dd.unreg();
33790         this.proxy.parentNode.removeChild(this.proxy);
33791         if(removeEl){
33792             this.el.remove();
33793         }
33794     }
33795 });
33796
33797 /**
33798  * @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.
33799  */
33800 Roo.bootstrap.SplitBar.createProxy = function(dir){
33801     var proxy = new Roo.Element(document.createElement("div"));
33802     proxy.unselectable();
33803     var cls = 'roo-splitbar-proxy';
33804     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33805     document.body.appendChild(proxy.dom);
33806     return proxy.dom;
33807 };
33808
33809 /** 
33810  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33811  * Default Adapter. It assumes the splitter and resizing element are not positioned
33812  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33813  */
33814 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33815 };
33816
33817 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33818     // do nothing for now
33819     init : function(s){
33820     
33821     },
33822     /**
33823      * Called before drag operations to get the current size of the resizing element. 
33824      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33825      */
33826      getElementSize : function(s){
33827         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33828             return s.resizingEl.getWidth();
33829         }else{
33830             return s.resizingEl.getHeight();
33831         }
33832     },
33833     
33834     /**
33835      * Called after drag operations to set the size of the resizing element.
33836      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33837      * @param {Number} newSize The new size to set
33838      * @param {Function} onComplete A function to be invoked when resizing is complete
33839      */
33840     setElementSize : function(s, newSize, onComplete){
33841         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33842             if(!s.animate){
33843                 s.resizingEl.setWidth(newSize);
33844                 if(onComplete){
33845                     onComplete(s, newSize);
33846                 }
33847             }else{
33848                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33849             }
33850         }else{
33851             
33852             if(!s.animate){
33853                 s.resizingEl.setHeight(newSize);
33854                 if(onComplete){
33855                     onComplete(s, newSize);
33856                 }
33857             }else{
33858                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33859             }
33860         }
33861     }
33862 };
33863
33864 /** 
33865  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33866  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33867  * Adapter that  moves the splitter element to align with the resized sizing element. 
33868  * Used with an absolute positioned SplitBar.
33869  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33870  * document.body, make sure you assign an id to the body element.
33871  */
33872 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33873     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33874     this.container = Roo.get(container);
33875 };
33876
33877 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33878     init : function(s){
33879         this.basic.init(s);
33880     },
33881     
33882     getElementSize : function(s){
33883         return this.basic.getElementSize(s);
33884     },
33885     
33886     setElementSize : function(s, newSize, onComplete){
33887         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33888     },
33889     
33890     moveSplitter : function(s){
33891         var yes = Roo.bootstrap.SplitBar;
33892         switch(s.placement){
33893             case yes.LEFT:
33894                 s.el.setX(s.resizingEl.getRight());
33895                 break;
33896             case yes.RIGHT:
33897                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33898                 break;
33899             case yes.TOP:
33900                 s.el.setY(s.resizingEl.getBottom());
33901                 break;
33902             case yes.BOTTOM:
33903                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33904                 break;
33905         }
33906     }
33907 };
33908
33909 /**
33910  * Orientation constant - Create a vertical SplitBar
33911  * @static
33912  * @type Number
33913  */
33914 Roo.bootstrap.SplitBar.VERTICAL = 1;
33915
33916 /**
33917  * Orientation constant - Create a horizontal SplitBar
33918  * @static
33919  * @type Number
33920  */
33921 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33922
33923 /**
33924  * Placement constant - The resizing element is to the left of the splitter element
33925  * @static
33926  * @type Number
33927  */
33928 Roo.bootstrap.SplitBar.LEFT = 1;
33929
33930 /**
33931  * Placement constant - The resizing element is to the right of the splitter element
33932  * @static
33933  * @type Number
33934  */
33935 Roo.bootstrap.SplitBar.RIGHT = 2;
33936
33937 /**
33938  * Placement constant - The resizing element is positioned above the splitter element
33939  * @static
33940  * @type Number
33941  */
33942 Roo.bootstrap.SplitBar.TOP = 3;
33943
33944 /**
33945  * Placement constant - The resizing element is positioned under splitter element
33946  * @static
33947  * @type Number
33948  */
33949 Roo.bootstrap.SplitBar.BOTTOM = 4;
33950 Roo.namespace("Roo.bootstrap.layout");/*
33951  * Based on:
33952  * Ext JS Library 1.1.1
33953  * Copyright(c) 2006-2007, Ext JS, LLC.
33954  *
33955  * Originally Released Under LGPL - original licence link has changed is not relivant.
33956  *
33957  * Fork - LGPL
33958  * <script type="text/javascript">
33959  */
33960
33961 /**
33962  * @class Roo.bootstrap.layout.Manager
33963  * @extends Roo.bootstrap.Component
33964  * Base class for layout managers.
33965  */
33966 Roo.bootstrap.layout.Manager = function(config)
33967 {
33968     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33969
33970
33971
33972
33973
33974     /** false to disable window resize monitoring @type Boolean */
33975     this.monitorWindowResize = true;
33976     this.regions = {};
33977     this.addEvents({
33978         /**
33979          * @event layout
33980          * Fires when a layout is performed.
33981          * @param {Roo.LayoutManager} this
33982          */
33983         "layout" : true,
33984         /**
33985          * @event regionresized
33986          * Fires when the user resizes a region.
33987          * @param {Roo.LayoutRegion} region The resized region
33988          * @param {Number} newSize The new size (width for east/west, height for north/south)
33989          */
33990         "regionresized" : true,
33991         /**
33992          * @event regioncollapsed
33993          * Fires when a region is collapsed.
33994          * @param {Roo.LayoutRegion} region The collapsed region
33995          */
33996         "regioncollapsed" : true,
33997         /**
33998          * @event regionexpanded
33999          * Fires when a region is expanded.
34000          * @param {Roo.LayoutRegion} region The expanded region
34001          */
34002         "regionexpanded" : true
34003     });
34004     this.updating = false;
34005
34006     if (config.el) {
34007         this.el = Roo.get(config.el);
34008         this.initEvents();
34009     }
34010
34011 };
34012
34013 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34014
34015
34016     regions : null,
34017
34018     monitorWindowResize : true,
34019
34020
34021     updating : false,
34022
34023
34024     onRender : function(ct, position)
34025     {
34026         if(!this.el){
34027             this.el = Roo.get(ct);
34028             this.initEvents();
34029         }
34030         //this.fireEvent('render',this);
34031     },
34032
34033
34034     initEvents: function()
34035     {
34036
34037
34038         // ie scrollbar fix
34039         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34040             document.body.scroll = "no";
34041         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34042             this.el.position('relative');
34043         }
34044         this.id = this.el.id;
34045         this.el.addClass("roo-layout-container");
34046         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34047         if(this.el.dom != document.body ) {
34048             this.el.on('resize', this.layout,this);
34049             this.el.on('show', this.layout,this);
34050         }
34051
34052     },
34053
34054     /**
34055      * Returns true if this layout is currently being updated
34056      * @return {Boolean}
34057      */
34058     isUpdating : function(){
34059         return this.updating;
34060     },
34061
34062     /**
34063      * Suspend the LayoutManager from doing auto-layouts while
34064      * making multiple add or remove calls
34065      */
34066     beginUpdate : function(){
34067         this.updating = true;
34068     },
34069
34070     /**
34071      * Restore auto-layouts and optionally disable the manager from performing a layout
34072      * @param {Boolean} noLayout true to disable a layout update
34073      */
34074     endUpdate : function(noLayout){
34075         this.updating = false;
34076         if(!noLayout){
34077             this.layout();
34078         }
34079     },
34080
34081     layout: function(){
34082         // abstract...
34083     },
34084
34085     onRegionResized : function(region, newSize){
34086         this.fireEvent("regionresized", region, newSize);
34087         this.layout();
34088     },
34089
34090     onRegionCollapsed : function(region){
34091         this.fireEvent("regioncollapsed", region);
34092     },
34093
34094     onRegionExpanded : function(region){
34095         this.fireEvent("regionexpanded", region);
34096     },
34097
34098     /**
34099      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34100      * performs box-model adjustments.
34101      * @return {Object} The size as an object {width: (the width), height: (the height)}
34102      */
34103     getViewSize : function()
34104     {
34105         var size;
34106         if(this.el.dom != document.body){
34107             size = this.el.getSize();
34108         }else{
34109             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34110         }
34111         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34112         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34113         return size;
34114     },
34115
34116     /**
34117      * Returns the Element this layout is bound to.
34118      * @return {Roo.Element}
34119      */
34120     getEl : function(){
34121         return this.el;
34122     },
34123
34124     /**
34125      * Returns the specified region.
34126      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34127      * @return {Roo.LayoutRegion}
34128      */
34129     getRegion : function(target){
34130         return this.regions[target.toLowerCase()];
34131     },
34132
34133     onWindowResize : function(){
34134         if(this.monitorWindowResize){
34135             this.layout();
34136         }
34137     }
34138 });
34139 /*
34140  * Based on:
34141  * Ext JS Library 1.1.1
34142  * Copyright(c) 2006-2007, Ext JS, LLC.
34143  *
34144  * Originally Released Under LGPL - original licence link has changed is not relivant.
34145  *
34146  * Fork - LGPL
34147  * <script type="text/javascript">
34148  */
34149 /**
34150  * @class Roo.bootstrap.layout.Border
34151  * @extends Roo.bootstrap.layout.Manager
34152  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34153  * please see: examples/bootstrap/nested.html<br><br>
34154  
34155 <b>The container the layout is rendered into can be either the body element or any other element.
34156 If it is not the body element, the container needs to either be an absolute positioned element,
34157 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34158 the container size if it is not the body element.</b>
34159
34160 * @constructor
34161 * Create a new Border
34162 * @param {Object} config Configuration options
34163  */
34164 Roo.bootstrap.layout.Border = function(config){
34165     config = config || {};
34166     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34167     
34168     
34169     
34170     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34171         if(config[region]){
34172             config[region].region = region;
34173             this.addRegion(config[region]);
34174         }
34175     },this);
34176     
34177 };
34178
34179 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34180
34181 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34182     /**
34183      * Creates and adds a new region if it doesn't already exist.
34184      * @param {String} target The target region key (north, south, east, west or center).
34185      * @param {Object} config The regions config object
34186      * @return {BorderLayoutRegion} The new region
34187      */
34188     addRegion : function(config)
34189     {
34190         if(!this.regions[config.region]){
34191             var r = this.factory(config);
34192             this.bindRegion(r);
34193         }
34194         return this.regions[config.region];
34195     },
34196
34197     // private (kinda)
34198     bindRegion : function(r){
34199         this.regions[r.config.region] = r;
34200         
34201         r.on("visibilitychange",    this.layout, this);
34202         r.on("paneladded",          this.layout, this);
34203         r.on("panelremoved",        this.layout, this);
34204         r.on("invalidated",         this.layout, this);
34205         r.on("resized",             this.onRegionResized, this);
34206         r.on("collapsed",           this.onRegionCollapsed, this);
34207         r.on("expanded",            this.onRegionExpanded, this);
34208     },
34209
34210     /**
34211      * Performs a layout update.
34212      */
34213     layout : function()
34214     {
34215         if(this.updating) {
34216             return;
34217         }
34218         
34219         // render all the rebions if they have not been done alreayd?
34220         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34221             if(this.regions[region] && !this.regions[region].bodyEl){
34222                 this.regions[region].onRender(this.el)
34223             }
34224         },this);
34225         
34226         var size = this.getViewSize();
34227         var w = size.width;
34228         var h = size.height;
34229         var centerW = w;
34230         var centerH = h;
34231         var centerY = 0;
34232         var centerX = 0;
34233         //var x = 0, y = 0;
34234
34235         var rs = this.regions;
34236         var north = rs["north"];
34237         var south = rs["south"]; 
34238         var west = rs["west"];
34239         var east = rs["east"];
34240         var center = rs["center"];
34241         //if(this.hideOnLayout){ // not supported anymore
34242             //c.el.setStyle("display", "none");
34243         //}
34244         if(north && north.isVisible()){
34245             var b = north.getBox();
34246             var m = north.getMargins();
34247             b.width = w - (m.left+m.right);
34248             b.x = m.left;
34249             b.y = m.top;
34250             centerY = b.height + b.y + m.bottom;
34251             centerH -= centerY;
34252             north.updateBox(this.safeBox(b));
34253         }
34254         if(south && south.isVisible()){
34255             var b = south.getBox();
34256             var m = south.getMargins();
34257             b.width = w - (m.left+m.right);
34258             b.x = m.left;
34259             var totalHeight = (b.height + m.top + m.bottom);
34260             b.y = h - totalHeight + m.top;
34261             centerH -= totalHeight;
34262             south.updateBox(this.safeBox(b));
34263         }
34264         if(west && west.isVisible()){
34265             var b = west.getBox();
34266             var m = west.getMargins();
34267             b.height = centerH - (m.top+m.bottom);
34268             b.x = m.left;
34269             b.y = centerY + m.top;
34270             var totalWidth = (b.width + m.left + m.right);
34271             centerX += totalWidth;
34272             centerW -= totalWidth;
34273             west.updateBox(this.safeBox(b));
34274         }
34275         if(east && east.isVisible()){
34276             var b = east.getBox();
34277             var m = east.getMargins();
34278             b.height = centerH - (m.top+m.bottom);
34279             var totalWidth = (b.width + m.left + m.right);
34280             b.x = w - totalWidth + m.left;
34281             b.y = centerY + m.top;
34282             centerW -= totalWidth;
34283             east.updateBox(this.safeBox(b));
34284         }
34285         if(center){
34286             var m = center.getMargins();
34287             var centerBox = {
34288                 x: centerX + m.left,
34289                 y: centerY + m.top,
34290                 width: centerW - (m.left+m.right),
34291                 height: centerH - (m.top+m.bottom)
34292             };
34293             //if(this.hideOnLayout){
34294                 //center.el.setStyle("display", "block");
34295             //}
34296             center.updateBox(this.safeBox(centerBox));
34297         }
34298         this.el.repaint();
34299         this.fireEvent("layout", this);
34300     },
34301
34302     // private
34303     safeBox : function(box){
34304         box.width = Math.max(0, box.width);
34305         box.height = Math.max(0, box.height);
34306         return box;
34307     },
34308
34309     /**
34310      * Adds a ContentPanel (or subclass) to this layout.
34311      * @param {String} target The target region key (north, south, east, west or center).
34312      * @param {Roo.ContentPanel} panel The panel to add
34313      * @return {Roo.ContentPanel} The added panel
34314      */
34315     add : function(target, panel){
34316          
34317         target = target.toLowerCase();
34318         return this.regions[target].add(panel);
34319     },
34320
34321     /**
34322      * Remove a ContentPanel (or subclass) to this layout.
34323      * @param {String} target The target region key (north, south, east, west or center).
34324      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34325      * @return {Roo.ContentPanel} The removed panel
34326      */
34327     remove : function(target, panel){
34328         target = target.toLowerCase();
34329         return this.regions[target].remove(panel);
34330     },
34331
34332     /**
34333      * Searches all regions for a panel with the specified id
34334      * @param {String} panelId
34335      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34336      */
34337     findPanel : function(panelId){
34338         var rs = this.regions;
34339         for(var target in rs){
34340             if(typeof rs[target] != "function"){
34341                 var p = rs[target].getPanel(panelId);
34342                 if(p){
34343                     return p;
34344                 }
34345             }
34346         }
34347         return null;
34348     },
34349
34350     /**
34351      * Searches all regions for a panel with the specified id and activates (shows) it.
34352      * @param {String/ContentPanel} panelId The panels id or the panel itself
34353      * @return {Roo.ContentPanel} The shown panel or null
34354      */
34355     showPanel : function(panelId) {
34356       var rs = this.regions;
34357       for(var target in rs){
34358          var r = rs[target];
34359          if(typeof r != "function"){
34360             if(r.hasPanel(panelId)){
34361                return r.showPanel(panelId);
34362             }
34363          }
34364       }
34365       return null;
34366    },
34367
34368    /**
34369      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34370      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34371      */
34372    /*
34373     restoreState : function(provider){
34374         if(!provider){
34375             provider = Roo.state.Manager;
34376         }
34377         var sm = new Roo.LayoutStateManager();
34378         sm.init(this, provider);
34379     },
34380 */
34381  
34382  
34383     /**
34384      * Adds a xtype elements to the layout.
34385      * <pre><code>
34386
34387 layout.addxtype({
34388        xtype : 'ContentPanel',
34389        region: 'west',
34390        items: [ .... ]
34391    }
34392 );
34393
34394 layout.addxtype({
34395         xtype : 'NestedLayoutPanel',
34396         region: 'west',
34397         layout: {
34398            center: { },
34399            west: { }   
34400         },
34401         items : [ ... list of content panels or nested layout panels.. ]
34402    }
34403 );
34404 </code></pre>
34405      * @param {Object} cfg Xtype definition of item to add.
34406      */
34407     addxtype : function(cfg)
34408     {
34409         // basically accepts a pannel...
34410         // can accept a layout region..!?!?
34411         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34412         
34413         
34414         // theory?  children can only be panels??
34415         
34416         //if (!cfg.xtype.match(/Panel$/)) {
34417         //    return false;
34418         //}
34419         var ret = false;
34420         
34421         if (typeof(cfg.region) == 'undefined') {
34422             Roo.log("Failed to add Panel, region was not set");
34423             Roo.log(cfg);
34424             return false;
34425         }
34426         var region = cfg.region;
34427         delete cfg.region;
34428         
34429           
34430         var xitems = [];
34431         if (cfg.items) {
34432             xitems = cfg.items;
34433             delete cfg.items;
34434         }
34435         var nb = false;
34436         
34437         switch(cfg.xtype) 
34438         {
34439             case 'Content':  // ContentPanel (el, cfg)
34440             case 'Scroll':  // ContentPanel (el, cfg)
34441             case 'View': 
34442                 cfg.autoCreate = true;
34443                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34444                 //} else {
34445                 //    var el = this.el.createChild();
34446                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34447                 //}
34448                 
34449                 this.add(region, ret);
34450                 break;
34451             
34452             /*
34453             case 'TreePanel': // our new panel!
34454                 cfg.el = this.el.createChild();
34455                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34456                 this.add(region, ret);
34457                 break;
34458             */
34459             
34460             case 'Nest': 
34461                 // create a new Layout (which is  a Border Layout...
34462                 
34463                 var clayout = cfg.layout;
34464                 clayout.el  = this.el.createChild();
34465                 clayout.items   = clayout.items  || [];
34466                 
34467                 delete cfg.layout;
34468                 
34469                 // replace this exitems with the clayout ones..
34470                 xitems = clayout.items;
34471                  
34472                 // force background off if it's in center...
34473                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34474                     cfg.background = false;
34475                 }
34476                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34477                 
34478                 
34479                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34480                 //console.log('adding nested layout panel '  + cfg.toSource());
34481                 this.add(region, ret);
34482                 nb = {}; /// find first...
34483                 break;
34484             
34485             case 'Grid':
34486                 
34487                 // needs grid and region
34488                 
34489                 //var el = this.getRegion(region).el.createChild();
34490                 /*
34491                  *var el = this.el.createChild();
34492                 // create the grid first...
34493                 cfg.grid.container = el;
34494                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34495                 */
34496                 
34497                 if (region == 'center' && this.active ) {
34498                     cfg.background = false;
34499                 }
34500                 
34501                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34502                 
34503                 this.add(region, ret);
34504                 /*
34505                 if (cfg.background) {
34506                     // render grid on panel activation (if panel background)
34507                     ret.on('activate', function(gp) {
34508                         if (!gp.grid.rendered) {
34509                     //        gp.grid.render(el);
34510                         }
34511                     });
34512                 } else {
34513                   //  cfg.grid.render(el);
34514                 }
34515                 */
34516                 break;
34517            
34518            
34519             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34520                 // it was the old xcomponent building that caused this before.
34521                 // espeically if border is the top element in the tree.
34522                 ret = this;
34523                 break; 
34524                 
34525                     
34526                 
34527                 
34528                 
34529             default:
34530                 /*
34531                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34532                     
34533                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34534                     this.add(region, ret);
34535                 } else {
34536                 */
34537                     Roo.log(cfg);
34538                     throw "Can not add '" + cfg.xtype + "' to Border";
34539                     return null;
34540              
34541                                 
34542              
34543         }
34544         this.beginUpdate();
34545         // add children..
34546         var region = '';
34547         var abn = {};
34548         Roo.each(xitems, function(i)  {
34549             region = nb && i.region ? i.region : false;
34550             
34551             var add = ret.addxtype(i);
34552            
34553             if (region) {
34554                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34555                 if (!i.background) {
34556                     abn[region] = nb[region] ;
34557                 }
34558             }
34559             
34560         });
34561         this.endUpdate();
34562
34563         // make the last non-background panel active..
34564         //if (nb) { Roo.log(abn); }
34565         if (nb) {
34566             
34567             for(var r in abn) {
34568                 region = this.getRegion(r);
34569                 if (region) {
34570                     // tried using nb[r], but it does not work..
34571                      
34572                     region.showPanel(abn[r]);
34573                    
34574                 }
34575             }
34576         }
34577         return ret;
34578         
34579     },
34580     
34581     
34582 // private
34583     factory : function(cfg)
34584     {
34585         
34586         var validRegions = Roo.bootstrap.layout.Border.regions;
34587
34588         var target = cfg.region;
34589         cfg.mgr = this;
34590         
34591         var r = Roo.bootstrap.layout;
34592         Roo.log(target);
34593         switch(target){
34594             case "north":
34595                 return new r.North(cfg);
34596             case "south":
34597                 return new r.South(cfg);
34598             case "east":
34599                 return new r.East(cfg);
34600             case "west":
34601                 return new r.West(cfg);
34602             case "center":
34603                 return new r.Center(cfg);
34604         }
34605         throw 'Layout region "'+target+'" not supported.';
34606     }
34607     
34608     
34609 });
34610  /*
34611  * Based on:
34612  * Ext JS Library 1.1.1
34613  * Copyright(c) 2006-2007, Ext JS, LLC.
34614  *
34615  * Originally Released Under LGPL - original licence link has changed is not relivant.
34616  *
34617  * Fork - LGPL
34618  * <script type="text/javascript">
34619  */
34620  
34621 /**
34622  * @class Roo.bootstrap.layout.Basic
34623  * @extends Roo.util.Observable
34624  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34625  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34626  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34627  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34628  * @cfg {string}   region  the region that it inhabits..
34629  * @cfg {bool}   skipConfig skip config?
34630  * 
34631
34632  */
34633 Roo.bootstrap.layout.Basic = function(config){
34634     
34635     this.mgr = config.mgr;
34636     
34637     this.position = config.region;
34638     
34639     var skipConfig = config.skipConfig;
34640     
34641     this.events = {
34642         /**
34643          * @scope Roo.BasicLayoutRegion
34644          */
34645         
34646         /**
34647          * @event beforeremove
34648          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34649          * @param {Roo.LayoutRegion} this
34650          * @param {Roo.ContentPanel} panel The panel
34651          * @param {Object} e The cancel event object
34652          */
34653         "beforeremove" : true,
34654         /**
34655          * @event invalidated
34656          * Fires when the layout for this region is changed.
34657          * @param {Roo.LayoutRegion} this
34658          */
34659         "invalidated" : true,
34660         /**
34661          * @event visibilitychange
34662          * Fires when this region is shown or hidden 
34663          * @param {Roo.LayoutRegion} this
34664          * @param {Boolean} visibility true or false
34665          */
34666         "visibilitychange" : true,
34667         /**
34668          * @event paneladded
34669          * Fires when a panel is added. 
34670          * @param {Roo.LayoutRegion} this
34671          * @param {Roo.ContentPanel} panel The panel
34672          */
34673         "paneladded" : true,
34674         /**
34675          * @event panelremoved
34676          * Fires when a panel is removed. 
34677          * @param {Roo.LayoutRegion} this
34678          * @param {Roo.ContentPanel} panel The panel
34679          */
34680         "panelremoved" : true,
34681         /**
34682          * @event beforecollapse
34683          * Fires when this region before collapse.
34684          * @param {Roo.LayoutRegion} this
34685          */
34686         "beforecollapse" : true,
34687         /**
34688          * @event collapsed
34689          * Fires when this region is collapsed.
34690          * @param {Roo.LayoutRegion} this
34691          */
34692         "collapsed" : true,
34693         /**
34694          * @event expanded
34695          * Fires when this region is expanded.
34696          * @param {Roo.LayoutRegion} this
34697          */
34698         "expanded" : true,
34699         /**
34700          * @event slideshow
34701          * Fires when this region is slid into view.
34702          * @param {Roo.LayoutRegion} this
34703          */
34704         "slideshow" : true,
34705         /**
34706          * @event slidehide
34707          * Fires when this region slides out of view. 
34708          * @param {Roo.LayoutRegion} this
34709          */
34710         "slidehide" : true,
34711         /**
34712          * @event panelactivated
34713          * Fires when a panel is activated. 
34714          * @param {Roo.LayoutRegion} this
34715          * @param {Roo.ContentPanel} panel The activated panel
34716          */
34717         "panelactivated" : true,
34718         /**
34719          * @event resized
34720          * Fires when the user resizes this region. 
34721          * @param {Roo.LayoutRegion} this
34722          * @param {Number} newSize The new size (width for east/west, height for north/south)
34723          */
34724         "resized" : true
34725     };
34726     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34727     this.panels = new Roo.util.MixedCollection();
34728     this.panels.getKey = this.getPanelId.createDelegate(this);
34729     this.box = null;
34730     this.activePanel = null;
34731     // ensure listeners are added...
34732     
34733     if (config.listeners || config.events) {
34734         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34735             listeners : config.listeners || {},
34736             events : config.events || {}
34737         });
34738     }
34739     
34740     if(skipConfig !== true){
34741         this.applyConfig(config);
34742     }
34743 };
34744
34745 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34746 {
34747     getPanelId : function(p){
34748         return p.getId();
34749     },
34750     
34751     applyConfig : function(config){
34752         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34753         this.config = config;
34754         
34755     },
34756     
34757     /**
34758      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34759      * the width, for horizontal (north, south) the height.
34760      * @param {Number} newSize The new width or height
34761      */
34762     resizeTo : function(newSize){
34763         var el = this.el ? this.el :
34764                  (this.activePanel ? this.activePanel.getEl() : null);
34765         if(el){
34766             switch(this.position){
34767                 case "east":
34768                 case "west":
34769                     el.setWidth(newSize);
34770                     this.fireEvent("resized", this, newSize);
34771                 break;
34772                 case "north":
34773                 case "south":
34774                     el.setHeight(newSize);
34775                     this.fireEvent("resized", this, newSize);
34776                 break;                
34777             }
34778         }
34779     },
34780     
34781     getBox : function(){
34782         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34783     },
34784     
34785     getMargins : function(){
34786         return this.margins;
34787     },
34788     
34789     updateBox : function(box){
34790         this.box = box;
34791         var el = this.activePanel.getEl();
34792         el.dom.style.left = box.x + "px";
34793         el.dom.style.top = box.y + "px";
34794         this.activePanel.setSize(box.width, box.height);
34795     },
34796     
34797     /**
34798      * Returns the container element for this region.
34799      * @return {Roo.Element}
34800      */
34801     getEl : function(){
34802         return this.activePanel;
34803     },
34804     
34805     /**
34806      * Returns true if this region is currently visible.
34807      * @return {Boolean}
34808      */
34809     isVisible : function(){
34810         return this.activePanel ? true : false;
34811     },
34812     
34813     setActivePanel : function(panel){
34814         panel = this.getPanel(panel);
34815         if(this.activePanel && this.activePanel != panel){
34816             this.activePanel.setActiveState(false);
34817             this.activePanel.getEl().setLeftTop(-10000,-10000);
34818         }
34819         this.activePanel = panel;
34820         panel.setActiveState(true);
34821         if(this.box){
34822             panel.setSize(this.box.width, this.box.height);
34823         }
34824         this.fireEvent("panelactivated", this, panel);
34825         this.fireEvent("invalidated");
34826     },
34827     
34828     /**
34829      * Show the specified panel.
34830      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34831      * @return {Roo.ContentPanel} The shown panel or null
34832      */
34833     showPanel : function(panel){
34834         panel = this.getPanel(panel);
34835         if(panel){
34836             this.setActivePanel(panel);
34837         }
34838         return panel;
34839     },
34840     
34841     /**
34842      * Get the active panel for this region.
34843      * @return {Roo.ContentPanel} The active panel or null
34844      */
34845     getActivePanel : function(){
34846         return this.activePanel;
34847     },
34848     
34849     /**
34850      * Add the passed ContentPanel(s)
34851      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34852      * @return {Roo.ContentPanel} The panel added (if only one was added)
34853      */
34854     add : function(panel){
34855         if(arguments.length > 1){
34856             for(var i = 0, len = arguments.length; i < len; i++) {
34857                 this.add(arguments[i]);
34858             }
34859             return null;
34860         }
34861         if(this.hasPanel(panel)){
34862             this.showPanel(panel);
34863             return panel;
34864         }
34865         var el = panel.getEl();
34866         if(el.dom.parentNode != this.mgr.el.dom){
34867             this.mgr.el.dom.appendChild(el.dom);
34868         }
34869         if(panel.setRegion){
34870             panel.setRegion(this);
34871         }
34872         this.panels.add(panel);
34873         el.setStyle("position", "absolute");
34874         if(!panel.background){
34875             this.setActivePanel(panel);
34876             if(this.config.initialSize && this.panels.getCount()==1){
34877                 this.resizeTo(this.config.initialSize);
34878             }
34879         }
34880         this.fireEvent("paneladded", this, panel);
34881         return panel;
34882     },
34883     
34884     /**
34885      * Returns true if the panel is in this region.
34886      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34887      * @return {Boolean}
34888      */
34889     hasPanel : function(panel){
34890         if(typeof panel == "object"){ // must be panel obj
34891             panel = panel.getId();
34892         }
34893         return this.getPanel(panel) ? true : false;
34894     },
34895     
34896     /**
34897      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34898      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34899      * @param {Boolean} preservePanel Overrides the config preservePanel option
34900      * @return {Roo.ContentPanel} The panel that was removed
34901      */
34902     remove : function(panel, preservePanel){
34903         panel = this.getPanel(panel);
34904         if(!panel){
34905             return null;
34906         }
34907         var e = {};
34908         this.fireEvent("beforeremove", this, panel, e);
34909         if(e.cancel === true){
34910             return null;
34911         }
34912         var panelId = panel.getId();
34913         this.panels.removeKey(panelId);
34914         return panel;
34915     },
34916     
34917     /**
34918      * Returns the panel specified or null if it's not in this region.
34919      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34920      * @return {Roo.ContentPanel}
34921      */
34922     getPanel : function(id){
34923         if(typeof id == "object"){ // must be panel obj
34924             return id;
34925         }
34926         return this.panels.get(id);
34927     },
34928     
34929     /**
34930      * Returns this regions position (north/south/east/west/center).
34931      * @return {String} 
34932      */
34933     getPosition: function(){
34934         return this.position;    
34935     }
34936 });/*
34937  * Based on:
34938  * Ext JS Library 1.1.1
34939  * Copyright(c) 2006-2007, Ext JS, LLC.
34940  *
34941  * Originally Released Under LGPL - original licence link has changed is not relivant.
34942  *
34943  * Fork - LGPL
34944  * <script type="text/javascript">
34945  */
34946  
34947 /**
34948  * @class Roo.bootstrap.layout.Region
34949  * @extends Roo.bootstrap.layout.Basic
34950  * This class represents a region in a layout manager.
34951  
34952  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34953  * @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})
34954  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34955  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34956  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34957  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34958  * @cfg {String}    title           The title for the region (overrides panel titles)
34959  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34960  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34961  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34962  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34963  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34964  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34965  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34966  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34967  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34968  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34969
34970  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34971  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34972  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34973  * @cfg {Number}    width           For East/West panels
34974  * @cfg {Number}    height          For North/South panels
34975  * @cfg {Boolean}   split           To show the splitter
34976  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34977  * 
34978  * @cfg {string}   cls             Extra CSS classes to add to region
34979  * 
34980  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34981  * @cfg {string}   region  the region that it inhabits..
34982  *
34983
34984  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34985  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34986
34987  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34988  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34989  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34990  */
34991 Roo.bootstrap.layout.Region = function(config)
34992 {
34993     this.applyConfig(config);
34994
34995     var mgr = config.mgr;
34996     var pos = config.region;
34997     config.skipConfig = true;
34998     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34999     
35000     if (mgr.el) {
35001         this.onRender(mgr.el);   
35002     }
35003      
35004     this.visible = true;
35005     this.collapsed = false;
35006     this.unrendered_panels = [];
35007 };
35008
35009 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35010
35011     position: '', // set by wrapper (eg. north/south etc..)
35012     unrendered_panels : null,  // unrendered panels.
35013     createBody : function(){
35014         /** This region's body element 
35015         * @type Roo.Element */
35016         this.bodyEl = this.el.createChild({
35017                 tag: "div",
35018                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35019         });
35020     },
35021
35022     onRender: function(ctr, pos)
35023     {
35024         var dh = Roo.DomHelper;
35025         /** This region's container element 
35026         * @type Roo.Element */
35027         this.el = dh.append(ctr.dom, {
35028                 tag: "div",
35029                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35030             }, true);
35031         /** This region's title element 
35032         * @type Roo.Element */
35033     
35034         this.titleEl = dh.append(this.el.dom,
35035             {
35036                     tag: "div",
35037                     unselectable: "on",
35038                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35039                     children:[
35040                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35041                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35042                     ]}, true);
35043         
35044         this.titleEl.enableDisplayMode();
35045         /** This region's title text element 
35046         * @type HTMLElement */
35047         this.titleTextEl = this.titleEl.dom.firstChild;
35048         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35049         /*
35050         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35051         this.closeBtn.enableDisplayMode();
35052         this.closeBtn.on("click", this.closeClicked, this);
35053         this.closeBtn.hide();
35054     */
35055         this.createBody(this.config);
35056         if(this.config.hideWhenEmpty){
35057             this.hide();
35058             this.on("paneladded", this.validateVisibility, this);
35059             this.on("panelremoved", this.validateVisibility, this);
35060         }
35061         if(this.autoScroll){
35062             this.bodyEl.setStyle("overflow", "auto");
35063         }else{
35064             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35065         }
35066         //if(c.titlebar !== false){
35067             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35068                 this.titleEl.hide();
35069             }else{
35070                 this.titleEl.show();
35071                 if(this.config.title){
35072                     this.titleTextEl.innerHTML = this.config.title;
35073                 }
35074             }
35075         //}
35076         if(this.config.collapsed){
35077             this.collapse(true);
35078         }
35079         if(this.config.hidden){
35080             this.hide();
35081         }
35082         
35083         if (this.unrendered_panels && this.unrendered_panels.length) {
35084             for (var i =0;i< this.unrendered_panels.length; i++) {
35085                 this.add(this.unrendered_panels[i]);
35086             }
35087             this.unrendered_panels = null;
35088             
35089         }
35090         
35091     },
35092     
35093     applyConfig : function(c)
35094     {
35095         /*
35096          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35097             var dh = Roo.DomHelper;
35098             if(c.titlebar !== false){
35099                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35100                 this.collapseBtn.on("click", this.collapse, this);
35101                 this.collapseBtn.enableDisplayMode();
35102                 /*
35103                 if(c.showPin === true || this.showPin){
35104                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35105                     this.stickBtn.enableDisplayMode();
35106                     this.stickBtn.on("click", this.expand, this);
35107                     this.stickBtn.hide();
35108                 }
35109                 
35110             }
35111             */
35112             /** This region's collapsed element
35113             * @type Roo.Element */
35114             /*
35115              *
35116             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35117                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35118             ]}, true);
35119             
35120             if(c.floatable !== false){
35121                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35122                this.collapsedEl.on("click", this.collapseClick, this);
35123             }
35124
35125             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35126                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35127                    id: "message", unselectable: "on", style:{"float":"left"}});
35128                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35129              }
35130             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35131             this.expandBtn.on("click", this.expand, this);
35132             
35133         }
35134         
35135         if(this.collapseBtn){
35136             this.collapseBtn.setVisible(c.collapsible == true);
35137         }
35138         
35139         this.cmargins = c.cmargins || this.cmargins ||
35140                          (this.position == "west" || this.position == "east" ?
35141                              {top: 0, left: 2, right:2, bottom: 0} :
35142                              {top: 2, left: 0, right:0, bottom: 2});
35143         */
35144         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35145         
35146         
35147         this.bottomTabs = c.tabPosition != "top";
35148         
35149         this.autoScroll = c.autoScroll || false;
35150         
35151         
35152        
35153         
35154         this.duration = c.duration || .30;
35155         this.slideDuration = c.slideDuration || .45;
35156         this.config = c;
35157        
35158     },
35159     /**
35160      * Returns true if this region is currently visible.
35161      * @return {Boolean}
35162      */
35163     isVisible : function(){
35164         return this.visible;
35165     },
35166
35167     /**
35168      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35169      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35170      */
35171     //setCollapsedTitle : function(title){
35172     //    title = title || "&#160;";
35173      //   if(this.collapsedTitleTextEl){
35174       //      this.collapsedTitleTextEl.innerHTML = title;
35175        // }
35176     //},
35177
35178     getBox : function(){
35179         var b;
35180       //  if(!this.collapsed){
35181             b = this.el.getBox(false, true);
35182        // }else{
35183           //  b = this.collapsedEl.getBox(false, true);
35184         //}
35185         return b;
35186     },
35187
35188     getMargins : function(){
35189         return this.margins;
35190         //return this.collapsed ? this.cmargins : this.margins;
35191     },
35192 /*
35193     highlight : function(){
35194         this.el.addClass("x-layout-panel-dragover");
35195     },
35196
35197     unhighlight : function(){
35198         this.el.removeClass("x-layout-panel-dragover");
35199     },
35200 */
35201     updateBox : function(box)
35202     {
35203         if (!this.bodyEl) {
35204             return; // not rendered yet..
35205         }
35206         
35207         this.box = box;
35208         if(!this.collapsed){
35209             this.el.dom.style.left = box.x + "px";
35210             this.el.dom.style.top = box.y + "px";
35211             this.updateBody(box.width, box.height);
35212         }else{
35213             this.collapsedEl.dom.style.left = box.x + "px";
35214             this.collapsedEl.dom.style.top = box.y + "px";
35215             this.collapsedEl.setSize(box.width, box.height);
35216         }
35217         if(this.tabs){
35218             this.tabs.autoSizeTabs();
35219         }
35220     },
35221
35222     updateBody : function(w, h)
35223     {
35224         if(w !== null){
35225             this.el.setWidth(w);
35226             w -= this.el.getBorderWidth("rl");
35227             if(this.config.adjustments){
35228                 w += this.config.adjustments[0];
35229             }
35230         }
35231         if(h !== null && h > 0){
35232             this.el.setHeight(h);
35233             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35234             h -= this.el.getBorderWidth("tb");
35235             if(this.config.adjustments){
35236                 h += this.config.adjustments[1];
35237             }
35238             this.bodyEl.setHeight(h);
35239             if(this.tabs){
35240                 h = this.tabs.syncHeight(h);
35241             }
35242         }
35243         if(this.panelSize){
35244             w = w !== null ? w : this.panelSize.width;
35245             h = h !== null ? h : this.panelSize.height;
35246         }
35247         if(this.activePanel){
35248             var el = this.activePanel.getEl();
35249             w = w !== null ? w : el.getWidth();
35250             h = h !== null ? h : el.getHeight();
35251             this.panelSize = {width: w, height: h};
35252             this.activePanel.setSize(w, h);
35253         }
35254         if(Roo.isIE && this.tabs){
35255             this.tabs.el.repaint();
35256         }
35257     },
35258
35259     /**
35260      * Returns the container element for this region.
35261      * @return {Roo.Element}
35262      */
35263     getEl : function(){
35264         return this.el;
35265     },
35266
35267     /**
35268      * Hides this region.
35269      */
35270     hide : function(){
35271         //if(!this.collapsed){
35272             this.el.dom.style.left = "-2000px";
35273             this.el.hide();
35274         //}else{
35275          //   this.collapsedEl.dom.style.left = "-2000px";
35276          //   this.collapsedEl.hide();
35277        // }
35278         this.visible = false;
35279         this.fireEvent("visibilitychange", this, false);
35280     },
35281
35282     /**
35283      * Shows this region if it was previously hidden.
35284      */
35285     show : function(){
35286         //if(!this.collapsed){
35287             this.el.show();
35288         //}else{
35289         //    this.collapsedEl.show();
35290        // }
35291         this.visible = true;
35292         this.fireEvent("visibilitychange", this, true);
35293     },
35294 /*
35295     closeClicked : function(){
35296         if(this.activePanel){
35297             this.remove(this.activePanel);
35298         }
35299     },
35300
35301     collapseClick : function(e){
35302         if(this.isSlid){
35303            e.stopPropagation();
35304            this.slideIn();
35305         }else{
35306            e.stopPropagation();
35307            this.slideOut();
35308         }
35309     },
35310 */
35311     /**
35312      * Collapses this region.
35313      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35314      */
35315     /*
35316     collapse : function(skipAnim, skipCheck = false){
35317         if(this.collapsed) {
35318             return;
35319         }
35320         
35321         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35322             
35323             this.collapsed = true;
35324             if(this.split){
35325                 this.split.el.hide();
35326             }
35327             if(this.config.animate && skipAnim !== true){
35328                 this.fireEvent("invalidated", this);
35329                 this.animateCollapse();
35330             }else{
35331                 this.el.setLocation(-20000,-20000);
35332                 this.el.hide();
35333                 this.collapsedEl.show();
35334                 this.fireEvent("collapsed", this);
35335                 this.fireEvent("invalidated", this);
35336             }
35337         }
35338         
35339     },
35340 */
35341     animateCollapse : function(){
35342         // overridden
35343     },
35344
35345     /**
35346      * Expands this region if it was previously collapsed.
35347      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35348      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35349      */
35350     /*
35351     expand : function(e, skipAnim){
35352         if(e) {
35353             e.stopPropagation();
35354         }
35355         if(!this.collapsed || this.el.hasActiveFx()) {
35356             return;
35357         }
35358         if(this.isSlid){
35359             this.afterSlideIn();
35360             skipAnim = true;
35361         }
35362         this.collapsed = false;
35363         if(this.config.animate && skipAnim !== true){
35364             this.animateExpand();
35365         }else{
35366             this.el.show();
35367             if(this.split){
35368                 this.split.el.show();
35369             }
35370             this.collapsedEl.setLocation(-2000,-2000);
35371             this.collapsedEl.hide();
35372             this.fireEvent("invalidated", this);
35373             this.fireEvent("expanded", this);
35374         }
35375     },
35376 */
35377     animateExpand : function(){
35378         // overridden
35379     },
35380
35381     initTabs : function()
35382     {
35383         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35384         
35385         var ts = new Roo.bootstrap.panel.Tabs({
35386                 el: this.bodyEl.dom,
35387                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35388                 disableTooltips: this.config.disableTabTips,
35389                 toolbar : this.config.toolbar
35390             });
35391         
35392         if(this.config.hideTabs){
35393             ts.stripWrap.setDisplayed(false);
35394         }
35395         this.tabs = ts;
35396         ts.resizeTabs = this.config.resizeTabs === true;
35397         ts.minTabWidth = this.config.minTabWidth || 40;
35398         ts.maxTabWidth = this.config.maxTabWidth || 250;
35399         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35400         ts.monitorResize = false;
35401         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35402         ts.bodyEl.addClass('roo-layout-tabs-body');
35403         this.panels.each(this.initPanelAsTab, this);
35404     },
35405
35406     initPanelAsTab : function(panel){
35407         var ti = this.tabs.addTab(
35408             panel.getEl().id,
35409             panel.getTitle(),
35410             null,
35411             this.config.closeOnTab && panel.isClosable(),
35412             panel.tpl
35413         );
35414         if(panel.tabTip !== undefined){
35415             ti.setTooltip(panel.tabTip);
35416         }
35417         ti.on("activate", function(){
35418               this.setActivePanel(panel);
35419         }, this);
35420         
35421         if(this.config.closeOnTab){
35422             ti.on("beforeclose", function(t, e){
35423                 e.cancel = true;
35424                 this.remove(panel);
35425             }, this);
35426         }
35427         
35428         panel.tabItem = ti;
35429         
35430         return ti;
35431     },
35432
35433     updatePanelTitle : function(panel, title)
35434     {
35435         if(this.activePanel == panel){
35436             this.updateTitle(title);
35437         }
35438         if(this.tabs){
35439             var ti = this.tabs.getTab(panel.getEl().id);
35440             ti.setText(title);
35441             if(panel.tabTip !== undefined){
35442                 ti.setTooltip(panel.tabTip);
35443             }
35444         }
35445     },
35446
35447     updateTitle : function(title){
35448         if(this.titleTextEl && !this.config.title){
35449             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35450         }
35451     },
35452
35453     setActivePanel : function(panel)
35454     {
35455         panel = this.getPanel(panel);
35456         if(this.activePanel && this.activePanel != panel){
35457             if(this.activePanel.setActiveState(false) === false){
35458                 return;
35459             }
35460         }
35461         this.activePanel = panel;
35462         panel.setActiveState(true);
35463         if(this.panelSize){
35464             panel.setSize(this.panelSize.width, this.panelSize.height);
35465         }
35466         if(this.closeBtn){
35467             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35468         }
35469         this.updateTitle(panel.getTitle());
35470         if(this.tabs){
35471             this.fireEvent("invalidated", this);
35472         }
35473         this.fireEvent("panelactivated", this, panel);
35474     },
35475
35476     /**
35477      * Shows the specified panel.
35478      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35479      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35480      */
35481     showPanel : function(panel)
35482     {
35483         panel = this.getPanel(panel);
35484         if(panel){
35485             if(this.tabs){
35486                 var tab = this.tabs.getTab(panel.getEl().id);
35487                 if(tab.isHidden()){
35488                     this.tabs.unhideTab(tab.id);
35489                 }
35490                 tab.activate();
35491             }else{
35492                 this.setActivePanel(panel);
35493             }
35494         }
35495         return panel;
35496     },
35497
35498     /**
35499      * Get the active panel for this region.
35500      * @return {Roo.ContentPanel} The active panel or null
35501      */
35502     getActivePanel : function(){
35503         return this.activePanel;
35504     },
35505
35506     validateVisibility : function(){
35507         if(this.panels.getCount() < 1){
35508             this.updateTitle("&#160;");
35509             this.closeBtn.hide();
35510             this.hide();
35511         }else{
35512             if(!this.isVisible()){
35513                 this.show();
35514             }
35515         }
35516     },
35517
35518     /**
35519      * Adds the passed ContentPanel(s) to this region.
35520      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35521      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35522      */
35523     add : function(panel)
35524     {
35525         if(arguments.length > 1){
35526             for(var i = 0, len = arguments.length; i < len; i++) {
35527                 this.add(arguments[i]);
35528             }
35529             return null;
35530         }
35531         
35532         // if we have not been rendered yet, then we can not really do much of this..
35533         if (!this.bodyEl) {
35534             this.unrendered_panels.push(panel);
35535             return panel;
35536         }
35537         
35538         
35539         
35540         
35541         if(this.hasPanel(panel)){
35542             this.showPanel(panel);
35543             return panel;
35544         }
35545         panel.setRegion(this);
35546         this.panels.add(panel);
35547        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35548             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35549             // and hide them... ???
35550             this.bodyEl.dom.appendChild(panel.getEl().dom);
35551             if(panel.background !== true){
35552                 this.setActivePanel(panel);
35553             }
35554             this.fireEvent("paneladded", this, panel);
35555             return panel;
35556         }
35557         */
35558         if(!this.tabs){
35559             this.initTabs();
35560         }else{
35561             this.initPanelAsTab(panel);
35562         }
35563         
35564         
35565         if(panel.background !== true){
35566             this.tabs.activate(panel.getEl().id);
35567         }
35568         this.fireEvent("paneladded", this, panel);
35569         return panel;
35570     },
35571
35572     /**
35573      * Hides the tab for the specified panel.
35574      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35575      */
35576     hidePanel : function(panel){
35577         if(this.tabs && (panel = this.getPanel(panel))){
35578             this.tabs.hideTab(panel.getEl().id);
35579         }
35580     },
35581
35582     /**
35583      * Unhides the tab for a previously hidden panel.
35584      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35585      */
35586     unhidePanel : function(panel){
35587         if(this.tabs && (panel = this.getPanel(panel))){
35588             this.tabs.unhideTab(panel.getEl().id);
35589         }
35590     },
35591
35592     clearPanels : function(){
35593         while(this.panels.getCount() > 0){
35594              this.remove(this.panels.first());
35595         }
35596     },
35597
35598     /**
35599      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35600      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35601      * @param {Boolean} preservePanel Overrides the config preservePanel option
35602      * @return {Roo.ContentPanel} The panel that was removed
35603      */
35604     remove : function(panel, preservePanel)
35605     {
35606         panel = this.getPanel(panel);
35607         if(!panel){
35608             return null;
35609         }
35610         var e = {};
35611         this.fireEvent("beforeremove", this, panel, e);
35612         if(e.cancel === true){
35613             return null;
35614         }
35615         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35616         var panelId = panel.getId();
35617         this.panels.removeKey(panelId);
35618         if(preservePanel){
35619             document.body.appendChild(panel.getEl().dom);
35620         }
35621         if(this.tabs){
35622             this.tabs.removeTab(panel.getEl().id);
35623         }else if (!preservePanel){
35624             this.bodyEl.dom.removeChild(panel.getEl().dom);
35625         }
35626         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35627             var p = this.panels.first();
35628             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35629             tempEl.appendChild(p.getEl().dom);
35630             this.bodyEl.update("");
35631             this.bodyEl.dom.appendChild(p.getEl().dom);
35632             tempEl = null;
35633             this.updateTitle(p.getTitle());
35634             this.tabs = null;
35635             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35636             this.setActivePanel(p);
35637         }
35638         panel.setRegion(null);
35639         if(this.activePanel == panel){
35640             this.activePanel = null;
35641         }
35642         if(this.config.autoDestroy !== false && preservePanel !== true){
35643             try{panel.destroy();}catch(e){}
35644         }
35645         this.fireEvent("panelremoved", this, panel);
35646         return panel;
35647     },
35648
35649     /**
35650      * Returns the TabPanel component used by this region
35651      * @return {Roo.TabPanel}
35652      */
35653     getTabs : function(){
35654         return this.tabs;
35655     },
35656
35657     createTool : function(parentEl, className){
35658         var btn = Roo.DomHelper.append(parentEl, {
35659             tag: "div",
35660             cls: "x-layout-tools-button",
35661             children: [ {
35662                 tag: "div",
35663                 cls: "roo-layout-tools-button-inner " + className,
35664                 html: "&#160;"
35665             }]
35666         }, true);
35667         btn.addClassOnOver("roo-layout-tools-button-over");
35668         return btn;
35669     }
35670 });/*
35671  * Based on:
35672  * Ext JS Library 1.1.1
35673  * Copyright(c) 2006-2007, Ext JS, LLC.
35674  *
35675  * Originally Released Under LGPL - original licence link has changed is not relivant.
35676  *
35677  * Fork - LGPL
35678  * <script type="text/javascript">
35679  */
35680  
35681
35682
35683 /**
35684  * @class Roo.SplitLayoutRegion
35685  * @extends Roo.LayoutRegion
35686  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35687  */
35688 Roo.bootstrap.layout.Split = function(config){
35689     this.cursor = config.cursor;
35690     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35691 };
35692
35693 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35694 {
35695     splitTip : "Drag to resize.",
35696     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35697     useSplitTips : false,
35698
35699     applyConfig : function(config){
35700         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35701     },
35702     
35703     onRender : function(ctr,pos) {
35704         
35705         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35706         if(!this.config.split){
35707             return;
35708         }
35709         if(!this.split){
35710             
35711             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35712                             tag: "div",
35713                             id: this.el.id + "-split",
35714                             cls: "roo-layout-split roo-layout-split-"+this.position,
35715                             html: "&#160;"
35716             });
35717             /** The SplitBar for this region 
35718             * @type Roo.SplitBar */
35719             // does not exist yet...
35720             Roo.log([this.position, this.orientation]);
35721             
35722             this.split = new Roo.bootstrap.SplitBar({
35723                 dragElement : splitEl,
35724                 resizingElement: this.el,
35725                 orientation : this.orientation
35726             });
35727             
35728             this.split.on("moved", this.onSplitMove, this);
35729             this.split.useShim = this.config.useShim === true;
35730             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35731             if(this.useSplitTips){
35732                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35733             }
35734             //if(config.collapsible){
35735             //    this.split.el.on("dblclick", this.collapse,  this);
35736             //}
35737         }
35738         if(typeof this.config.minSize != "undefined"){
35739             this.split.minSize = this.config.minSize;
35740         }
35741         if(typeof this.config.maxSize != "undefined"){
35742             this.split.maxSize = this.config.maxSize;
35743         }
35744         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35745             this.hideSplitter();
35746         }
35747         
35748     },
35749
35750     getHMaxSize : function(){
35751          var cmax = this.config.maxSize || 10000;
35752          var center = this.mgr.getRegion("center");
35753          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35754     },
35755
35756     getVMaxSize : function(){
35757          var cmax = this.config.maxSize || 10000;
35758          var center = this.mgr.getRegion("center");
35759          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35760     },
35761
35762     onSplitMove : function(split, newSize){
35763         this.fireEvent("resized", this, newSize);
35764     },
35765     
35766     /** 
35767      * Returns the {@link Roo.SplitBar} for this region.
35768      * @return {Roo.SplitBar}
35769      */
35770     getSplitBar : function(){
35771         return this.split;
35772     },
35773     
35774     hide : function(){
35775         this.hideSplitter();
35776         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35777     },
35778
35779     hideSplitter : function(){
35780         if(this.split){
35781             this.split.el.setLocation(-2000,-2000);
35782             this.split.el.hide();
35783         }
35784     },
35785
35786     show : function(){
35787         if(this.split){
35788             this.split.el.show();
35789         }
35790         Roo.bootstrap.layout.Split.superclass.show.call(this);
35791     },
35792     
35793     beforeSlide: function(){
35794         if(Roo.isGecko){// firefox overflow auto bug workaround
35795             this.bodyEl.clip();
35796             if(this.tabs) {
35797                 this.tabs.bodyEl.clip();
35798             }
35799             if(this.activePanel){
35800                 this.activePanel.getEl().clip();
35801                 
35802                 if(this.activePanel.beforeSlide){
35803                     this.activePanel.beforeSlide();
35804                 }
35805             }
35806         }
35807     },
35808     
35809     afterSlide : function(){
35810         if(Roo.isGecko){// firefox overflow auto bug workaround
35811             this.bodyEl.unclip();
35812             if(this.tabs) {
35813                 this.tabs.bodyEl.unclip();
35814             }
35815             if(this.activePanel){
35816                 this.activePanel.getEl().unclip();
35817                 if(this.activePanel.afterSlide){
35818                     this.activePanel.afterSlide();
35819                 }
35820             }
35821         }
35822     },
35823
35824     initAutoHide : function(){
35825         if(this.autoHide !== false){
35826             if(!this.autoHideHd){
35827                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35828                 this.autoHideHd = {
35829                     "mouseout": function(e){
35830                         if(!e.within(this.el, true)){
35831                             st.delay(500);
35832                         }
35833                     },
35834                     "mouseover" : function(e){
35835                         st.cancel();
35836                     },
35837                     scope : this
35838                 };
35839             }
35840             this.el.on(this.autoHideHd);
35841         }
35842     },
35843
35844     clearAutoHide : function(){
35845         if(this.autoHide !== false){
35846             this.el.un("mouseout", this.autoHideHd.mouseout);
35847             this.el.un("mouseover", this.autoHideHd.mouseover);
35848         }
35849     },
35850
35851     clearMonitor : function(){
35852         Roo.get(document).un("click", this.slideInIf, this);
35853     },
35854
35855     // these names are backwards but not changed for compat
35856     slideOut : function(){
35857         if(this.isSlid || this.el.hasActiveFx()){
35858             return;
35859         }
35860         this.isSlid = true;
35861         if(this.collapseBtn){
35862             this.collapseBtn.hide();
35863         }
35864         this.closeBtnState = this.closeBtn.getStyle('display');
35865         this.closeBtn.hide();
35866         if(this.stickBtn){
35867             this.stickBtn.show();
35868         }
35869         this.el.show();
35870         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35871         this.beforeSlide();
35872         this.el.setStyle("z-index", 10001);
35873         this.el.slideIn(this.getSlideAnchor(), {
35874             callback: function(){
35875                 this.afterSlide();
35876                 this.initAutoHide();
35877                 Roo.get(document).on("click", this.slideInIf, this);
35878                 this.fireEvent("slideshow", this);
35879             },
35880             scope: this,
35881             block: true
35882         });
35883     },
35884
35885     afterSlideIn : function(){
35886         this.clearAutoHide();
35887         this.isSlid = false;
35888         this.clearMonitor();
35889         this.el.setStyle("z-index", "");
35890         if(this.collapseBtn){
35891             this.collapseBtn.show();
35892         }
35893         this.closeBtn.setStyle('display', this.closeBtnState);
35894         if(this.stickBtn){
35895             this.stickBtn.hide();
35896         }
35897         this.fireEvent("slidehide", this);
35898     },
35899
35900     slideIn : function(cb){
35901         if(!this.isSlid || this.el.hasActiveFx()){
35902             Roo.callback(cb);
35903             return;
35904         }
35905         this.isSlid = false;
35906         this.beforeSlide();
35907         this.el.slideOut(this.getSlideAnchor(), {
35908             callback: function(){
35909                 this.el.setLeftTop(-10000, -10000);
35910                 this.afterSlide();
35911                 this.afterSlideIn();
35912                 Roo.callback(cb);
35913             },
35914             scope: this,
35915             block: true
35916         });
35917     },
35918     
35919     slideInIf : function(e){
35920         if(!e.within(this.el)){
35921             this.slideIn();
35922         }
35923     },
35924
35925     animateCollapse : function(){
35926         this.beforeSlide();
35927         this.el.setStyle("z-index", 20000);
35928         var anchor = this.getSlideAnchor();
35929         this.el.slideOut(anchor, {
35930             callback : function(){
35931                 this.el.setStyle("z-index", "");
35932                 this.collapsedEl.slideIn(anchor, {duration:.3});
35933                 this.afterSlide();
35934                 this.el.setLocation(-10000,-10000);
35935                 this.el.hide();
35936                 this.fireEvent("collapsed", this);
35937             },
35938             scope: this,
35939             block: true
35940         });
35941     },
35942
35943     animateExpand : function(){
35944         this.beforeSlide();
35945         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35946         this.el.setStyle("z-index", 20000);
35947         this.collapsedEl.hide({
35948             duration:.1
35949         });
35950         this.el.slideIn(this.getSlideAnchor(), {
35951             callback : function(){
35952                 this.el.setStyle("z-index", "");
35953                 this.afterSlide();
35954                 if(this.split){
35955                     this.split.el.show();
35956                 }
35957                 this.fireEvent("invalidated", this);
35958                 this.fireEvent("expanded", this);
35959             },
35960             scope: this,
35961             block: true
35962         });
35963     },
35964
35965     anchors : {
35966         "west" : "left",
35967         "east" : "right",
35968         "north" : "top",
35969         "south" : "bottom"
35970     },
35971
35972     sanchors : {
35973         "west" : "l",
35974         "east" : "r",
35975         "north" : "t",
35976         "south" : "b"
35977     },
35978
35979     canchors : {
35980         "west" : "tl-tr",
35981         "east" : "tr-tl",
35982         "north" : "tl-bl",
35983         "south" : "bl-tl"
35984     },
35985
35986     getAnchor : function(){
35987         return this.anchors[this.position];
35988     },
35989
35990     getCollapseAnchor : function(){
35991         return this.canchors[this.position];
35992     },
35993
35994     getSlideAnchor : function(){
35995         return this.sanchors[this.position];
35996     },
35997
35998     getAlignAdj : function(){
35999         var cm = this.cmargins;
36000         switch(this.position){
36001             case "west":
36002                 return [0, 0];
36003             break;
36004             case "east":
36005                 return [0, 0];
36006             break;
36007             case "north":
36008                 return [0, 0];
36009             break;
36010             case "south":
36011                 return [0, 0];
36012             break;
36013         }
36014     },
36015
36016     getExpandAdj : function(){
36017         var c = this.collapsedEl, cm = this.cmargins;
36018         switch(this.position){
36019             case "west":
36020                 return [-(cm.right+c.getWidth()+cm.left), 0];
36021             break;
36022             case "east":
36023                 return [cm.right+c.getWidth()+cm.left, 0];
36024             break;
36025             case "north":
36026                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36027             break;
36028             case "south":
36029                 return [0, cm.top+cm.bottom+c.getHeight()];
36030             break;
36031         }
36032     }
36033 });/*
36034  * Based on:
36035  * Ext JS Library 1.1.1
36036  * Copyright(c) 2006-2007, Ext JS, LLC.
36037  *
36038  * Originally Released Under LGPL - original licence link has changed is not relivant.
36039  *
36040  * Fork - LGPL
36041  * <script type="text/javascript">
36042  */
36043 /*
36044  * These classes are private internal classes
36045  */
36046 Roo.bootstrap.layout.Center = function(config){
36047     config.region = "center";
36048     Roo.bootstrap.layout.Region.call(this, config);
36049     this.visible = true;
36050     this.minWidth = config.minWidth || 20;
36051     this.minHeight = config.minHeight || 20;
36052 };
36053
36054 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36055     hide : function(){
36056         // center panel can't be hidden
36057     },
36058     
36059     show : function(){
36060         // center panel can't be hidden
36061     },
36062     
36063     getMinWidth: function(){
36064         return this.minWidth;
36065     },
36066     
36067     getMinHeight: function(){
36068         return this.minHeight;
36069     }
36070 });
36071
36072
36073
36074
36075  
36076
36077
36078
36079
36080
36081 Roo.bootstrap.layout.North = function(config)
36082 {
36083     config.region = 'north';
36084     config.cursor = 'n-resize';
36085     
36086     Roo.bootstrap.layout.Split.call(this, config);
36087     
36088     
36089     if(this.split){
36090         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36091         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36092         this.split.el.addClass("roo-layout-split-v");
36093     }
36094     var size = config.initialSize || config.height;
36095     if(typeof size != "undefined"){
36096         this.el.setHeight(size);
36097     }
36098 };
36099 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36100 {
36101     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36102     
36103     
36104     
36105     getBox : function(){
36106         if(this.collapsed){
36107             return this.collapsedEl.getBox();
36108         }
36109         var box = this.el.getBox();
36110         if(this.split){
36111             box.height += this.split.el.getHeight();
36112         }
36113         return box;
36114     },
36115     
36116     updateBox : function(box){
36117         if(this.split && !this.collapsed){
36118             box.height -= this.split.el.getHeight();
36119             this.split.el.setLeft(box.x);
36120             this.split.el.setTop(box.y+box.height);
36121             this.split.el.setWidth(box.width);
36122         }
36123         if(this.collapsed){
36124             this.updateBody(box.width, null);
36125         }
36126         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36127     }
36128 });
36129
36130
36131
36132
36133
36134 Roo.bootstrap.layout.South = function(config){
36135     config.region = 'south';
36136     config.cursor = 's-resize';
36137     Roo.bootstrap.layout.Split.call(this, config);
36138     if(this.split){
36139         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36140         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36141         this.split.el.addClass("roo-layout-split-v");
36142     }
36143     var size = config.initialSize || config.height;
36144     if(typeof size != "undefined"){
36145         this.el.setHeight(size);
36146     }
36147 };
36148
36149 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36150     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36151     getBox : function(){
36152         if(this.collapsed){
36153             return this.collapsedEl.getBox();
36154         }
36155         var box = this.el.getBox();
36156         if(this.split){
36157             var sh = this.split.el.getHeight();
36158             box.height += sh;
36159             box.y -= sh;
36160         }
36161         return box;
36162     },
36163     
36164     updateBox : function(box){
36165         if(this.split && !this.collapsed){
36166             var sh = this.split.el.getHeight();
36167             box.height -= sh;
36168             box.y += sh;
36169             this.split.el.setLeft(box.x);
36170             this.split.el.setTop(box.y-sh);
36171             this.split.el.setWidth(box.width);
36172         }
36173         if(this.collapsed){
36174             this.updateBody(box.width, null);
36175         }
36176         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36177     }
36178 });
36179
36180 Roo.bootstrap.layout.East = function(config){
36181     config.region = "east";
36182     config.cursor = "e-resize";
36183     Roo.bootstrap.layout.Split.call(this, config);
36184     if(this.split){
36185         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36186         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36187         this.split.el.addClass("roo-layout-split-h");
36188     }
36189     var size = config.initialSize || config.width;
36190     if(typeof size != "undefined"){
36191         this.el.setWidth(size);
36192     }
36193 };
36194 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36195     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36196     getBox : function(){
36197         if(this.collapsed){
36198             return this.collapsedEl.getBox();
36199         }
36200         var box = this.el.getBox();
36201         if(this.split){
36202             var sw = this.split.el.getWidth();
36203             box.width += sw;
36204             box.x -= sw;
36205         }
36206         return box;
36207     },
36208
36209     updateBox : function(box){
36210         if(this.split && !this.collapsed){
36211             var sw = this.split.el.getWidth();
36212             box.width -= sw;
36213             this.split.el.setLeft(box.x);
36214             this.split.el.setTop(box.y);
36215             this.split.el.setHeight(box.height);
36216             box.x += sw;
36217         }
36218         if(this.collapsed){
36219             this.updateBody(null, box.height);
36220         }
36221         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36222     }
36223 });
36224
36225 Roo.bootstrap.layout.West = function(config){
36226     config.region = "west";
36227     config.cursor = "w-resize";
36228     
36229     Roo.bootstrap.layout.Split.call(this, config);
36230     if(this.split){
36231         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36232         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36233         this.split.el.addClass("roo-layout-split-h");
36234     }
36235     
36236 };
36237 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36238     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36239     
36240     onRender: function(ctr, pos)
36241     {
36242         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36243         var size = this.config.initialSize || this.config.width;
36244         if(typeof size != "undefined"){
36245             this.el.setWidth(size);
36246         }
36247     },
36248     
36249     getBox : function(){
36250         if(this.collapsed){
36251             return this.collapsedEl.getBox();
36252         }
36253         var box = this.el.getBox();
36254         if(this.split){
36255             box.width += this.split.el.getWidth();
36256         }
36257         return box;
36258     },
36259     
36260     updateBox : function(box){
36261         if(this.split && !this.collapsed){
36262             var sw = this.split.el.getWidth();
36263             box.width -= sw;
36264             this.split.el.setLeft(box.x+box.width);
36265             this.split.el.setTop(box.y);
36266             this.split.el.setHeight(box.height);
36267         }
36268         if(this.collapsed){
36269             this.updateBody(null, box.height);
36270         }
36271         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36272     }
36273 });
36274 Roo.namespace("Roo.bootstrap.panel");/*
36275  * Based on:
36276  * Ext JS Library 1.1.1
36277  * Copyright(c) 2006-2007, Ext JS, LLC.
36278  *
36279  * Originally Released Under LGPL - original licence link has changed is not relivant.
36280  *
36281  * Fork - LGPL
36282  * <script type="text/javascript">
36283  */
36284 /**
36285  * @class Roo.ContentPanel
36286  * @extends Roo.util.Observable
36287  * A basic ContentPanel element.
36288  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36289  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36290  * @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
36291  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36292  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36293  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36294  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36295  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36296  * @cfg {String} title          The title for this panel
36297  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36298  * @cfg {String} url            Calls {@link #setUrl} with this value
36299  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36300  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36301  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36302  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36303  * @cfg {Boolean} badges render the badges
36304
36305  * @constructor
36306  * Create a new ContentPanel.
36307  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36308  * @param {String/Object} config A string to set only the title or a config object
36309  * @param {String} content (optional) Set the HTML content for this panel
36310  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36311  */
36312 Roo.bootstrap.panel.Content = function( config){
36313     
36314     this.tpl = config.tpl || false;
36315     
36316     var el = config.el;
36317     var content = config.content;
36318
36319     if(config.autoCreate){ // xtype is available if this is called from factory
36320         el = Roo.id();
36321     }
36322     this.el = Roo.get(el);
36323     if(!this.el && config && config.autoCreate){
36324         if(typeof config.autoCreate == "object"){
36325             if(!config.autoCreate.id){
36326                 config.autoCreate.id = config.id||el;
36327             }
36328             this.el = Roo.DomHelper.append(document.body,
36329                         config.autoCreate, true);
36330         }else{
36331             var elcfg =  {   tag: "div",
36332                             cls: "roo-layout-inactive-content",
36333                             id: config.id||el
36334                             };
36335             if (config.html) {
36336                 elcfg.html = config.html;
36337                 
36338             }
36339                         
36340             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36341         }
36342     } 
36343     this.closable = false;
36344     this.loaded = false;
36345     this.active = false;
36346    
36347       
36348     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36349         
36350         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36351         
36352         this.wrapEl = this.el; //this.el.wrap();
36353         var ti = [];
36354         if (config.toolbar.items) {
36355             ti = config.toolbar.items ;
36356             delete config.toolbar.items ;
36357         }
36358         
36359         var nitems = [];
36360         this.toolbar.render(this.wrapEl, 'before');
36361         for(var i =0;i < ti.length;i++) {
36362           //  Roo.log(['add child', items[i]]);
36363             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36364         }
36365         this.toolbar.items = nitems;
36366         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36367         delete config.toolbar;
36368         
36369     }
36370     /*
36371     // xtype created footer. - not sure if will work as we normally have to render first..
36372     if (this.footer && !this.footer.el && this.footer.xtype) {
36373         if (!this.wrapEl) {
36374             this.wrapEl = this.el.wrap();
36375         }
36376     
36377         this.footer.container = this.wrapEl.createChild();
36378          
36379         this.footer = Roo.factory(this.footer, Roo);
36380         
36381     }
36382     */
36383     
36384      if(typeof config == "string"){
36385         this.title = config;
36386     }else{
36387         Roo.apply(this, config);
36388     }
36389     
36390     if(this.resizeEl){
36391         this.resizeEl = Roo.get(this.resizeEl, true);
36392     }else{
36393         this.resizeEl = this.el;
36394     }
36395     // handle view.xtype
36396     
36397  
36398     
36399     
36400     this.addEvents({
36401         /**
36402          * @event activate
36403          * Fires when this panel is activated. 
36404          * @param {Roo.ContentPanel} this
36405          */
36406         "activate" : true,
36407         /**
36408          * @event deactivate
36409          * Fires when this panel is activated. 
36410          * @param {Roo.ContentPanel} this
36411          */
36412         "deactivate" : true,
36413
36414         /**
36415          * @event resize
36416          * Fires when this panel is resized if fitToFrame is true.
36417          * @param {Roo.ContentPanel} this
36418          * @param {Number} width The width after any component adjustments
36419          * @param {Number} height The height after any component adjustments
36420          */
36421         "resize" : true,
36422         
36423          /**
36424          * @event render
36425          * Fires when this tab is created
36426          * @param {Roo.ContentPanel} this
36427          */
36428         "render" : true
36429         
36430         
36431         
36432     });
36433     
36434
36435     
36436     
36437     if(this.autoScroll){
36438         this.resizeEl.setStyle("overflow", "auto");
36439     } else {
36440         // fix randome scrolling
36441         //this.el.on('scroll', function() {
36442         //    Roo.log('fix random scolling');
36443         //    this.scrollTo('top',0); 
36444         //});
36445     }
36446     content = content || this.content;
36447     if(content){
36448         this.setContent(content);
36449     }
36450     if(config && config.url){
36451         this.setUrl(this.url, this.params, this.loadOnce);
36452     }
36453     
36454     
36455     
36456     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36457     
36458     if (this.view && typeof(this.view.xtype) != 'undefined') {
36459         this.view.el = this.el.appendChild(document.createElement("div"));
36460         this.view = Roo.factory(this.view); 
36461         this.view.render  &&  this.view.render(false, '');  
36462     }
36463     
36464     
36465     this.fireEvent('render', this);
36466 };
36467
36468 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36469     
36470     tabTip : '',
36471     
36472     setRegion : function(region){
36473         this.region = region;
36474         this.setActiveClass(region && !this.background);
36475     },
36476     
36477     
36478     setActiveClass: function(state)
36479     {
36480         if(state){
36481            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36482            this.el.setStyle('position','relative');
36483         }else{
36484            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36485            this.el.setStyle('position', 'absolute');
36486         } 
36487     },
36488     
36489     /**
36490      * Returns the toolbar for this Panel if one was configured. 
36491      * @return {Roo.Toolbar} 
36492      */
36493     getToolbar : function(){
36494         return this.toolbar;
36495     },
36496     
36497     setActiveState : function(active)
36498     {
36499         this.active = active;
36500         this.setActiveClass(active);
36501         if(!active){
36502             if(this.fireEvent("deactivate", this) === false){
36503                 return false;
36504             }
36505             return true;
36506         }
36507         this.fireEvent("activate", this);
36508         return true;
36509     },
36510     /**
36511      * Updates this panel's element
36512      * @param {String} content The new content
36513      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36514     */
36515     setContent : function(content, loadScripts){
36516         this.el.update(content, loadScripts);
36517     },
36518
36519     ignoreResize : function(w, h){
36520         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36521             return true;
36522         }else{
36523             this.lastSize = {width: w, height: h};
36524             return false;
36525         }
36526     },
36527     /**
36528      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36529      * @return {Roo.UpdateManager} The UpdateManager
36530      */
36531     getUpdateManager : function(){
36532         return this.el.getUpdateManager();
36533     },
36534      /**
36535      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36536      * @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:
36537 <pre><code>
36538 panel.load({
36539     url: "your-url.php",
36540     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36541     callback: yourFunction,
36542     scope: yourObject, //(optional scope)
36543     discardUrl: false,
36544     nocache: false,
36545     text: "Loading...",
36546     timeout: 30,
36547     scripts: false
36548 });
36549 </code></pre>
36550      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36551      * 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.
36552      * @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}
36553      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36554      * @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.
36555      * @return {Roo.ContentPanel} this
36556      */
36557     load : function(){
36558         var um = this.el.getUpdateManager();
36559         um.update.apply(um, arguments);
36560         return this;
36561     },
36562
36563
36564     /**
36565      * 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.
36566      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36567      * @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)
36568      * @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)
36569      * @return {Roo.UpdateManager} The UpdateManager
36570      */
36571     setUrl : function(url, params, loadOnce){
36572         if(this.refreshDelegate){
36573             this.removeListener("activate", this.refreshDelegate);
36574         }
36575         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36576         this.on("activate", this.refreshDelegate);
36577         return this.el.getUpdateManager();
36578     },
36579     
36580     _handleRefresh : function(url, params, loadOnce){
36581         if(!loadOnce || !this.loaded){
36582             var updater = this.el.getUpdateManager();
36583             updater.update(url, params, this._setLoaded.createDelegate(this));
36584         }
36585     },
36586     
36587     _setLoaded : function(){
36588         this.loaded = true;
36589     }, 
36590     
36591     /**
36592      * Returns this panel's id
36593      * @return {String} 
36594      */
36595     getId : function(){
36596         return this.el.id;
36597     },
36598     
36599     /** 
36600      * Returns this panel's element - used by regiosn to add.
36601      * @return {Roo.Element} 
36602      */
36603     getEl : function(){
36604         return this.wrapEl || this.el;
36605     },
36606     
36607    
36608     
36609     adjustForComponents : function(width, height)
36610     {
36611         //Roo.log('adjustForComponents ');
36612         if(this.resizeEl != this.el){
36613             width -= this.el.getFrameWidth('lr');
36614             height -= this.el.getFrameWidth('tb');
36615         }
36616         if(this.toolbar){
36617             var te = this.toolbar.getEl();
36618             te.setWidth(width);
36619             height -= te.getHeight();
36620         }
36621         if(this.footer){
36622             var te = this.footer.getEl();
36623             te.setWidth(width);
36624             height -= te.getHeight();
36625         }
36626         
36627         
36628         if(this.adjustments){
36629             width += this.adjustments[0];
36630             height += this.adjustments[1];
36631         }
36632         return {"width": width, "height": height};
36633     },
36634     
36635     setSize : function(width, height){
36636         if(this.fitToFrame && !this.ignoreResize(width, height)){
36637             if(this.fitContainer && this.resizeEl != this.el){
36638                 this.el.setSize(width, height);
36639             }
36640             var size = this.adjustForComponents(width, height);
36641             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36642             this.fireEvent('resize', this, size.width, size.height);
36643         }
36644     },
36645     
36646     /**
36647      * Returns this panel's title
36648      * @return {String} 
36649      */
36650     getTitle : function(){
36651         
36652         if (typeof(this.title) != 'object') {
36653             return this.title;
36654         }
36655         
36656         var t = '';
36657         for (var k in this.title) {
36658             if (!this.title.hasOwnProperty(k)) {
36659                 continue;
36660             }
36661             
36662             if (k.indexOf('-') >= 0) {
36663                 var s = k.split('-');
36664                 for (var i = 0; i<s.length; i++) {
36665                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36666                 }
36667             } else {
36668                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36669             }
36670         }
36671         return t;
36672     },
36673     
36674     /**
36675      * Set this panel's title
36676      * @param {String} title
36677      */
36678     setTitle : function(title){
36679         this.title = title;
36680         if(this.region){
36681             this.region.updatePanelTitle(this, title);
36682         }
36683     },
36684     
36685     /**
36686      * Returns true is this panel was configured to be closable
36687      * @return {Boolean} 
36688      */
36689     isClosable : function(){
36690         return this.closable;
36691     },
36692     
36693     beforeSlide : function(){
36694         this.el.clip();
36695         this.resizeEl.clip();
36696     },
36697     
36698     afterSlide : function(){
36699         this.el.unclip();
36700         this.resizeEl.unclip();
36701     },
36702     
36703     /**
36704      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36705      *   Will fail silently if the {@link #setUrl} method has not been called.
36706      *   This does not activate the panel, just updates its content.
36707      */
36708     refresh : function(){
36709         if(this.refreshDelegate){
36710            this.loaded = false;
36711            this.refreshDelegate();
36712         }
36713     },
36714     
36715     /**
36716      * Destroys this panel
36717      */
36718     destroy : function(){
36719         this.el.removeAllListeners();
36720         var tempEl = document.createElement("span");
36721         tempEl.appendChild(this.el.dom);
36722         tempEl.innerHTML = "";
36723         this.el.remove();
36724         this.el = null;
36725     },
36726     
36727     /**
36728      * form - if the content panel contains a form - this is a reference to it.
36729      * @type {Roo.form.Form}
36730      */
36731     form : false,
36732     /**
36733      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36734      *    This contains a reference to it.
36735      * @type {Roo.View}
36736      */
36737     view : false,
36738     
36739       /**
36740      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36741      * <pre><code>
36742
36743 layout.addxtype({
36744        xtype : 'Form',
36745        items: [ .... ]
36746    }
36747 );
36748
36749 </code></pre>
36750      * @param {Object} cfg Xtype definition of item to add.
36751      */
36752     
36753     
36754     getChildContainer: function () {
36755         return this.getEl();
36756     }
36757     
36758     
36759     /*
36760         var  ret = new Roo.factory(cfg);
36761         return ret;
36762         
36763         
36764         // add form..
36765         if (cfg.xtype.match(/^Form$/)) {
36766             
36767             var el;
36768             //if (this.footer) {
36769             //    el = this.footer.container.insertSibling(false, 'before');
36770             //} else {
36771                 el = this.el.createChild();
36772             //}
36773
36774             this.form = new  Roo.form.Form(cfg);
36775             
36776             
36777             if ( this.form.allItems.length) {
36778                 this.form.render(el.dom);
36779             }
36780             return this.form;
36781         }
36782         // should only have one of theses..
36783         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36784             // views.. should not be just added - used named prop 'view''
36785             
36786             cfg.el = this.el.appendChild(document.createElement("div"));
36787             // factory?
36788             
36789             var ret = new Roo.factory(cfg);
36790              
36791              ret.render && ret.render(false, ''); // render blank..
36792             this.view = ret;
36793             return ret;
36794         }
36795         return false;
36796     }
36797     \*/
36798 });
36799  
36800 /**
36801  * @class Roo.bootstrap.panel.Grid
36802  * @extends Roo.bootstrap.panel.Content
36803  * @constructor
36804  * Create a new GridPanel.
36805  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36806  * @param {Object} config A the config object
36807   
36808  */
36809
36810
36811
36812 Roo.bootstrap.panel.Grid = function(config)
36813 {
36814     
36815       
36816     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36817         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36818
36819     config.el = this.wrapper;
36820     //this.el = this.wrapper;
36821     
36822       if (config.container) {
36823         // ctor'ed from a Border/panel.grid
36824         
36825         
36826         this.wrapper.setStyle("overflow", "hidden");
36827         this.wrapper.addClass('roo-grid-container');
36828
36829     }
36830     
36831     
36832     if(config.toolbar){
36833         var tool_el = this.wrapper.createChild();    
36834         this.toolbar = Roo.factory(config.toolbar);
36835         var ti = [];
36836         if (config.toolbar.items) {
36837             ti = config.toolbar.items ;
36838             delete config.toolbar.items ;
36839         }
36840         
36841         var nitems = [];
36842         this.toolbar.render(tool_el);
36843         for(var i =0;i < ti.length;i++) {
36844           //  Roo.log(['add child', items[i]]);
36845             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36846         }
36847         this.toolbar.items = nitems;
36848         
36849         delete config.toolbar;
36850     }
36851     
36852     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36853     config.grid.scrollBody = true;;
36854     config.grid.monitorWindowResize = false; // turn off autosizing
36855     config.grid.autoHeight = false;
36856     config.grid.autoWidth = false;
36857     
36858     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36859     
36860     if (config.background) {
36861         // render grid on panel activation (if panel background)
36862         this.on('activate', function(gp) {
36863             if (!gp.grid.rendered) {
36864                 gp.grid.render(this.wrapper);
36865                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36866             }
36867         });
36868             
36869     } else {
36870         this.grid.render(this.wrapper);
36871         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36872
36873     }
36874     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36875     // ??? needed ??? config.el = this.wrapper;
36876     
36877     
36878     
36879   
36880     // xtype created footer. - not sure if will work as we normally have to render first..
36881     if (this.footer && !this.footer.el && this.footer.xtype) {
36882         
36883         var ctr = this.grid.getView().getFooterPanel(true);
36884         this.footer.dataSource = this.grid.dataSource;
36885         this.footer = Roo.factory(this.footer, Roo);
36886         this.footer.render(ctr);
36887         
36888     }
36889     
36890     
36891     
36892     
36893      
36894 };
36895
36896 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36897     getId : function(){
36898         return this.grid.id;
36899     },
36900     
36901     /**
36902      * Returns the grid for this panel
36903      * @return {Roo.bootstrap.Table} 
36904      */
36905     getGrid : function(){
36906         return this.grid;    
36907     },
36908     
36909     setSize : function(width, height){
36910         if(!this.ignoreResize(width, height)){
36911             var grid = this.grid;
36912             var size = this.adjustForComponents(width, height);
36913             var gridel = grid.getGridEl();
36914             gridel.setSize(size.width, size.height);
36915             /*
36916             var thd = grid.getGridEl().select('thead',true).first();
36917             var tbd = grid.getGridEl().select('tbody', true).first();
36918             if (tbd) {
36919                 tbd.setSize(width, height - thd.getHeight());
36920             }
36921             */
36922             grid.autoSize();
36923         }
36924     },
36925      
36926     
36927     
36928     beforeSlide : function(){
36929         this.grid.getView().scroller.clip();
36930     },
36931     
36932     afterSlide : function(){
36933         this.grid.getView().scroller.unclip();
36934     },
36935     
36936     destroy : function(){
36937         this.grid.destroy();
36938         delete this.grid;
36939         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36940     }
36941 });
36942
36943 /**
36944  * @class Roo.bootstrap.panel.Nest
36945  * @extends Roo.bootstrap.panel.Content
36946  * @constructor
36947  * Create a new Panel, that can contain a layout.Border.
36948  * 
36949  * 
36950  * @param {Roo.BorderLayout} layout The layout for this panel
36951  * @param {String/Object} config A string to set only the title or a config object
36952  */
36953 Roo.bootstrap.panel.Nest = function(config)
36954 {
36955     // construct with only one argument..
36956     /* FIXME - implement nicer consturctors
36957     if (layout.layout) {
36958         config = layout;
36959         layout = config.layout;
36960         delete config.layout;
36961     }
36962     if (layout.xtype && !layout.getEl) {
36963         // then layout needs constructing..
36964         layout = Roo.factory(layout, Roo);
36965     }
36966     */
36967     
36968     config.el =  config.layout.getEl();
36969     
36970     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36971     
36972     config.layout.monitorWindowResize = false; // turn off autosizing
36973     this.layout = config.layout;
36974     this.layout.getEl().addClass("roo-layout-nested-layout");
36975     
36976     
36977     
36978     
36979 };
36980
36981 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36982
36983     setSize : function(width, height){
36984         if(!this.ignoreResize(width, height)){
36985             var size = this.adjustForComponents(width, height);
36986             var el = this.layout.getEl();
36987             if (size.height < 1) {
36988                 el.setWidth(size.width);   
36989             } else {
36990                 el.setSize(size.width, size.height);
36991             }
36992             var touch = el.dom.offsetWidth;
36993             this.layout.layout();
36994             // ie requires a double layout on the first pass
36995             if(Roo.isIE && !this.initialized){
36996                 this.initialized = true;
36997                 this.layout.layout();
36998             }
36999         }
37000     },
37001     
37002     // activate all subpanels if not currently active..
37003     
37004     setActiveState : function(active){
37005         this.active = active;
37006         this.setActiveClass(active);
37007         
37008         if(!active){
37009             this.fireEvent("deactivate", this);
37010             return;
37011         }
37012         
37013         this.fireEvent("activate", this);
37014         // not sure if this should happen before or after..
37015         if (!this.layout) {
37016             return; // should not happen..
37017         }
37018         var reg = false;
37019         for (var r in this.layout.regions) {
37020             reg = this.layout.getRegion(r);
37021             if (reg.getActivePanel()) {
37022                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37023                 reg.setActivePanel(reg.getActivePanel());
37024                 continue;
37025             }
37026             if (!reg.panels.length) {
37027                 continue;
37028             }
37029             reg.showPanel(reg.getPanel(0));
37030         }
37031         
37032         
37033         
37034         
37035     },
37036     
37037     /**
37038      * Returns the nested BorderLayout for this panel
37039      * @return {Roo.BorderLayout} 
37040      */
37041     getLayout : function(){
37042         return this.layout;
37043     },
37044     
37045      /**
37046      * Adds a xtype elements to the layout of the nested panel
37047      * <pre><code>
37048
37049 panel.addxtype({
37050        xtype : 'ContentPanel',
37051        region: 'west',
37052        items: [ .... ]
37053    }
37054 );
37055
37056 panel.addxtype({
37057         xtype : 'NestedLayoutPanel',
37058         region: 'west',
37059         layout: {
37060            center: { },
37061            west: { }   
37062         },
37063         items : [ ... list of content panels or nested layout panels.. ]
37064    }
37065 );
37066 </code></pre>
37067      * @param {Object} cfg Xtype definition of item to add.
37068      */
37069     addxtype : function(cfg) {
37070         return this.layout.addxtype(cfg);
37071     
37072     }
37073 });        /*
37074  * Based on:
37075  * Ext JS Library 1.1.1
37076  * Copyright(c) 2006-2007, Ext JS, LLC.
37077  *
37078  * Originally Released Under LGPL - original licence link has changed is not relivant.
37079  *
37080  * Fork - LGPL
37081  * <script type="text/javascript">
37082  */
37083 /**
37084  * @class Roo.TabPanel
37085  * @extends Roo.util.Observable
37086  * A lightweight tab container.
37087  * <br><br>
37088  * Usage:
37089  * <pre><code>
37090 // basic tabs 1, built from existing content
37091 var tabs = new Roo.TabPanel("tabs1");
37092 tabs.addTab("script", "View Script");
37093 tabs.addTab("markup", "View Markup");
37094 tabs.activate("script");
37095
37096 // more advanced tabs, built from javascript
37097 var jtabs = new Roo.TabPanel("jtabs");
37098 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37099
37100 // set up the UpdateManager
37101 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37102 var updater = tab2.getUpdateManager();
37103 updater.setDefaultUrl("ajax1.htm");
37104 tab2.on('activate', updater.refresh, updater, true);
37105
37106 // Use setUrl for Ajax loading
37107 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37108 tab3.setUrl("ajax2.htm", null, true);
37109
37110 // Disabled tab
37111 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37112 tab4.disable();
37113
37114 jtabs.activate("jtabs-1");
37115  * </code></pre>
37116  * @constructor
37117  * Create a new TabPanel.
37118  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37119  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37120  */
37121 Roo.bootstrap.panel.Tabs = function(config){
37122     /**
37123     * The container element for this TabPanel.
37124     * @type Roo.Element
37125     */
37126     this.el = Roo.get(config.el);
37127     delete config.el;
37128     if(config){
37129         if(typeof config == "boolean"){
37130             this.tabPosition = config ? "bottom" : "top";
37131         }else{
37132             Roo.apply(this, config);
37133         }
37134     }
37135     
37136     if(this.tabPosition == "bottom"){
37137         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37138         this.el.addClass("roo-tabs-bottom");
37139     }
37140     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37141     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37142     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37143     if(Roo.isIE){
37144         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37145     }
37146     if(this.tabPosition != "bottom"){
37147         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37148          * @type Roo.Element
37149          */
37150         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37151         this.el.addClass("roo-tabs-top");
37152     }
37153     this.items = [];
37154
37155     this.bodyEl.setStyle("position", "relative");
37156
37157     this.active = null;
37158     this.activateDelegate = this.activate.createDelegate(this);
37159
37160     this.addEvents({
37161         /**
37162          * @event tabchange
37163          * Fires when the active tab changes
37164          * @param {Roo.TabPanel} this
37165          * @param {Roo.TabPanelItem} activePanel The new active tab
37166          */
37167         "tabchange": true,
37168         /**
37169          * @event beforetabchange
37170          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37171          * @param {Roo.TabPanel} this
37172          * @param {Object} e Set cancel to true on this object to cancel the tab change
37173          * @param {Roo.TabPanelItem} tab The tab being changed to
37174          */
37175         "beforetabchange" : true
37176     });
37177
37178     Roo.EventManager.onWindowResize(this.onResize, this);
37179     this.cpad = this.el.getPadding("lr");
37180     this.hiddenCount = 0;
37181
37182
37183     // toolbar on the tabbar support...
37184     if (this.toolbar) {
37185         alert("no toolbar support yet");
37186         this.toolbar  = false;
37187         /*
37188         var tcfg = this.toolbar;
37189         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37190         this.toolbar = new Roo.Toolbar(tcfg);
37191         if (Roo.isSafari) {
37192             var tbl = tcfg.container.child('table', true);
37193             tbl.setAttribute('width', '100%');
37194         }
37195         */
37196         
37197     }
37198    
37199
37200
37201     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37202 };
37203
37204 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37205     /*
37206      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37207      */
37208     tabPosition : "top",
37209     /*
37210      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37211      */
37212     currentTabWidth : 0,
37213     /*
37214      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37215      */
37216     minTabWidth : 40,
37217     /*
37218      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37219      */
37220     maxTabWidth : 250,
37221     /*
37222      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37223      */
37224     preferredTabWidth : 175,
37225     /*
37226      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37227      */
37228     resizeTabs : false,
37229     /*
37230      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37231      */
37232     monitorResize : true,
37233     /*
37234      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37235      */
37236     toolbar : false,
37237
37238     /**
37239      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37240      * @param {String} id The id of the div to use <b>or create</b>
37241      * @param {String} text The text for the tab
37242      * @param {String} content (optional) Content to put in the TabPanelItem body
37243      * @param {Boolean} closable (optional) True to create a close icon on the tab
37244      * @return {Roo.TabPanelItem} The created TabPanelItem
37245      */
37246     addTab : function(id, text, content, closable, tpl)
37247     {
37248         var item = new Roo.bootstrap.panel.TabItem({
37249             panel: this,
37250             id : id,
37251             text : text,
37252             closable : closable,
37253             tpl : tpl
37254         });
37255         this.addTabItem(item);
37256         if(content){
37257             item.setContent(content);
37258         }
37259         return item;
37260     },
37261
37262     /**
37263      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37264      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37265      * @return {Roo.TabPanelItem}
37266      */
37267     getTab : function(id){
37268         return this.items[id];
37269     },
37270
37271     /**
37272      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37273      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37274      */
37275     hideTab : function(id){
37276         var t = this.items[id];
37277         if(!t.isHidden()){
37278            t.setHidden(true);
37279            this.hiddenCount++;
37280            this.autoSizeTabs();
37281         }
37282     },
37283
37284     /**
37285      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37286      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37287      */
37288     unhideTab : function(id){
37289         var t = this.items[id];
37290         if(t.isHidden()){
37291            t.setHidden(false);
37292            this.hiddenCount--;
37293            this.autoSizeTabs();
37294         }
37295     },
37296
37297     /**
37298      * Adds an existing {@link Roo.TabPanelItem}.
37299      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37300      */
37301     addTabItem : function(item){
37302         this.items[item.id] = item;
37303         this.items.push(item);
37304       //  if(this.resizeTabs){
37305     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37306   //         this.autoSizeTabs();
37307 //        }else{
37308 //            item.autoSize();
37309        // }
37310     },
37311
37312     /**
37313      * Removes a {@link Roo.TabPanelItem}.
37314      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37315      */
37316     removeTab : function(id){
37317         var items = this.items;
37318         var tab = items[id];
37319         if(!tab) { return; }
37320         var index = items.indexOf(tab);
37321         if(this.active == tab && items.length > 1){
37322             var newTab = this.getNextAvailable(index);
37323             if(newTab) {
37324                 newTab.activate();
37325             }
37326         }
37327         this.stripEl.dom.removeChild(tab.pnode.dom);
37328         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37329             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37330         }
37331         items.splice(index, 1);
37332         delete this.items[tab.id];
37333         tab.fireEvent("close", tab);
37334         tab.purgeListeners();
37335         this.autoSizeTabs();
37336     },
37337
37338     getNextAvailable : function(start){
37339         var items = this.items;
37340         var index = start;
37341         // look for a next tab that will slide over to
37342         // replace the one being removed
37343         while(index < items.length){
37344             var item = items[++index];
37345             if(item && !item.isHidden()){
37346                 return item;
37347             }
37348         }
37349         // if one isn't found select the previous tab (on the left)
37350         index = start;
37351         while(index >= 0){
37352             var item = items[--index];
37353             if(item && !item.isHidden()){
37354                 return item;
37355             }
37356         }
37357         return null;
37358     },
37359
37360     /**
37361      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37362      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37363      */
37364     disableTab : function(id){
37365         var tab = this.items[id];
37366         if(tab && this.active != tab){
37367             tab.disable();
37368         }
37369     },
37370
37371     /**
37372      * Enables a {@link Roo.TabPanelItem} that is disabled.
37373      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37374      */
37375     enableTab : function(id){
37376         var tab = this.items[id];
37377         tab.enable();
37378     },
37379
37380     /**
37381      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37382      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37383      * @return {Roo.TabPanelItem} The TabPanelItem.
37384      */
37385     activate : function(id){
37386         var tab = this.items[id];
37387         if(!tab){
37388             return null;
37389         }
37390         if(tab == this.active || tab.disabled){
37391             return tab;
37392         }
37393         var e = {};
37394         this.fireEvent("beforetabchange", this, e, tab);
37395         if(e.cancel !== true && !tab.disabled){
37396             if(this.active){
37397                 this.active.hide();
37398             }
37399             this.active = this.items[id];
37400             this.active.show();
37401             this.fireEvent("tabchange", this, this.active);
37402         }
37403         return tab;
37404     },
37405
37406     /**
37407      * Gets the active {@link Roo.TabPanelItem}.
37408      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37409      */
37410     getActiveTab : function(){
37411         return this.active;
37412     },
37413
37414     /**
37415      * Updates the tab body element to fit the height of the container element
37416      * for overflow scrolling
37417      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37418      */
37419     syncHeight : function(targetHeight){
37420         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37421         var bm = this.bodyEl.getMargins();
37422         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37423         this.bodyEl.setHeight(newHeight);
37424         return newHeight;
37425     },
37426
37427     onResize : function(){
37428         if(this.monitorResize){
37429             this.autoSizeTabs();
37430         }
37431     },
37432
37433     /**
37434      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37435      */
37436     beginUpdate : function(){
37437         this.updating = true;
37438     },
37439
37440     /**
37441      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37442      */
37443     endUpdate : function(){
37444         this.updating = false;
37445         this.autoSizeTabs();
37446     },
37447
37448     /**
37449      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37450      */
37451     autoSizeTabs : function(){
37452         var count = this.items.length;
37453         var vcount = count - this.hiddenCount;
37454         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37455             return;
37456         }
37457         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37458         var availWidth = Math.floor(w / vcount);
37459         var b = this.stripBody;
37460         if(b.getWidth() > w){
37461             var tabs = this.items;
37462             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37463             if(availWidth < this.minTabWidth){
37464                 /*if(!this.sleft){    // incomplete scrolling code
37465                     this.createScrollButtons();
37466                 }
37467                 this.showScroll();
37468                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37469             }
37470         }else{
37471             if(this.currentTabWidth < this.preferredTabWidth){
37472                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37473             }
37474         }
37475     },
37476
37477     /**
37478      * Returns the number of tabs in this TabPanel.
37479      * @return {Number}
37480      */
37481      getCount : function(){
37482          return this.items.length;
37483      },
37484
37485     /**
37486      * Resizes all the tabs to the passed width
37487      * @param {Number} The new width
37488      */
37489     setTabWidth : function(width){
37490         this.currentTabWidth = width;
37491         for(var i = 0, len = this.items.length; i < len; i++) {
37492                 if(!this.items[i].isHidden()) {
37493                 this.items[i].setWidth(width);
37494             }
37495         }
37496     },
37497
37498     /**
37499      * Destroys this TabPanel
37500      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37501      */
37502     destroy : function(removeEl){
37503         Roo.EventManager.removeResizeListener(this.onResize, this);
37504         for(var i = 0, len = this.items.length; i < len; i++){
37505             this.items[i].purgeListeners();
37506         }
37507         if(removeEl === true){
37508             this.el.update("");
37509             this.el.remove();
37510         }
37511     },
37512     
37513     createStrip : function(container)
37514     {
37515         var strip = document.createElement("nav");
37516         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37517         container.appendChild(strip);
37518         return strip;
37519     },
37520     
37521     createStripList : function(strip)
37522     {
37523         // div wrapper for retard IE
37524         // returns the "tr" element.
37525         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37526         //'<div class="x-tabs-strip-wrap">'+
37527           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37528           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37529         return strip.firstChild; //.firstChild.firstChild.firstChild;
37530     },
37531     createBody : function(container)
37532     {
37533         var body = document.createElement("div");
37534         Roo.id(body, "tab-body");
37535         //Roo.fly(body).addClass("x-tabs-body");
37536         Roo.fly(body).addClass("tab-content");
37537         container.appendChild(body);
37538         return body;
37539     },
37540     createItemBody :function(bodyEl, id){
37541         var body = Roo.getDom(id);
37542         if(!body){
37543             body = document.createElement("div");
37544             body.id = id;
37545         }
37546         //Roo.fly(body).addClass("x-tabs-item-body");
37547         Roo.fly(body).addClass("tab-pane");
37548          bodyEl.insertBefore(body, bodyEl.firstChild);
37549         return body;
37550     },
37551     /** @private */
37552     createStripElements :  function(stripEl, text, closable, tpl)
37553     {
37554         var td = document.createElement("li"); // was td..
37555         
37556         
37557         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37558         
37559         
37560         stripEl.appendChild(td);
37561         /*if(closable){
37562             td.className = "x-tabs-closable";
37563             if(!this.closeTpl){
37564                 this.closeTpl = new Roo.Template(
37565                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37566                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37567                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37568                 );
37569             }
37570             var el = this.closeTpl.overwrite(td, {"text": text});
37571             var close = el.getElementsByTagName("div")[0];
37572             var inner = el.getElementsByTagName("em")[0];
37573             return {"el": el, "close": close, "inner": inner};
37574         } else {
37575         */
37576         // not sure what this is..
37577 //            if(!this.tabTpl){
37578                 //this.tabTpl = new Roo.Template(
37579                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37580                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37581                 //);
37582 //                this.tabTpl = new Roo.Template(
37583 //                   '<a href="#">' +
37584 //                   '<span unselectable="on"' +
37585 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37586 //                            ' >{text}</span></a>'
37587 //                );
37588 //                
37589 //            }
37590
37591
37592             var template = tpl || this.tabTpl || false;
37593             
37594             if(!template){
37595                 
37596                 template = new Roo.Template(
37597                    '<a href="#">' +
37598                    '<span unselectable="on"' +
37599                             (this.disableTooltips ? '' : ' title="{text}"') +
37600                             ' >{text}</span></a>'
37601                 );
37602             }
37603             
37604             switch (typeof(template)) {
37605                 case 'object' :
37606                     break;
37607                 case 'string' :
37608                     template = new Roo.Template(template);
37609                     break;
37610                 default :
37611                     break;
37612             }
37613             
37614             var el = template.overwrite(td, {"text": text});
37615             
37616             var inner = el.getElementsByTagName("span")[0];
37617             
37618             return {"el": el, "inner": inner};
37619             
37620     }
37621         
37622     
37623 });
37624
37625 /**
37626  * @class Roo.TabPanelItem
37627  * @extends Roo.util.Observable
37628  * Represents an individual item (tab plus body) in a TabPanel.
37629  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37630  * @param {String} id The id of this TabPanelItem
37631  * @param {String} text The text for the tab of this TabPanelItem
37632  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37633  */
37634 Roo.bootstrap.panel.TabItem = function(config){
37635     /**
37636      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37637      * @type Roo.TabPanel
37638      */
37639     this.tabPanel = config.panel;
37640     /**
37641      * The id for this TabPanelItem
37642      * @type String
37643      */
37644     this.id = config.id;
37645     /** @private */
37646     this.disabled = false;
37647     /** @private */
37648     this.text = config.text;
37649     /** @private */
37650     this.loaded = false;
37651     this.closable = config.closable;
37652
37653     /**
37654      * The body element for this TabPanelItem.
37655      * @type Roo.Element
37656      */
37657     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37658     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37659     this.bodyEl.setStyle("display", "block");
37660     this.bodyEl.setStyle("zoom", "1");
37661     //this.hideAction();
37662
37663     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37664     /** @private */
37665     this.el = Roo.get(els.el);
37666     this.inner = Roo.get(els.inner, true);
37667     this.textEl = Roo.get(this.el.dom.firstChild, true);
37668     this.pnode = Roo.get(els.el.parentNode, true);
37669 //    this.el.on("mousedown", this.onTabMouseDown, this);
37670     this.el.on("click", this.onTabClick, this);
37671     /** @private */
37672     if(config.closable){
37673         var c = Roo.get(els.close, true);
37674         c.dom.title = this.closeText;
37675         c.addClassOnOver("close-over");
37676         c.on("click", this.closeClick, this);
37677      }
37678
37679     this.addEvents({
37680          /**
37681          * @event activate
37682          * Fires when this tab becomes the active tab.
37683          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37684          * @param {Roo.TabPanelItem} this
37685          */
37686         "activate": true,
37687         /**
37688          * @event beforeclose
37689          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37690          * @param {Roo.TabPanelItem} this
37691          * @param {Object} e Set cancel to true on this object to cancel the close.
37692          */
37693         "beforeclose": true,
37694         /**
37695          * @event close
37696          * Fires when this tab is closed.
37697          * @param {Roo.TabPanelItem} this
37698          */
37699          "close": true,
37700         /**
37701          * @event deactivate
37702          * Fires when this tab is no longer the active tab.
37703          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37704          * @param {Roo.TabPanelItem} this
37705          */
37706          "deactivate" : true
37707     });
37708     this.hidden = false;
37709
37710     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37711 };
37712
37713 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37714            {
37715     purgeListeners : function(){
37716        Roo.util.Observable.prototype.purgeListeners.call(this);
37717        this.el.removeAllListeners();
37718     },
37719     /**
37720      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37721      */
37722     show : function(){
37723         this.pnode.addClass("active");
37724         this.showAction();
37725         if(Roo.isOpera){
37726             this.tabPanel.stripWrap.repaint();
37727         }
37728         this.fireEvent("activate", this.tabPanel, this);
37729     },
37730
37731     /**
37732      * Returns true if this tab is the active tab.
37733      * @return {Boolean}
37734      */
37735     isActive : function(){
37736         return this.tabPanel.getActiveTab() == this;
37737     },
37738
37739     /**
37740      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37741      */
37742     hide : function(){
37743         this.pnode.removeClass("active");
37744         this.hideAction();
37745         this.fireEvent("deactivate", this.tabPanel, this);
37746     },
37747
37748     hideAction : function(){
37749         this.bodyEl.hide();
37750         this.bodyEl.setStyle("position", "absolute");
37751         this.bodyEl.setLeft("-20000px");
37752         this.bodyEl.setTop("-20000px");
37753     },
37754
37755     showAction : function(){
37756         this.bodyEl.setStyle("position", "relative");
37757         this.bodyEl.setTop("");
37758         this.bodyEl.setLeft("");
37759         this.bodyEl.show();
37760     },
37761
37762     /**
37763      * Set the tooltip for the tab.
37764      * @param {String} tooltip The tab's tooltip
37765      */
37766     setTooltip : function(text){
37767         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37768             this.textEl.dom.qtip = text;
37769             this.textEl.dom.removeAttribute('title');
37770         }else{
37771             this.textEl.dom.title = text;
37772         }
37773     },
37774
37775     onTabClick : function(e){
37776         e.preventDefault();
37777         this.tabPanel.activate(this.id);
37778     },
37779
37780     onTabMouseDown : function(e){
37781         e.preventDefault();
37782         this.tabPanel.activate(this.id);
37783     },
37784 /*
37785     getWidth : function(){
37786         return this.inner.getWidth();
37787     },
37788
37789     setWidth : function(width){
37790         var iwidth = width - this.pnode.getPadding("lr");
37791         this.inner.setWidth(iwidth);
37792         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37793         this.pnode.setWidth(width);
37794     },
37795 */
37796     /**
37797      * Show or hide the tab
37798      * @param {Boolean} hidden True to hide or false to show.
37799      */
37800     setHidden : function(hidden){
37801         this.hidden = hidden;
37802         this.pnode.setStyle("display", hidden ? "none" : "");
37803     },
37804
37805     /**
37806      * Returns true if this tab is "hidden"
37807      * @return {Boolean}
37808      */
37809     isHidden : function(){
37810         return this.hidden;
37811     },
37812
37813     /**
37814      * Returns the text for this tab
37815      * @return {String}
37816      */
37817     getText : function(){
37818         return this.text;
37819     },
37820     /*
37821     autoSize : function(){
37822         //this.el.beginMeasure();
37823         this.textEl.setWidth(1);
37824         /*
37825          *  #2804 [new] Tabs in Roojs
37826          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37827          */
37828         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37829         //this.el.endMeasure();
37830     //},
37831
37832     /**
37833      * Sets the text for the tab (Note: this also sets the tooltip text)
37834      * @param {String} text The tab's text and tooltip
37835      */
37836     setText : function(text){
37837         this.text = text;
37838         this.textEl.update(text);
37839         this.setTooltip(text);
37840         //if(!this.tabPanel.resizeTabs){
37841         //    this.autoSize();
37842         //}
37843     },
37844     /**
37845      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37846      */
37847     activate : function(){
37848         this.tabPanel.activate(this.id);
37849     },
37850
37851     /**
37852      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37853      */
37854     disable : function(){
37855         if(this.tabPanel.active != this){
37856             this.disabled = true;
37857             this.pnode.addClass("disabled");
37858         }
37859     },
37860
37861     /**
37862      * Enables this TabPanelItem if it was previously disabled.
37863      */
37864     enable : function(){
37865         this.disabled = false;
37866         this.pnode.removeClass("disabled");
37867     },
37868
37869     /**
37870      * Sets the content for this TabPanelItem.
37871      * @param {String} content The content
37872      * @param {Boolean} loadScripts true to look for and load scripts
37873      */
37874     setContent : function(content, loadScripts){
37875         this.bodyEl.update(content, loadScripts);
37876     },
37877
37878     /**
37879      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37880      * @return {Roo.UpdateManager} The UpdateManager
37881      */
37882     getUpdateManager : function(){
37883         return this.bodyEl.getUpdateManager();
37884     },
37885
37886     /**
37887      * Set a URL to be used to load the content for this TabPanelItem.
37888      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37889      * @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)
37890      * @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)
37891      * @return {Roo.UpdateManager} The UpdateManager
37892      */
37893     setUrl : function(url, params, loadOnce){
37894         if(this.refreshDelegate){
37895             this.un('activate', this.refreshDelegate);
37896         }
37897         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37898         this.on("activate", this.refreshDelegate);
37899         return this.bodyEl.getUpdateManager();
37900     },
37901
37902     /** @private */
37903     _handleRefresh : function(url, params, loadOnce){
37904         if(!loadOnce || !this.loaded){
37905             var updater = this.bodyEl.getUpdateManager();
37906             updater.update(url, params, this._setLoaded.createDelegate(this));
37907         }
37908     },
37909
37910     /**
37911      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37912      *   Will fail silently if the setUrl method has not been called.
37913      *   This does not activate the panel, just updates its content.
37914      */
37915     refresh : function(){
37916         if(this.refreshDelegate){
37917            this.loaded = false;
37918            this.refreshDelegate();
37919         }
37920     },
37921
37922     /** @private */
37923     _setLoaded : function(){
37924         this.loaded = true;
37925     },
37926
37927     /** @private */
37928     closeClick : function(e){
37929         var o = {};
37930         e.stopEvent();
37931         this.fireEvent("beforeclose", this, o);
37932         if(o.cancel !== true){
37933             this.tabPanel.removeTab(this.id);
37934         }
37935     },
37936     /**
37937      * The text displayed in the tooltip for the close icon.
37938      * @type String
37939      */
37940     closeText : "Close this tab"
37941 });
37942 /**
37943 *    This script refer to:
37944 *    Title: International Telephone Input
37945 *    Author: Jack O'Connor
37946 *    Code version:  v12.1.12
37947 *    Availability: https://github.com/jackocnr/intl-tel-input.git
37948 **/
37949
37950 Roo.bootstrap.PhoneInputData = function() {
37951     var d = [
37952       [
37953         "Afghanistan (‫افغانستان‬‎)",
37954         "af",
37955         "93"
37956       ],
37957       [
37958         "Albania (Shqipëri)",
37959         "al",
37960         "355"
37961       ],
37962       [
37963         "Algeria (‫الجزائر‬‎)",
37964         "dz",
37965         "213"
37966       ],
37967       [
37968         "American Samoa",
37969         "as",
37970         "1684"
37971       ],
37972       [
37973         "Andorra",
37974         "ad",
37975         "376"
37976       ],
37977       [
37978         "Angola",
37979         "ao",
37980         "244"
37981       ],
37982       [
37983         "Anguilla",
37984         "ai",
37985         "1264"
37986       ],
37987       [
37988         "Antigua and Barbuda",
37989         "ag",
37990         "1268"
37991       ],
37992       [
37993         "Argentina",
37994         "ar",
37995         "54"
37996       ],
37997       [
37998         "Armenia (Հայաստան)",
37999         "am",
38000         "374"
38001       ],
38002       [
38003         "Aruba",
38004         "aw",
38005         "297"
38006       ],
38007       [
38008         "Australia",
38009         "au",
38010         "61",
38011         0
38012       ],
38013       [
38014         "Austria (Österreich)",
38015         "at",
38016         "43"
38017       ],
38018       [
38019         "Azerbaijan (Azərbaycan)",
38020         "az",
38021         "994"
38022       ],
38023       [
38024         "Bahamas",
38025         "bs",
38026         "1242"
38027       ],
38028       [
38029         "Bahrain (‫البحرين‬‎)",
38030         "bh",
38031         "973"
38032       ],
38033       [
38034         "Bangladesh (বাংলাদেশ)",
38035         "bd",
38036         "880"
38037       ],
38038       [
38039         "Barbados",
38040         "bb",
38041         "1246"
38042       ],
38043       [
38044         "Belarus (Беларусь)",
38045         "by",
38046         "375"
38047       ],
38048       [
38049         "Belgium (België)",
38050         "be",
38051         "32"
38052       ],
38053       [
38054         "Belize",
38055         "bz",
38056         "501"
38057       ],
38058       [
38059         "Benin (Bénin)",
38060         "bj",
38061         "229"
38062       ],
38063       [
38064         "Bermuda",
38065         "bm",
38066         "1441"
38067       ],
38068       [
38069         "Bhutan (འབྲུག)",
38070         "bt",
38071         "975"
38072       ],
38073       [
38074         "Bolivia",
38075         "bo",
38076         "591"
38077       ],
38078       [
38079         "Bosnia and Herzegovina (Босна и Херцеговина)",
38080         "ba",
38081         "387"
38082       ],
38083       [
38084         "Botswana",
38085         "bw",
38086         "267"
38087       ],
38088       [
38089         "Brazil (Brasil)",
38090         "br",
38091         "55"
38092       ],
38093       [
38094         "British Indian Ocean Territory",
38095         "io",
38096         "246"
38097       ],
38098       [
38099         "British Virgin Islands",
38100         "vg",
38101         "1284"
38102       ],
38103       [
38104         "Brunei",
38105         "bn",
38106         "673"
38107       ],
38108       [
38109         "Bulgaria (България)",
38110         "bg",
38111         "359"
38112       ],
38113       [
38114         "Burkina Faso",
38115         "bf",
38116         "226"
38117       ],
38118       [
38119         "Burundi (Uburundi)",
38120         "bi",
38121         "257"
38122       ],
38123       [
38124         "Cambodia (កម្ពុជា)",
38125         "kh",
38126         "855"
38127       ],
38128       [
38129         "Cameroon (Cameroun)",
38130         "cm",
38131         "237"
38132       ],
38133       [
38134         "Canada",
38135         "ca",
38136         "1",
38137         1,
38138         ["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"]
38139       ],
38140       [
38141         "Cape Verde (Kabu Verdi)",
38142         "cv",
38143         "238"
38144       ],
38145       [
38146         "Caribbean Netherlands",
38147         "bq",
38148         "599",
38149         1
38150       ],
38151       [
38152         "Cayman Islands",
38153         "ky",
38154         "1345"
38155       ],
38156       [
38157         "Central African Republic (République centrafricaine)",
38158         "cf",
38159         "236"
38160       ],
38161       [
38162         "Chad (Tchad)",
38163         "td",
38164         "235"
38165       ],
38166       [
38167         "Chile",
38168         "cl",
38169         "56"
38170       ],
38171       [
38172         "China (中国)",
38173         "cn",
38174         "86"
38175       ],
38176       [
38177         "Christmas Island",
38178         "cx",
38179         "61",
38180         2
38181       ],
38182       [
38183         "Cocos (Keeling) Islands",
38184         "cc",
38185         "61",
38186         1
38187       ],
38188       [
38189         "Colombia",
38190         "co",
38191         "57"
38192       ],
38193       [
38194         "Comoros (‫جزر القمر‬‎)",
38195         "km",
38196         "269"
38197       ],
38198       [
38199         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38200         "cd",
38201         "243"
38202       ],
38203       [
38204         "Congo (Republic) (Congo-Brazzaville)",
38205         "cg",
38206         "242"
38207       ],
38208       [
38209         "Cook Islands",
38210         "ck",
38211         "682"
38212       ],
38213       [
38214         "Costa Rica",
38215         "cr",
38216         "506"
38217       ],
38218       [
38219         "Côte d’Ivoire",
38220         "ci",
38221         "225"
38222       ],
38223       [
38224         "Croatia (Hrvatska)",
38225         "hr",
38226         "385"
38227       ],
38228       [
38229         "Cuba",
38230         "cu",
38231         "53"
38232       ],
38233       [
38234         "Curaçao",
38235         "cw",
38236         "599",
38237         0
38238       ],
38239       [
38240         "Cyprus (Κύπρος)",
38241         "cy",
38242         "357"
38243       ],
38244       [
38245         "Czech Republic (Česká republika)",
38246         "cz",
38247         "420"
38248       ],
38249       [
38250         "Denmark (Danmark)",
38251         "dk",
38252         "45"
38253       ],
38254       [
38255         "Djibouti",
38256         "dj",
38257         "253"
38258       ],
38259       [
38260         "Dominica",
38261         "dm",
38262         "1767"
38263       ],
38264       [
38265         "Dominican Republic (República Dominicana)",
38266         "do",
38267         "1",
38268         2,
38269         ["809", "829", "849"]
38270       ],
38271       [
38272         "Ecuador",
38273         "ec",
38274         "593"
38275       ],
38276       [
38277         "Egypt (‫مصر‬‎)",
38278         "eg",
38279         "20"
38280       ],
38281       [
38282         "El Salvador",
38283         "sv",
38284         "503"
38285       ],
38286       [
38287         "Equatorial Guinea (Guinea Ecuatorial)",
38288         "gq",
38289         "240"
38290       ],
38291       [
38292         "Eritrea",
38293         "er",
38294         "291"
38295       ],
38296       [
38297         "Estonia (Eesti)",
38298         "ee",
38299         "372"
38300       ],
38301       [
38302         "Ethiopia",
38303         "et",
38304         "251"
38305       ],
38306       [
38307         "Falkland Islands (Islas Malvinas)",
38308         "fk",
38309         "500"
38310       ],
38311       [
38312         "Faroe Islands (Føroyar)",
38313         "fo",
38314         "298"
38315       ],
38316       [
38317         "Fiji",
38318         "fj",
38319         "679"
38320       ],
38321       [
38322         "Finland (Suomi)",
38323         "fi",
38324         "358",
38325         0
38326       ],
38327       [
38328         "France",
38329         "fr",
38330         "33"
38331       ],
38332       [
38333         "French Guiana (Guyane française)",
38334         "gf",
38335         "594"
38336       ],
38337       [
38338         "French Polynesia (Polynésie française)",
38339         "pf",
38340         "689"
38341       ],
38342       [
38343         "Gabon",
38344         "ga",
38345         "241"
38346       ],
38347       [
38348         "Gambia",
38349         "gm",
38350         "220"
38351       ],
38352       [
38353         "Georgia (საქართველო)",
38354         "ge",
38355         "995"
38356       ],
38357       [
38358         "Germany (Deutschland)",
38359         "de",
38360         "49"
38361       ],
38362       [
38363         "Ghana (Gaana)",
38364         "gh",
38365         "233"
38366       ],
38367       [
38368         "Gibraltar",
38369         "gi",
38370         "350"
38371       ],
38372       [
38373         "Greece (Ελλάδα)",
38374         "gr",
38375         "30"
38376       ],
38377       [
38378         "Greenland (Kalaallit Nunaat)",
38379         "gl",
38380         "299"
38381       ],
38382       [
38383         "Grenada",
38384         "gd",
38385         "1473"
38386       ],
38387       [
38388         "Guadeloupe",
38389         "gp",
38390         "590",
38391         0
38392       ],
38393       [
38394         "Guam",
38395         "gu",
38396         "1671"
38397       ],
38398       [
38399         "Guatemala",
38400         "gt",
38401         "502"
38402       ],
38403       [
38404         "Guernsey",
38405         "gg",
38406         "44",
38407         1
38408       ],
38409       [
38410         "Guinea (Guinée)",
38411         "gn",
38412         "224"
38413       ],
38414       [
38415         "Guinea-Bissau (Guiné Bissau)",
38416         "gw",
38417         "245"
38418       ],
38419       [
38420         "Guyana",
38421         "gy",
38422         "592"
38423       ],
38424       [
38425         "Haiti",
38426         "ht",
38427         "509"
38428       ],
38429       [
38430         "Honduras",
38431         "hn",
38432         "504"
38433       ],
38434       [
38435         "Hong Kong (香港)",
38436         "hk",
38437         "852"
38438       ],
38439       [
38440         "Hungary (Magyarország)",
38441         "hu",
38442         "36"
38443       ],
38444       [
38445         "Iceland (Ísland)",
38446         "is",
38447         "354"
38448       ],
38449       [
38450         "India (भारत)",
38451         "in",
38452         "91"
38453       ],
38454       [
38455         "Indonesia",
38456         "id",
38457         "62"
38458       ],
38459       [
38460         "Iran (‫ایران‬‎)",
38461         "ir",
38462         "98"
38463       ],
38464       [
38465         "Iraq (‫العراق‬‎)",
38466         "iq",
38467         "964"
38468       ],
38469       [
38470         "Ireland",
38471         "ie",
38472         "353"
38473       ],
38474       [
38475         "Isle of Man",
38476         "im",
38477         "44",
38478         2
38479       ],
38480       [
38481         "Israel (‫ישראל‬‎)",
38482         "il",
38483         "972"
38484       ],
38485       [
38486         "Italy (Italia)",
38487         "it",
38488         "39",
38489         0
38490       ],
38491       [
38492         "Jamaica",
38493         "jm",
38494         "1876"
38495       ],
38496       [
38497         "Japan (日本)",
38498         "jp",
38499         "81"
38500       ],
38501       [
38502         "Jersey",
38503         "je",
38504         "44",
38505         3
38506       ],
38507       [
38508         "Jordan (‫الأردن‬‎)",
38509         "jo",
38510         "962"
38511       ],
38512       [
38513         "Kazakhstan (Казахстан)",
38514         "kz",
38515         "7",
38516         1
38517       ],
38518       [
38519         "Kenya",
38520         "ke",
38521         "254"
38522       ],
38523       [
38524         "Kiribati",
38525         "ki",
38526         "686"
38527       ],
38528       [
38529         "Kosovo",
38530         "xk",
38531         "383"
38532       ],
38533       [
38534         "Kuwait (‫الكويت‬‎)",
38535         "kw",
38536         "965"
38537       ],
38538       [
38539         "Kyrgyzstan (Кыргызстан)",
38540         "kg",
38541         "996"
38542       ],
38543       [
38544         "Laos (ລາວ)",
38545         "la",
38546         "856"
38547       ],
38548       [
38549         "Latvia (Latvija)",
38550         "lv",
38551         "371"
38552       ],
38553       [
38554         "Lebanon (‫لبنان‬‎)",
38555         "lb",
38556         "961"
38557       ],
38558       [
38559         "Lesotho",
38560         "ls",
38561         "266"
38562       ],
38563       [
38564         "Liberia",
38565         "lr",
38566         "231"
38567       ],
38568       [
38569         "Libya (‫ليبيا‬‎)",
38570         "ly",
38571         "218"
38572       ],
38573       [
38574         "Liechtenstein",
38575         "li",
38576         "423"
38577       ],
38578       [
38579         "Lithuania (Lietuva)",
38580         "lt",
38581         "370"
38582       ],
38583       [
38584         "Luxembourg",
38585         "lu",
38586         "352"
38587       ],
38588       [
38589         "Macau (澳門)",
38590         "mo",
38591         "853"
38592       ],
38593       [
38594         "Macedonia (FYROM) (Македонија)",
38595         "mk",
38596         "389"
38597       ],
38598       [
38599         "Madagascar (Madagasikara)",
38600         "mg",
38601         "261"
38602       ],
38603       [
38604         "Malawi",
38605         "mw",
38606         "265"
38607       ],
38608       [
38609         "Malaysia",
38610         "my",
38611         "60"
38612       ],
38613       [
38614         "Maldives",
38615         "mv",
38616         "960"
38617       ],
38618       [
38619         "Mali",
38620         "ml",
38621         "223"
38622       ],
38623       [
38624         "Malta",
38625         "mt",
38626         "356"
38627       ],
38628       [
38629         "Marshall Islands",
38630         "mh",
38631         "692"
38632       ],
38633       [
38634         "Martinique",
38635         "mq",
38636         "596"
38637       ],
38638       [
38639         "Mauritania (‫موريتانيا‬‎)",
38640         "mr",
38641         "222"
38642       ],
38643       [
38644         "Mauritius (Moris)",
38645         "mu",
38646         "230"
38647       ],
38648       [
38649         "Mayotte",
38650         "yt",
38651         "262",
38652         1
38653       ],
38654       [
38655         "Mexico (México)",
38656         "mx",
38657         "52"
38658       ],
38659       [
38660         "Micronesia",
38661         "fm",
38662         "691"
38663       ],
38664       [
38665         "Moldova (Republica Moldova)",
38666         "md",
38667         "373"
38668       ],
38669       [
38670         "Monaco",
38671         "mc",
38672         "377"
38673       ],
38674       [
38675         "Mongolia (Монгол)",
38676         "mn",
38677         "976"
38678       ],
38679       [
38680         "Montenegro (Crna Gora)",
38681         "me",
38682         "382"
38683       ],
38684       [
38685         "Montserrat",
38686         "ms",
38687         "1664"
38688       ],
38689       [
38690         "Morocco (‫المغرب‬‎)",
38691         "ma",
38692         "212",
38693         0
38694       ],
38695       [
38696         "Mozambique (Moçambique)",
38697         "mz",
38698         "258"
38699       ],
38700       [
38701         "Myanmar (Burma) (မြန်မာ)",
38702         "mm",
38703         "95"
38704       ],
38705       [
38706         "Namibia (Namibië)",
38707         "na",
38708         "264"
38709       ],
38710       [
38711         "Nauru",
38712         "nr",
38713         "674"
38714       ],
38715       [
38716         "Nepal (नेपाल)",
38717         "np",
38718         "977"
38719       ],
38720       [
38721         "Netherlands (Nederland)",
38722         "nl",
38723         "31"
38724       ],
38725       [
38726         "New Caledonia (Nouvelle-Calédonie)",
38727         "nc",
38728         "687"
38729       ],
38730       [
38731         "New Zealand",
38732         "nz",
38733         "64"
38734       ],
38735       [
38736         "Nicaragua",
38737         "ni",
38738         "505"
38739       ],
38740       [
38741         "Niger (Nijar)",
38742         "ne",
38743         "227"
38744       ],
38745       [
38746         "Nigeria",
38747         "ng",
38748         "234"
38749       ],
38750       [
38751         "Niue",
38752         "nu",
38753         "683"
38754       ],
38755       [
38756         "Norfolk Island",
38757         "nf",
38758         "672"
38759       ],
38760       [
38761         "North Korea (조선 민주주의 인민 공화국)",
38762         "kp",
38763         "850"
38764       ],
38765       [
38766         "Northern Mariana Islands",
38767         "mp",
38768         "1670"
38769       ],
38770       [
38771         "Norway (Norge)",
38772         "no",
38773         "47",
38774         0
38775       ],
38776       [
38777         "Oman (‫عُمان‬‎)",
38778         "om",
38779         "968"
38780       ],
38781       [
38782         "Pakistan (‫پاکستان‬‎)",
38783         "pk",
38784         "92"
38785       ],
38786       [
38787         "Palau",
38788         "pw",
38789         "680"
38790       ],
38791       [
38792         "Palestine (‫فلسطين‬‎)",
38793         "ps",
38794         "970"
38795       ],
38796       [
38797         "Panama (Panamá)",
38798         "pa",
38799         "507"
38800       ],
38801       [
38802         "Papua New Guinea",
38803         "pg",
38804         "675"
38805       ],
38806       [
38807         "Paraguay",
38808         "py",
38809         "595"
38810       ],
38811       [
38812         "Peru (Perú)",
38813         "pe",
38814         "51"
38815       ],
38816       [
38817         "Philippines",
38818         "ph",
38819         "63"
38820       ],
38821       [
38822         "Poland (Polska)",
38823         "pl",
38824         "48"
38825       ],
38826       [
38827         "Portugal",
38828         "pt",
38829         "351"
38830       ],
38831       [
38832         "Puerto Rico",
38833         "pr",
38834         "1",
38835         3,
38836         ["787", "939"]
38837       ],
38838       [
38839         "Qatar (‫قطر‬‎)",
38840         "qa",
38841         "974"
38842       ],
38843       [
38844         "Réunion (La Réunion)",
38845         "re",
38846         "262",
38847         0
38848       ],
38849       [
38850         "Romania (România)",
38851         "ro",
38852         "40"
38853       ],
38854       [
38855         "Russia (Россия)",
38856         "ru",
38857         "7",
38858         0
38859       ],
38860       [
38861         "Rwanda",
38862         "rw",
38863         "250"
38864       ],
38865       [
38866         "Saint Barthélemy",
38867         "bl",
38868         "590",
38869         1
38870       ],
38871       [
38872         "Saint Helena",
38873         "sh",
38874         "290"
38875       ],
38876       [
38877         "Saint Kitts and Nevis",
38878         "kn",
38879         "1869"
38880       ],
38881       [
38882         "Saint Lucia",
38883         "lc",
38884         "1758"
38885       ],
38886       [
38887         "Saint Martin (Saint-Martin (partie française))",
38888         "mf",
38889         "590",
38890         2
38891       ],
38892       [
38893         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38894         "pm",
38895         "508"
38896       ],
38897       [
38898         "Saint Vincent and the Grenadines",
38899         "vc",
38900         "1784"
38901       ],
38902       [
38903         "Samoa",
38904         "ws",
38905         "685"
38906       ],
38907       [
38908         "San Marino",
38909         "sm",
38910         "378"
38911       ],
38912       [
38913         "São Tomé and Príncipe (São Tomé e Príncipe)",
38914         "st",
38915         "239"
38916       ],
38917       [
38918         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
38919         "sa",
38920         "966"
38921       ],
38922       [
38923         "Senegal (Sénégal)",
38924         "sn",
38925         "221"
38926       ],
38927       [
38928         "Serbia (Србија)",
38929         "rs",
38930         "381"
38931       ],
38932       [
38933         "Seychelles",
38934         "sc",
38935         "248"
38936       ],
38937       [
38938         "Sierra Leone",
38939         "sl",
38940         "232"
38941       ],
38942       [
38943         "Singapore",
38944         "sg",
38945         "65"
38946       ],
38947       [
38948         "Sint Maarten",
38949         "sx",
38950         "1721"
38951       ],
38952       [
38953         "Slovakia (Slovensko)",
38954         "sk",
38955         "421"
38956       ],
38957       [
38958         "Slovenia (Slovenija)",
38959         "si",
38960         "386"
38961       ],
38962       [
38963         "Solomon Islands",
38964         "sb",
38965         "677"
38966       ],
38967       [
38968         "Somalia (Soomaaliya)",
38969         "so",
38970         "252"
38971       ],
38972       [
38973         "South Africa",
38974         "za",
38975         "27"
38976       ],
38977       [
38978         "South Korea (대한민국)",
38979         "kr",
38980         "82"
38981       ],
38982       [
38983         "South Sudan (‫جنوب السودان‬‎)",
38984         "ss",
38985         "211"
38986       ],
38987       [
38988         "Spain (España)",
38989         "es",
38990         "34"
38991       ],
38992       [
38993         "Sri Lanka (ශ්‍රී ලංකාව)",
38994         "lk",
38995         "94"
38996       ],
38997       [
38998         "Sudan (‫السودان‬‎)",
38999         "sd",
39000         "249"
39001       ],
39002       [
39003         "Suriname",
39004         "sr",
39005         "597"
39006       ],
39007       [
39008         "Svalbard and Jan Mayen",
39009         "sj",
39010         "47",
39011         1
39012       ],
39013       [
39014         "Swaziland",
39015         "sz",
39016         "268"
39017       ],
39018       [
39019         "Sweden (Sverige)",
39020         "se",
39021         "46"
39022       ],
39023       [
39024         "Switzerland (Schweiz)",
39025         "ch",
39026         "41"
39027       ],
39028       [
39029         "Syria (‫سوريا‬‎)",
39030         "sy",
39031         "963"
39032       ],
39033       [
39034         "Taiwan (台灣)",
39035         "tw",
39036         "886"
39037       ],
39038       [
39039         "Tajikistan",
39040         "tj",
39041         "992"
39042       ],
39043       [
39044         "Tanzania",
39045         "tz",
39046         "255"
39047       ],
39048       [
39049         "Thailand (ไทย)",
39050         "th",
39051         "66"
39052       ],
39053       [
39054         "Timor-Leste",
39055         "tl",
39056         "670"
39057       ],
39058       [
39059         "Togo",
39060         "tg",
39061         "228"
39062       ],
39063       [
39064         "Tokelau",
39065         "tk",
39066         "690"
39067       ],
39068       [
39069         "Tonga",
39070         "to",
39071         "676"
39072       ],
39073       [
39074         "Trinidad and Tobago",
39075         "tt",
39076         "1868"
39077       ],
39078       [
39079         "Tunisia (‫تونس‬‎)",
39080         "tn",
39081         "216"
39082       ],
39083       [
39084         "Turkey (Türkiye)",
39085         "tr",
39086         "90"
39087       ],
39088       [
39089         "Turkmenistan",
39090         "tm",
39091         "993"
39092       ],
39093       [
39094         "Turks and Caicos Islands",
39095         "tc",
39096         "1649"
39097       ],
39098       [
39099         "Tuvalu",
39100         "tv",
39101         "688"
39102       ],
39103       [
39104         "U.S. Virgin Islands",
39105         "vi",
39106         "1340"
39107       ],
39108       [
39109         "Uganda",
39110         "ug",
39111         "256"
39112       ],
39113       [
39114         "Ukraine (Україна)",
39115         "ua",
39116         "380"
39117       ],
39118       [
39119         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39120         "ae",
39121         "971"
39122       ],
39123       [
39124         "United Kingdom",
39125         "gb",
39126         "44",
39127         0
39128       ],
39129       [
39130         "United States",
39131         "us",
39132         "1",
39133         0
39134       ],
39135       [
39136         "Uruguay",
39137         "uy",
39138         "598"
39139       ],
39140       [
39141         "Uzbekistan (Oʻzbekiston)",
39142         "uz",
39143         "998"
39144       ],
39145       [
39146         "Vanuatu",
39147         "vu",
39148         "678"
39149       ],
39150       [
39151         "Vatican City (Città del Vaticano)",
39152         "va",
39153         "39",
39154         1
39155       ],
39156       [
39157         "Venezuela",
39158         "ve",
39159         "58"
39160       ],
39161       [
39162         "Vietnam (Việt Nam)",
39163         "vn",
39164         "84"
39165       ],
39166       [
39167         "Wallis and Futuna (Wallis-et-Futuna)",
39168         "wf",
39169         "681"
39170       ],
39171       [
39172         "Western Sahara (‫الصحراء الغربية‬‎)",
39173         "eh",
39174         "212",
39175         1
39176       ],
39177       [
39178         "Yemen (‫اليمن‬‎)",
39179         "ye",
39180         "967"
39181       ],
39182       [
39183         "Zambia",
39184         "zm",
39185         "260"
39186       ],
39187       [
39188         "Zimbabwe",
39189         "zw",
39190         "263"
39191       ],
39192       [
39193         "Åland Islands",
39194         "ax",
39195         "358",
39196         1
39197       ]
39198   ];
39199   
39200   return d;
39201 }/**
39202 *    This script refer to:
39203 *    Title: International Telephone Input
39204 *    Author: Jack O'Connor
39205 *    Code version:  v12.1.12
39206 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39207 **/
39208
39209 /**
39210  * @class Roo.bootstrap.PhoneInput
39211  * @extends Roo.bootstrap.TriggerField
39212  * An input with International dial-code selection
39213  
39214  * @cfg {String} defaultDialCode default '+852'
39215  * @cfg {Array} preferedCountries default []
39216   
39217  * @constructor
39218  * Create a new PhoneInput.
39219  * @param {Object} config Configuration options
39220  */
39221
39222 Roo.bootstrap.PhoneInput = function(config) {
39223     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39224 };
39225
39226 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39227         
39228         listWidth: undefined,
39229         
39230         selectedClass: 'active',
39231         
39232         invalidClass : "has-warning",
39233         
39234         validClass: 'has-success',
39235         
39236         allowed: '0123456789',
39237         
39238         /**
39239          * @cfg {String} defaultDialCode The default dial code when initializing the input
39240          */
39241         defaultDialCode: '+852',
39242         
39243         /**
39244          * @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
39245          */
39246         preferedCountries: false,
39247         
39248         getAutoCreate : function()
39249         {
39250             var data = Roo.bootstrap.PhoneInputData();
39251             var align = this.labelAlign || this.parentLabelAlign();
39252             var id = Roo.id();
39253             
39254             this.allCountries = [];
39255             this.dialCodeMapping = [];
39256             
39257             for (var i = 0; i < data.length; i++) {
39258               var c = data[i];
39259               this.allCountries[i] = {
39260                 name: c[0],
39261                 iso2: c[1],
39262                 dialCode: c[2],
39263                 priority: c[3] || 0,
39264                 areaCodes: c[4] || null
39265               };
39266               this.dialCodeMapping[c[2]] = {
39267                   name: c[0],
39268                   iso2: c[1],
39269                   priority: c[3] || 0,
39270                   areaCodes: c[4] || null
39271               };
39272             }
39273             
39274             var cfg = {
39275                 cls: 'form-group',
39276                 cn: []
39277             };
39278             
39279             var input =  {
39280                 tag: 'input',
39281                 id : id,
39282                 cls : 'form-control tel-input',
39283                 autocomplete: 'new-password'
39284             };
39285             
39286             var hiddenInput = {
39287                 tag: 'input',
39288                 type: 'hidden',
39289                 cls: 'hidden-tel-input'
39290             };
39291             
39292             if (this.name) {
39293                 hiddenInput.name = this.name;
39294             }
39295             
39296             if (this.disabled) {
39297                 input.disabled = true;
39298             }
39299             
39300             var flag_container = {
39301                 tag: 'div',
39302                 cls: 'flag-box',
39303                 cn: [
39304                     {
39305                         tag: 'div',
39306                         cls: 'flag'
39307                     },
39308                     {
39309                         tag: 'div',
39310                         cls: 'caret'
39311                     }
39312                 ]
39313             };
39314             
39315             var box = {
39316                 tag: 'div',
39317                 cls: this.hasFeedback ? 'has-feedback' : '',
39318                 cn: [
39319                     hiddenInput,
39320                     input,
39321                     {
39322                         tag: 'input',
39323                         cls: 'dial-code-holder',
39324                         disabled: true
39325                     }
39326                 ]
39327             };
39328             
39329             var container = {
39330                 cls: 'roo-select2-container input-group',
39331                 cn: [
39332                     flag_container,
39333                     box
39334                 ]
39335             };
39336             
39337             if (this.fieldLabel.length) {
39338                 var indicator = {
39339                     tag: 'i',
39340                     tooltip: 'This field is required'
39341                 };
39342                 
39343                 var label = {
39344                     tag: 'label',
39345                     'for':  id,
39346                     cls: 'control-label',
39347                     cn: []
39348                 };
39349                 
39350                 var label_text = {
39351                     tag: 'span',
39352                     html: this.fieldLabel
39353                 };
39354                 
39355                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39356                 label.cn = [
39357                     indicator,
39358                     label_text
39359                 ];
39360                 
39361                 if(this.indicatorpos == 'right') {
39362                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39363                     label.cn = [
39364                         label_text,
39365                         indicator
39366                     ];
39367                 }
39368                 
39369                 if(align == 'left') {
39370                     container = {
39371                         tag: 'div',
39372                         cn: [
39373                             container
39374                         ]
39375                     };
39376                     
39377                     if(this.labelWidth > 12){
39378                         label.style = "width: " + this.labelWidth + 'px';
39379                     }
39380                     if(this.labelWidth < 13 && this.labelmd == 0){
39381                         this.labelmd = this.labelWidth;
39382                     }
39383                     if(this.labellg > 0){
39384                         label.cls += ' col-lg-' + this.labellg;
39385                         input.cls += ' col-lg-' + (12 - this.labellg);
39386                     }
39387                     if(this.labelmd > 0){
39388                         label.cls += ' col-md-' + this.labelmd;
39389                         container.cls += ' col-md-' + (12 - this.labelmd);
39390                     }
39391                     if(this.labelsm > 0){
39392                         label.cls += ' col-sm-' + this.labelsm;
39393                         container.cls += ' col-sm-' + (12 - this.labelsm);
39394                     }
39395                     if(this.labelxs > 0){
39396                         label.cls += ' col-xs-' + this.labelxs;
39397                         container.cls += ' col-xs-' + (12 - this.labelxs);
39398                     }
39399                 }
39400             }
39401             
39402             cfg.cn = [
39403                 label,
39404                 container
39405             ];
39406             
39407             var settings = this;
39408             
39409             ['xs','sm','md','lg'].map(function(size){
39410                 if (settings[size]) {
39411                     cfg.cls += ' col-' + size + '-' + settings[size];
39412                 }
39413             });
39414             
39415             this.store = new Roo.data.Store({
39416                 proxy : new Roo.data.MemoryProxy({}),
39417                 reader : new Roo.data.JsonReader({
39418                     fields : [
39419                         {
39420                             'name' : 'name',
39421                             'type' : 'string'
39422                         },
39423                         {
39424                             'name' : 'iso2',
39425                             'type' : 'string'
39426                         },
39427                         {
39428                             'name' : 'dialCode',
39429                             'type' : 'string'
39430                         },
39431                         {
39432                             'name' : 'priority',
39433                             'type' : 'string'
39434                         },
39435                         {
39436                             'name' : 'areaCodes',
39437                             'type' : 'string'
39438                         }
39439                     ]
39440                 })
39441             });
39442             
39443             if(!this.preferedCountries) {
39444                 this.preferedCountries = [
39445                     'hk',
39446                     'gb',
39447                     'us'
39448                 ];
39449             }
39450             
39451             var p = this.preferedCountries.reverse();
39452             
39453             if(p) {
39454                 for (var i = 0; i < p.length; i++) {
39455                     for (var j = 0; j < this.allCountries.length; j++) {
39456                         if(this.allCountries[j].iso2 == p[i]) {
39457                             var t = this.allCountries[j];
39458                             this.allCountries.splice(j,1);
39459                             this.allCountries.unshift(t);
39460                         }
39461                     } 
39462                 }
39463             }
39464             
39465             this.store.proxy.data = {
39466                 success: true,
39467                 data: this.allCountries
39468             };
39469             
39470             return cfg;
39471         },
39472         
39473         initEvents : function()
39474         {
39475             this.createList();
39476             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39477             
39478             this.indicator = this.indicatorEl();
39479             this.flag = this.flagEl();
39480             this.dialCodeHolder = this.dialCodeHolderEl();
39481             
39482             this.trigger = this.el.select('div.flag-box',true).first();
39483             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39484             
39485             var _this = this;
39486             
39487             (function(){
39488                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39489                 _this.list.setWidth(lw);
39490             }).defer(100);
39491             
39492             this.list.on('mouseover', this.onViewOver, this);
39493             this.list.on('mousemove', this.onViewMove, this);
39494             this.inputEl().on("keyup", this.onKeyUp, this);
39495             
39496             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39497
39498             this.view = new Roo.View(this.list, this.tpl, {
39499                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39500             });
39501             
39502             this.view.on('click', this.onViewClick, this);
39503             this.setValue(this.defaultDialCode);
39504         },
39505         
39506         onTriggerClick : function(e)
39507         {
39508             Roo.log('trigger click');
39509             if(this.disabled){
39510                 return;
39511             }
39512             
39513             if(this.isExpanded()){
39514                 this.collapse();
39515                 this.hasFocus = false;
39516             }else {
39517                 this.store.load({});
39518                 this.hasFocus = true;
39519                 this.expand();
39520             }
39521         },
39522         
39523         isExpanded : function()
39524         {
39525             return this.list.isVisible();
39526         },
39527         
39528         collapse : function()
39529         {
39530             if(!this.isExpanded()){
39531                 return;
39532             }
39533             this.list.hide();
39534             Roo.get(document).un('mousedown', this.collapseIf, this);
39535             Roo.get(document).un('mousewheel', this.collapseIf, this);
39536             this.fireEvent('collapse', this);
39537             this.validate();
39538         },
39539         
39540         expand : function()
39541         {
39542             Roo.log('expand');
39543
39544             if(this.isExpanded() || !this.hasFocus){
39545                 return;
39546             }
39547             
39548             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39549             this.list.setWidth(lw);
39550             
39551             this.list.show();
39552             this.restrictHeight();
39553             
39554             Roo.get(document).on('mousedown', this.collapseIf, this);
39555             Roo.get(document).on('mousewheel', this.collapseIf, this);
39556             
39557             this.fireEvent('expand', this);
39558         },
39559         
39560         restrictHeight : function()
39561         {
39562             this.list.alignTo(this.inputEl(), this.listAlign);
39563             this.list.alignTo(this.inputEl(), this.listAlign);
39564         },
39565         
39566         onViewOver : function(e, t)
39567         {
39568             if(this.inKeyMode){
39569                 return;
39570             }
39571             var item = this.view.findItemFromChild(t);
39572             
39573             if(item){
39574                 var index = this.view.indexOf(item);
39575                 this.select(index, false);
39576             }
39577         },
39578
39579         // private
39580         onViewClick : function(view, doFocus, el, e)
39581         {
39582             var index = this.view.getSelectedIndexes()[0];
39583             
39584             var r = this.store.getAt(index);
39585             
39586             if(r){
39587                 this.onSelect(r, index);
39588             }
39589             if(doFocus !== false && !this.blockFocus){
39590                 this.inputEl().focus();
39591             }
39592         },
39593         
39594         onViewMove : function(e, t)
39595         {
39596             this.inKeyMode = false;
39597         },
39598         
39599         select : function(index, scrollIntoView)
39600         {
39601             this.selectedIndex = index;
39602             this.view.select(index);
39603             if(scrollIntoView !== false){
39604                 var el = this.view.getNode(index);
39605                 if(el){
39606                     this.list.scrollChildIntoView(el, false);
39607                 }
39608             }
39609         },
39610         
39611         createList : function()
39612         {
39613             this.list = Roo.get(document.body).createChild({
39614                 tag: 'ul',
39615                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39616                 style: 'display:none'
39617             });
39618             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39619         },
39620         
39621         collapseIf : function(e)
39622         {
39623             var in_combo  = e.within(this.el);
39624             var in_list =  e.within(this.list);
39625             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39626             
39627             if (in_combo || in_list || is_list) {
39628                 return;
39629             }
39630             this.collapse();
39631         },
39632         
39633         onSelect : function(record, index)
39634         {
39635             if(this.fireEvent('beforeselect', this, record, index) !== false){
39636                 
39637                 this.setFlagClass(record.data.iso2);
39638                 this.setDialCode(record.data.dialCode);
39639                 this.hasFocus = false;
39640                 this.collapse();
39641                 this.fireEvent('select', this, record, index);
39642             }
39643         },
39644         
39645         flagEl : function()
39646         {
39647             var flag = this.el.select('div.flag',true).first();
39648             if(!flag){
39649                 return false;
39650             }
39651             return flag;
39652         },
39653         
39654         dialCodeHolderEl : function()
39655         {
39656             var d = this.el.select('input.dial-code-holder',true).first();
39657             if(!d){
39658                 return false;
39659             }
39660             return d;
39661         },
39662         
39663         setDialCode : function(v)
39664         {
39665             this.dialCodeHolder.dom.value = '+'+v;
39666         },
39667         
39668         setFlagClass : function(n)
39669         {
39670             this.flag.dom.className = 'flag '+n;
39671         },
39672         
39673         getValue : function()
39674         {
39675             var v = this.inputEl().getValue();
39676             if(this.dialCodeHolder) {
39677                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39678             }
39679             return v;
39680         },
39681         
39682         setValue : function(v)
39683         {
39684             var d = this.getDialCode(v);
39685             
39686             //invalid dial code
39687             if(v.length == 0 || !d || d.length == 0) {
39688                 if(this.rendered){
39689                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39690                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39691                 }
39692                 return;
39693             }
39694             
39695             //valid dial code
39696             this.setFlagClass(this.dialCodeMapping[d].iso2);
39697             this.setDialCode(d);
39698             this.inputEl().dom.value = v.replace('+'+d,'');
39699             this.hiddenEl().dom.value = this.getValue();
39700             
39701             this.validate();
39702         },
39703         
39704         getDialCode : function(v = '')
39705         {
39706             if (v.length == 0) {
39707                 return this.dialCodeHolder.dom.value;
39708             }
39709             
39710             var dialCode = "";
39711             if (v.charAt(0) != "+") {
39712                 return false;
39713             }
39714             var numericChars = "";
39715             for (var i = 1; i < v.length; i++) {
39716               var c = v.charAt(i);
39717               if (!isNaN(c)) {
39718                 numericChars += c;
39719                 if (this.dialCodeMapping[numericChars]) {
39720                   dialCode = v.substr(1, i);
39721                 }
39722                 if (numericChars.length == 4) {
39723                   break;
39724                 }
39725               }
39726             }
39727             return dialCode;
39728         },
39729         
39730         reset : function()
39731         {
39732             this.setValue(this.defaultDialCode);
39733             this.validate();
39734         },
39735         
39736         hiddenEl : function()
39737         {
39738             return this.el.select('input.hidden-tel-input',true).first();
39739         },
39740         
39741         onKeyUp : function(e){
39742             
39743             var k = e.getKey();
39744             var c = e.getCharCode();
39745             
39746             Roo.log(c);
39747             
39748             if(
39749                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39750                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39751             ){
39752                 e.stopEvent();
39753             }
39754             
39755             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39756                 return;
39757             }
39758             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39759                 e.stopEvent();
39760             }
39761             
39762             this.setValue(this.getValue());
39763         }
39764         
39765 });