less/roojs-bootstrap/money-field.less
[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.addClass('invisible');
8919             
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.removeClass('visible');
9228             this.indicator.addClass('invisible');
9229         }
9230         
9231         this.el.addClass(this.validClass);
9232         
9233         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9234             
9235             var feedback = this.el.select('.form-control-feedback', true).first();
9236             
9237             if(feedback){
9238                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9239                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9240             }
9241             
9242         }
9243         
9244         this.fireEvent('valid', this);
9245     },
9246     
9247      /**
9248      * Mark this field as invalid
9249      * @param {String} msg The validation message
9250      */
9251     markInvalid : function(msg)
9252     {
9253         if(!this.el  || this.preventMark){ // not rendered
9254             return;
9255         }
9256         
9257         this.el.removeClass([this.invalidClass, this.validClass]);
9258         
9259         var feedback = this.el.select('.form-control-feedback', true).first();
9260             
9261         if(feedback){
9262             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9263         }
9264
9265         if(this.disabled){
9266             return;
9267         }
9268         
9269         if(this.allowBlank && !this.getRawValue().length){
9270             return;
9271         }
9272         
9273         if(this.indicator){
9274             this.indicator.removeClass('invisible');
9275             this.indicator.addClass('visible');
9276         }
9277         
9278         this.el.addClass(this.invalidClass);
9279         
9280         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9281             
9282             var feedback = this.el.select('.form-control-feedback', true).first();
9283             
9284             if(feedback){
9285                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9286                 
9287                 if(this.getValue().length || this.forceFeedback){
9288                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9289                 }
9290                 
9291             }
9292             
9293         }
9294         
9295         this.fireEvent('invalid', this, msg);
9296     },
9297     // private
9298     SafariOnKeyDown : function(event)
9299     {
9300         // this is a workaround for a password hang bug on chrome/ webkit.
9301         if (this.inputEl().dom.type != 'password') {
9302             return;
9303         }
9304         
9305         var isSelectAll = false;
9306         
9307         if(this.inputEl().dom.selectionEnd > 0){
9308             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9309         }
9310         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9311             event.preventDefault();
9312             this.setValue('');
9313             return;
9314         }
9315         
9316         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9317             
9318             event.preventDefault();
9319             // this is very hacky as keydown always get's upper case.
9320             //
9321             var cc = String.fromCharCode(event.getCharCode());
9322             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9323             
9324         }
9325     },
9326     adjustWidth : function(tag, w){
9327         tag = tag.toLowerCase();
9328         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9329             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9330                 if(tag == 'input'){
9331                     return w + 2;
9332                 }
9333                 if(tag == 'textarea'){
9334                     return w-2;
9335                 }
9336             }else if(Roo.isOpera){
9337                 if(tag == 'input'){
9338                     return w + 2;
9339                 }
9340                 if(tag == 'textarea'){
9341                     return w-2;
9342                 }
9343             }
9344         }
9345         return w;
9346     },
9347     
9348     setFieldLabel : function(v)
9349     {
9350         this.fieldLabel = v;
9351         
9352         if(this.rendered){
9353             this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9354         }
9355     }
9356 });
9357
9358  
9359 /*
9360  * - LGPL
9361  *
9362  * Input
9363  * 
9364  */
9365
9366 /**
9367  * @class Roo.bootstrap.TextArea
9368  * @extends Roo.bootstrap.Input
9369  * Bootstrap TextArea class
9370  * @cfg {Number} cols Specifies the visible width of a text area
9371  * @cfg {Number} rows Specifies the visible number of lines in a text area
9372  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9373  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9374  * @cfg {string} html text
9375  * 
9376  * @constructor
9377  * Create a new TextArea
9378  * @param {Object} config The config object
9379  */
9380
9381 Roo.bootstrap.TextArea = function(config){
9382     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9383    
9384 };
9385
9386 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9387      
9388     cols : false,
9389     rows : 5,
9390     readOnly : false,
9391     warp : 'soft',
9392     resize : false,
9393     value: false,
9394     html: false,
9395     
9396     getAutoCreate : function(){
9397         
9398         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9399         
9400         var id = Roo.id();
9401         
9402         var cfg = {};
9403         
9404         if(this.inputType != 'hidden'){
9405             cfg.cls = 'form-group' //input-group
9406         }
9407         
9408         var input =  {
9409             tag: 'textarea',
9410             id : id,
9411             warp : this.warp,
9412             rows : this.rows,
9413             value : this.value || '',
9414             html: this.html || '',
9415             cls : 'form-control',
9416             placeholder : this.placeholder || '' 
9417             
9418         };
9419         
9420         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9421             input.maxLength = this.maxLength;
9422         }
9423         
9424         if(this.resize){
9425             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9426         }
9427         
9428         if(this.cols){
9429             input.cols = this.cols;
9430         }
9431         
9432         if (this.readOnly) {
9433             input.readonly = true;
9434         }
9435         
9436         if (this.name) {
9437             input.name = this.name;
9438         }
9439         
9440         if (this.size) {
9441             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9442         }
9443         
9444         var settings=this;
9445         ['xs','sm','md','lg'].map(function(size){
9446             if (settings[size]) {
9447                 cfg.cls += ' col-' + size + '-' + settings[size];
9448             }
9449         });
9450         
9451         var inputblock = input;
9452         
9453         if(this.hasFeedback && !this.allowBlank){
9454             
9455             var feedback = {
9456                 tag: 'span',
9457                 cls: 'glyphicon form-control-feedback'
9458             };
9459
9460             inputblock = {
9461                 cls : 'has-feedback',
9462                 cn :  [
9463                     input,
9464                     feedback
9465                 ] 
9466             };  
9467         }
9468         
9469         
9470         if (this.before || this.after) {
9471             
9472             inputblock = {
9473                 cls : 'input-group',
9474                 cn :  [] 
9475             };
9476             if (this.before) {
9477                 inputblock.cn.push({
9478                     tag :'span',
9479                     cls : 'input-group-addon',
9480                     html : this.before
9481                 });
9482             }
9483             
9484             inputblock.cn.push(input);
9485             
9486             if(this.hasFeedback && !this.allowBlank){
9487                 inputblock.cls += ' has-feedback';
9488                 inputblock.cn.push(feedback);
9489             }
9490             
9491             if (this.after) {
9492                 inputblock.cn.push({
9493                     tag :'span',
9494                     cls : 'input-group-addon',
9495                     html : this.after
9496                 });
9497             }
9498             
9499         }
9500         
9501         if (align ==='left' && this.fieldLabel.length) {
9502             cfg.cn = [
9503                 {
9504                     tag: 'label',
9505                     'for' :  id,
9506                     cls : 'control-label',
9507                     html : this.fieldLabel
9508                 },
9509                 {
9510                     cls : "",
9511                     cn: [
9512                         inputblock
9513                     ]
9514                 }
9515
9516             ];
9517             
9518             if(this.labelWidth > 12){
9519                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9520             }
9521
9522             if(this.labelWidth < 13 && this.labelmd == 0){
9523                 this.labelmd = this.labelWidth;
9524             }
9525
9526             if(this.labellg > 0){
9527                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9528                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9529             }
9530
9531             if(this.labelmd > 0){
9532                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9533                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9534             }
9535
9536             if(this.labelsm > 0){
9537                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9538                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9539             }
9540
9541             if(this.labelxs > 0){
9542                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9543                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9544             }
9545             
9546         } else if ( this.fieldLabel.length) {
9547             cfg.cn = [
9548
9549                {
9550                    tag: 'label',
9551                    //cls : 'input-group-addon',
9552                    html : this.fieldLabel
9553
9554                },
9555
9556                inputblock
9557
9558            ];
9559
9560         } else {
9561
9562             cfg.cn = [
9563
9564                 inputblock
9565
9566             ];
9567                 
9568         }
9569         
9570         if (this.disabled) {
9571             input.disabled=true;
9572         }
9573         
9574         return cfg;
9575         
9576     },
9577     /**
9578      * return the real textarea element.
9579      */
9580     inputEl: function ()
9581     {
9582         return this.el.select('textarea.form-control',true).first();
9583     },
9584     
9585     /**
9586      * Clear any invalid styles/messages for this field
9587      */
9588     clearInvalid : function()
9589     {
9590         
9591         if(!this.el || this.preventMark){ // not rendered
9592             return;
9593         }
9594         
9595         var label = this.el.select('label', true).first();
9596         var icon = this.el.select('i.fa-star', true).first();
9597         
9598         if(label && icon){
9599             icon.remove();
9600         }
9601         
9602         this.el.removeClass(this.invalidClass);
9603         
9604         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9605             
9606             var feedback = this.el.select('.form-control-feedback', true).first();
9607             
9608             if(feedback){
9609                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9610             }
9611             
9612         }
9613         
9614         this.fireEvent('valid', this);
9615     },
9616     
9617      /**
9618      * Mark this field as valid
9619      */
9620     markValid : function()
9621     {
9622         if(!this.el  || this.preventMark){ // not rendered
9623             return;
9624         }
9625         
9626         this.el.removeClass([this.invalidClass, this.validClass]);
9627         
9628         var feedback = this.el.select('.form-control-feedback', true).first();
9629             
9630         if(feedback){
9631             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9632         }
9633
9634         if(this.disabled || this.allowBlank){
9635             return;
9636         }
9637         
9638         var label = this.el.select('label', true).first();
9639         var icon = this.el.select('i.fa-star', true).first();
9640         
9641         if(label && icon){
9642             icon.remove();
9643         }
9644         
9645         this.el.addClass(this.validClass);
9646         
9647         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9648             
9649             var feedback = this.el.select('.form-control-feedback', true).first();
9650             
9651             if(feedback){
9652                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9653                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9654             }
9655             
9656         }
9657         
9658         this.fireEvent('valid', this);
9659     },
9660     
9661      /**
9662      * Mark this field as invalid
9663      * @param {String} msg The validation message
9664      */
9665     markInvalid : function(msg)
9666     {
9667         if(!this.el  || this.preventMark){ // not rendered
9668             return;
9669         }
9670         
9671         this.el.removeClass([this.invalidClass, this.validClass]);
9672         
9673         var feedback = this.el.select('.form-control-feedback', true).first();
9674             
9675         if(feedback){
9676             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9677         }
9678
9679         if(this.disabled || this.allowBlank){
9680             return;
9681         }
9682         
9683         var label = this.el.select('label', true).first();
9684         var icon = this.el.select('i.fa-star', true).first();
9685         
9686         if(!this.getValue().length && label && !icon){
9687             this.el.createChild({
9688                 tag : 'i',
9689                 cls : 'text-danger fa fa-lg fa-star',
9690                 tooltip : 'This field is required',
9691                 style : 'margin-right:5px;'
9692             }, label, true);
9693         }
9694
9695         this.el.addClass(this.invalidClass);
9696         
9697         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9698             
9699             var feedback = this.el.select('.form-control-feedback', true).first();
9700             
9701             if(feedback){
9702                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9703                 
9704                 if(this.getValue().length || this.forceFeedback){
9705                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9706                 }
9707                 
9708             }
9709             
9710         }
9711         
9712         this.fireEvent('invalid', this, msg);
9713     }
9714 });
9715
9716  
9717 /*
9718  * - LGPL
9719  *
9720  * trigger field - base class for combo..
9721  * 
9722  */
9723  
9724 /**
9725  * @class Roo.bootstrap.TriggerField
9726  * @extends Roo.bootstrap.Input
9727  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9728  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9729  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9730  * for which you can provide a custom implementation.  For example:
9731  * <pre><code>
9732 var trigger = new Roo.bootstrap.TriggerField();
9733 trigger.onTriggerClick = myTriggerFn;
9734 trigger.applyTo('my-field');
9735 </code></pre>
9736  *
9737  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9738  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9739  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9740  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9741  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9742
9743  * @constructor
9744  * Create a new TriggerField.
9745  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9746  * to the base TextField)
9747  */
9748 Roo.bootstrap.TriggerField = function(config){
9749     this.mimicing = false;
9750     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9751 };
9752
9753 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9754     /**
9755      * @cfg {String} triggerClass A CSS class to apply to the trigger
9756      */
9757      /**
9758      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9759      */
9760     hideTrigger:false,
9761
9762     /**
9763      * @cfg {Boolean} removable (true|false) special filter default false
9764      */
9765     removable : false,
9766     
9767     /** @cfg {Boolean} grow @hide */
9768     /** @cfg {Number} growMin @hide */
9769     /** @cfg {Number} growMax @hide */
9770
9771     /**
9772      * @hide 
9773      * @method
9774      */
9775     autoSize: Roo.emptyFn,
9776     // private
9777     monitorTab : true,
9778     // private
9779     deferHeight : true,
9780
9781     
9782     actionMode : 'wrap',
9783     
9784     caret : false,
9785     
9786     
9787     getAutoCreate : function(){
9788        
9789         var align = this.labelAlign || this.parentLabelAlign();
9790         
9791         var id = Roo.id();
9792         
9793         var cfg = {
9794             cls: 'form-group' //input-group
9795         };
9796         
9797         
9798         var input =  {
9799             tag: 'input',
9800             id : id,
9801             type : this.inputType,
9802             cls : 'form-control',
9803             autocomplete: 'new-password',
9804             placeholder : this.placeholder || '' 
9805             
9806         };
9807         if (this.name) {
9808             input.name = this.name;
9809         }
9810         if (this.size) {
9811             input.cls += ' input-' + this.size;
9812         }
9813         
9814         if (this.disabled) {
9815             input.disabled=true;
9816         }
9817         
9818         var inputblock = input;
9819         
9820         if(this.hasFeedback && !this.allowBlank){
9821             
9822             var feedback = {
9823                 tag: 'span',
9824                 cls: 'glyphicon form-control-feedback'
9825             };
9826             
9827             if(this.removable && !this.editable && !this.tickable){
9828                 inputblock = {
9829                     cls : 'has-feedback',
9830                     cn :  [
9831                         inputblock,
9832                         {
9833                             tag: 'button',
9834                             html : 'x',
9835                             cls : 'roo-combo-removable-btn close'
9836                         },
9837                         feedback
9838                     ] 
9839                 };
9840             } else {
9841                 inputblock = {
9842                     cls : 'has-feedback',
9843                     cn :  [
9844                         inputblock,
9845                         feedback
9846                     ] 
9847                 };
9848             }
9849
9850         } else {
9851             if(this.removable && !this.editable && !this.tickable){
9852                 inputblock = {
9853                     cls : 'roo-removable',
9854                     cn :  [
9855                         inputblock,
9856                         {
9857                             tag: 'button',
9858                             html : 'x',
9859                             cls : 'roo-combo-removable-btn close'
9860                         }
9861                     ] 
9862                 };
9863             }
9864         }
9865         
9866         if (this.before || this.after) {
9867             
9868             inputblock = {
9869                 cls : 'input-group',
9870                 cn :  [] 
9871             };
9872             if (this.before) {
9873                 inputblock.cn.push({
9874                     tag :'span',
9875                     cls : 'input-group-addon',
9876                     html : this.before
9877                 });
9878             }
9879             
9880             inputblock.cn.push(input);
9881             
9882             if(this.hasFeedback && !this.allowBlank){
9883                 inputblock.cls += ' has-feedback';
9884                 inputblock.cn.push(feedback);
9885             }
9886             
9887             if (this.after) {
9888                 inputblock.cn.push({
9889                     tag :'span',
9890                     cls : 'input-group-addon',
9891                     html : this.after
9892                 });
9893             }
9894             
9895         };
9896         
9897         var box = {
9898             tag: 'div',
9899             cn: [
9900                 {
9901                     tag: 'input',
9902                     type : 'hidden',
9903                     cls: 'form-hidden-field'
9904                 },
9905                 inputblock
9906             ]
9907             
9908         };
9909         
9910         if(this.multiple){
9911             box = {
9912                 tag: 'div',
9913                 cn: [
9914                     {
9915                         tag: 'input',
9916                         type : 'hidden',
9917                         cls: 'form-hidden-field'
9918                     },
9919                     {
9920                         tag: 'ul',
9921                         cls: 'roo-select2-choices',
9922                         cn:[
9923                             {
9924                                 tag: 'li',
9925                                 cls: 'roo-select2-search-field',
9926                                 cn: [
9927
9928                                     inputblock
9929                                 ]
9930                             }
9931                         ]
9932                     }
9933                 ]
9934             }
9935         };
9936         
9937         var combobox = {
9938             cls: 'roo-select2-container input-group',
9939             cn: [
9940                 box
9941 //                {
9942 //                    tag: 'ul',
9943 //                    cls: 'typeahead typeahead-long dropdown-menu',
9944 //                    style: 'display:none'
9945 //                }
9946             ]
9947         };
9948         
9949         if(!this.multiple && this.showToggleBtn){
9950             
9951             var caret = {
9952                         tag: 'span',
9953                         cls: 'caret'
9954              };
9955             if (this.caret != false) {
9956                 caret = {
9957                      tag: 'i',
9958                      cls: 'fa fa-' + this.caret
9959                 };
9960                 
9961             }
9962             
9963             combobox.cn.push({
9964                 tag :'span',
9965                 cls : 'input-group-addon btn dropdown-toggle',
9966                 cn : [
9967                     caret,
9968                     {
9969                         tag: 'span',
9970                         cls: 'combobox-clear',
9971                         cn  : [
9972                             {
9973                                 tag : 'i',
9974                                 cls: 'icon-remove'
9975                             }
9976                         ]
9977                     }
9978                 ]
9979
9980             })
9981         }
9982         
9983         if(this.multiple){
9984             combobox.cls += ' roo-select2-container-multi';
9985         }
9986         
9987         if (align ==='left' && this.fieldLabel.length) {
9988             
9989             cfg.cls += ' roo-form-group-label-left';
9990
9991             cfg.cn = [
9992                 {
9993                     tag : 'i',
9994                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9995                     tooltip : 'This field is required'
9996                 },
9997                 {
9998                     tag: 'label',
9999                     'for' :  id,
10000                     cls : 'control-label',
10001                     html : this.fieldLabel
10002
10003                 },
10004                 {
10005                     cls : "", 
10006                     cn: [
10007                         combobox
10008                     ]
10009                 }
10010
10011             ];
10012             
10013             var labelCfg = cfg.cn[1];
10014             var contentCfg = cfg.cn[2];
10015             
10016             if(this.indicatorpos == 'right'){
10017                 cfg.cn = [
10018                     {
10019                         tag: 'label',
10020                         'for' :  id,
10021                         cls : 'control-label',
10022                         cn : [
10023                             {
10024                                 tag : 'span',
10025                                 html : this.fieldLabel
10026                             },
10027                             {
10028                                 tag : 'i',
10029                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10030                                 tooltip : 'This field is required'
10031                             }
10032                         ]
10033                     },
10034                     {
10035                         cls : "", 
10036                         cn: [
10037                             combobox
10038                         ]
10039                     }
10040
10041                 ];
10042                 
10043                 labelCfg = cfg.cn[0];
10044                 contentCfg = cfg.cn[1];
10045             }
10046             
10047             if(this.labelWidth > 12){
10048                 labelCfg.style = "width: " + this.labelWidth + 'px';
10049             }
10050             
10051             if(this.labelWidth < 13 && this.labelmd == 0){
10052                 this.labelmd = this.labelWidth;
10053             }
10054             
10055             if(this.labellg > 0){
10056                 labelCfg.cls += ' col-lg-' + this.labellg;
10057                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10058             }
10059             
10060             if(this.labelmd > 0){
10061                 labelCfg.cls += ' col-md-' + this.labelmd;
10062                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10063             }
10064             
10065             if(this.labelsm > 0){
10066                 labelCfg.cls += ' col-sm-' + this.labelsm;
10067                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10068             }
10069             
10070             if(this.labelxs > 0){
10071                 labelCfg.cls += ' col-xs-' + this.labelxs;
10072                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10073             }
10074             
10075         } else if ( this.fieldLabel.length) {
10076 //                Roo.log(" label");
10077             cfg.cn = [
10078                 {
10079                    tag : 'i',
10080                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10081                    tooltip : 'This field is required'
10082                },
10083                {
10084                    tag: 'label',
10085                    //cls : 'input-group-addon',
10086                    html : this.fieldLabel
10087
10088                },
10089
10090                combobox
10091
10092             ];
10093             
10094             if(this.indicatorpos == 'right'){
10095                 
10096                 cfg.cn = [
10097                     {
10098                        tag: 'label',
10099                        cn : [
10100                            {
10101                                tag : 'span',
10102                                html : this.fieldLabel
10103                            },
10104                            {
10105                               tag : 'i',
10106                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10107                               tooltip : 'This field is required'
10108                            }
10109                        ]
10110
10111                     },
10112                     combobox
10113
10114                 ];
10115
10116             }
10117
10118         } else {
10119             
10120 //                Roo.log(" no label && no align");
10121                 cfg = combobox
10122                      
10123                 
10124         }
10125         
10126         var settings=this;
10127         ['xs','sm','md','lg'].map(function(size){
10128             if (settings[size]) {
10129                 cfg.cls += ' col-' + size + '-' + settings[size];
10130             }
10131         });
10132         
10133         return cfg;
10134         
10135     },
10136     
10137     
10138     
10139     // private
10140     onResize : function(w, h){
10141 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10142 //        if(typeof w == 'number'){
10143 //            var x = w - this.trigger.getWidth();
10144 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10145 //            this.trigger.setStyle('left', x+'px');
10146 //        }
10147     },
10148
10149     // private
10150     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10151
10152     // private
10153     getResizeEl : function(){
10154         return this.inputEl();
10155     },
10156
10157     // private
10158     getPositionEl : function(){
10159         return this.inputEl();
10160     },
10161
10162     // private
10163     alignErrorIcon : function(){
10164         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10165     },
10166
10167     // private
10168     initEvents : function(){
10169         
10170         this.createList();
10171         
10172         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10173         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10174         if(!this.multiple && this.showToggleBtn){
10175             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10176             if(this.hideTrigger){
10177                 this.trigger.setDisplayed(false);
10178             }
10179             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10180         }
10181         
10182         if(this.multiple){
10183             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10184         }
10185         
10186         if(this.removable && !this.editable && !this.tickable){
10187             var close = this.closeTriggerEl();
10188             
10189             if(close){
10190                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10191                 close.on('click', this.removeBtnClick, this, close);
10192             }
10193         }
10194         
10195         //this.trigger.addClassOnOver('x-form-trigger-over');
10196         //this.trigger.addClassOnClick('x-form-trigger-click');
10197         
10198         //if(!this.width){
10199         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10200         //}
10201     },
10202     
10203     closeTriggerEl : function()
10204     {
10205         var close = this.el.select('.roo-combo-removable-btn', true).first();
10206         return close ? close : false;
10207     },
10208     
10209     removeBtnClick : function(e, h, el)
10210     {
10211         e.preventDefault();
10212         
10213         if(this.fireEvent("remove", this) !== false){
10214             this.reset();
10215             this.fireEvent("afterremove", this)
10216         }
10217     },
10218     
10219     createList : function()
10220     {
10221         this.list = Roo.get(document.body).createChild({
10222             tag: 'ul',
10223             cls: 'typeahead typeahead-long dropdown-menu',
10224             style: 'display:none'
10225         });
10226         
10227         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10228         
10229     },
10230
10231     // private
10232     initTrigger : function(){
10233        
10234     },
10235
10236     // private
10237     onDestroy : function(){
10238         if(this.trigger){
10239             this.trigger.removeAllListeners();
10240           //  this.trigger.remove();
10241         }
10242         //if(this.wrap){
10243         //    this.wrap.remove();
10244         //}
10245         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10246     },
10247
10248     // private
10249     onFocus : function(){
10250         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10251         /*
10252         if(!this.mimicing){
10253             this.wrap.addClass('x-trigger-wrap-focus');
10254             this.mimicing = true;
10255             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10256             if(this.monitorTab){
10257                 this.el.on("keydown", this.checkTab, this);
10258             }
10259         }
10260         */
10261     },
10262
10263     // private
10264     checkTab : function(e){
10265         if(e.getKey() == e.TAB){
10266             this.triggerBlur();
10267         }
10268     },
10269
10270     // private
10271     onBlur : function(){
10272         // do nothing
10273     },
10274
10275     // private
10276     mimicBlur : function(e, t){
10277         /*
10278         if(!this.wrap.contains(t) && this.validateBlur()){
10279             this.triggerBlur();
10280         }
10281         */
10282     },
10283
10284     // private
10285     triggerBlur : function(){
10286         this.mimicing = false;
10287         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10288         if(this.monitorTab){
10289             this.el.un("keydown", this.checkTab, this);
10290         }
10291         //this.wrap.removeClass('x-trigger-wrap-focus');
10292         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10293     },
10294
10295     // private
10296     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10297     validateBlur : function(e, t){
10298         return true;
10299     },
10300
10301     // private
10302     onDisable : function(){
10303         this.inputEl().dom.disabled = true;
10304         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10305         //if(this.wrap){
10306         //    this.wrap.addClass('x-item-disabled');
10307         //}
10308     },
10309
10310     // private
10311     onEnable : function(){
10312         this.inputEl().dom.disabled = false;
10313         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10314         //if(this.wrap){
10315         //    this.el.removeClass('x-item-disabled');
10316         //}
10317     },
10318
10319     // private
10320     onShow : function(){
10321         var ae = this.getActionEl();
10322         
10323         if(ae){
10324             ae.dom.style.display = '';
10325             ae.dom.style.visibility = 'visible';
10326         }
10327     },
10328
10329     // private
10330     
10331     onHide : function(){
10332         var ae = this.getActionEl();
10333         ae.dom.style.display = 'none';
10334     },
10335
10336     /**
10337      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10338      * by an implementing function.
10339      * @method
10340      * @param {EventObject} e
10341      */
10342     onTriggerClick : Roo.emptyFn
10343 });
10344  /*
10345  * Based on:
10346  * Ext JS Library 1.1.1
10347  * Copyright(c) 2006-2007, Ext JS, LLC.
10348  *
10349  * Originally Released Under LGPL - original licence link has changed is not relivant.
10350  *
10351  * Fork - LGPL
10352  * <script type="text/javascript">
10353  */
10354
10355
10356 /**
10357  * @class Roo.data.SortTypes
10358  * @singleton
10359  * Defines the default sorting (casting?) comparison functions used when sorting data.
10360  */
10361 Roo.data.SortTypes = {
10362     /**
10363      * Default sort that does nothing
10364      * @param {Mixed} s The value being converted
10365      * @return {Mixed} The comparison value
10366      */
10367     none : function(s){
10368         return s;
10369     },
10370     
10371     /**
10372      * The regular expression used to strip tags
10373      * @type {RegExp}
10374      * @property
10375      */
10376     stripTagsRE : /<\/?[^>]+>/gi,
10377     
10378     /**
10379      * Strips all HTML tags to sort on text only
10380      * @param {Mixed} s The value being converted
10381      * @return {String} The comparison value
10382      */
10383     asText : function(s){
10384         return String(s).replace(this.stripTagsRE, "");
10385     },
10386     
10387     /**
10388      * Strips all HTML tags to sort on text only - Case insensitive
10389      * @param {Mixed} s The value being converted
10390      * @return {String} The comparison value
10391      */
10392     asUCText : function(s){
10393         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10394     },
10395     
10396     /**
10397      * Case insensitive string
10398      * @param {Mixed} s The value being converted
10399      * @return {String} The comparison value
10400      */
10401     asUCString : function(s) {
10402         return String(s).toUpperCase();
10403     },
10404     
10405     /**
10406      * Date sorting
10407      * @param {Mixed} s The value being converted
10408      * @return {Number} The comparison value
10409      */
10410     asDate : function(s) {
10411         if(!s){
10412             return 0;
10413         }
10414         if(s instanceof Date){
10415             return s.getTime();
10416         }
10417         return Date.parse(String(s));
10418     },
10419     
10420     /**
10421      * Float sorting
10422      * @param {Mixed} s The value being converted
10423      * @return {Float} The comparison value
10424      */
10425     asFloat : function(s) {
10426         var val = parseFloat(String(s).replace(/,/g, ""));
10427         if(isNaN(val)) {
10428             val = 0;
10429         }
10430         return val;
10431     },
10432     
10433     /**
10434      * Integer sorting
10435      * @param {Mixed} s The value being converted
10436      * @return {Number} The comparison value
10437      */
10438     asInt : function(s) {
10439         var val = parseInt(String(s).replace(/,/g, ""));
10440         if(isNaN(val)) {
10441             val = 0;
10442         }
10443         return val;
10444     }
10445 };/*
10446  * Based on:
10447  * Ext JS Library 1.1.1
10448  * Copyright(c) 2006-2007, Ext JS, LLC.
10449  *
10450  * Originally Released Under LGPL - original licence link has changed is not relivant.
10451  *
10452  * Fork - LGPL
10453  * <script type="text/javascript">
10454  */
10455
10456 /**
10457 * @class Roo.data.Record
10458  * Instances of this class encapsulate both record <em>definition</em> information, and record
10459  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10460  * to access Records cached in an {@link Roo.data.Store} object.<br>
10461  * <p>
10462  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10463  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10464  * objects.<br>
10465  * <p>
10466  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10467  * @constructor
10468  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10469  * {@link #create}. The parameters are the same.
10470  * @param {Array} data An associative Array of data values keyed by the field name.
10471  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10472  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10473  * not specified an integer id is generated.
10474  */
10475 Roo.data.Record = function(data, id){
10476     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10477     this.data = data;
10478 };
10479
10480 /**
10481  * Generate a constructor for a specific record layout.
10482  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10483  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10484  * Each field definition object may contain the following properties: <ul>
10485  * <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,
10486  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10487  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10488  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10489  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10490  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10491  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10492  * this may be omitted.</p></li>
10493  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10494  * <ul><li>auto (Default, implies no conversion)</li>
10495  * <li>string</li>
10496  * <li>int</li>
10497  * <li>float</li>
10498  * <li>boolean</li>
10499  * <li>date</li></ul></p></li>
10500  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10501  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10502  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10503  * by the Reader into an object that will be stored in the Record. It is passed the
10504  * following parameters:<ul>
10505  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10506  * </ul></p></li>
10507  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10508  * </ul>
10509  * <br>usage:<br><pre><code>
10510 var TopicRecord = Roo.data.Record.create(
10511     {name: 'title', mapping: 'topic_title'},
10512     {name: 'author', mapping: 'username'},
10513     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10514     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10515     {name: 'lastPoster', mapping: 'user2'},
10516     {name: 'excerpt', mapping: 'post_text'}
10517 );
10518
10519 var myNewRecord = new TopicRecord({
10520     title: 'Do my job please',
10521     author: 'noobie',
10522     totalPosts: 1,
10523     lastPost: new Date(),
10524     lastPoster: 'Animal',
10525     excerpt: 'No way dude!'
10526 });
10527 myStore.add(myNewRecord);
10528 </code></pre>
10529  * @method create
10530  * @static
10531  */
10532 Roo.data.Record.create = function(o){
10533     var f = function(){
10534         f.superclass.constructor.apply(this, arguments);
10535     };
10536     Roo.extend(f, Roo.data.Record);
10537     var p = f.prototype;
10538     p.fields = new Roo.util.MixedCollection(false, function(field){
10539         return field.name;
10540     });
10541     for(var i = 0, len = o.length; i < len; i++){
10542         p.fields.add(new Roo.data.Field(o[i]));
10543     }
10544     f.getField = function(name){
10545         return p.fields.get(name);  
10546     };
10547     return f;
10548 };
10549
10550 Roo.data.Record.AUTO_ID = 1000;
10551 Roo.data.Record.EDIT = 'edit';
10552 Roo.data.Record.REJECT = 'reject';
10553 Roo.data.Record.COMMIT = 'commit';
10554
10555 Roo.data.Record.prototype = {
10556     /**
10557      * Readonly flag - true if this record has been modified.
10558      * @type Boolean
10559      */
10560     dirty : false,
10561     editing : false,
10562     error: null,
10563     modified: null,
10564
10565     // private
10566     join : function(store){
10567         this.store = store;
10568     },
10569
10570     /**
10571      * Set the named field to the specified value.
10572      * @param {String} name The name of the field to set.
10573      * @param {Object} value The value to set the field to.
10574      */
10575     set : function(name, value){
10576         if(this.data[name] == value){
10577             return;
10578         }
10579         this.dirty = true;
10580         if(!this.modified){
10581             this.modified = {};
10582         }
10583         if(typeof this.modified[name] == 'undefined'){
10584             this.modified[name] = this.data[name];
10585         }
10586         this.data[name] = value;
10587         if(!this.editing && this.store){
10588             this.store.afterEdit(this);
10589         }       
10590     },
10591
10592     /**
10593      * Get the value of the named field.
10594      * @param {String} name The name of the field to get the value of.
10595      * @return {Object} The value of the field.
10596      */
10597     get : function(name){
10598         return this.data[name]; 
10599     },
10600
10601     // private
10602     beginEdit : function(){
10603         this.editing = true;
10604         this.modified = {}; 
10605     },
10606
10607     // private
10608     cancelEdit : function(){
10609         this.editing = false;
10610         delete this.modified;
10611     },
10612
10613     // private
10614     endEdit : function(){
10615         this.editing = false;
10616         if(this.dirty && this.store){
10617             this.store.afterEdit(this);
10618         }
10619     },
10620
10621     /**
10622      * Usually called by the {@link Roo.data.Store} which owns the Record.
10623      * Rejects all changes made to the Record since either creation, or the last commit operation.
10624      * Modified fields are reverted to their original values.
10625      * <p>
10626      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10627      * of reject operations.
10628      */
10629     reject : function(){
10630         var m = this.modified;
10631         for(var n in m){
10632             if(typeof m[n] != "function"){
10633                 this.data[n] = m[n];
10634             }
10635         }
10636         this.dirty = false;
10637         delete this.modified;
10638         this.editing = false;
10639         if(this.store){
10640             this.store.afterReject(this);
10641         }
10642     },
10643
10644     /**
10645      * Usually called by the {@link Roo.data.Store} which owns the Record.
10646      * Commits all changes made to the Record since either creation, or the last commit operation.
10647      * <p>
10648      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10649      * of commit operations.
10650      */
10651     commit : function(){
10652         this.dirty = false;
10653         delete this.modified;
10654         this.editing = false;
10655         if(this.store){
10656             this.store.afterCommit(this);
10657         }
10658     },
10659
10660     // private
10661     hasError : function(){
10662         return this.error != null;
10663     },
10664
10665     // private
10666     clearError : function(){
10667         this.error = null;
10668     },
10669
10670     /**
10671      * Creates a copy of this record.
10672      * @param {String} id (optional) A new record id if you don't want to use this record's id
10673      * @return {Record}
10674      */
10675     copy : function(newId) {
10676         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10677     }
10678 };/*
10679  * Based on:
10680  * Ext JS Library 1.1.1
10681  * Copyright(c) 2006-2007, Ext JS, LLC.
10682  *
10683  * Originally Released Under LGPL - original licence link has changed is not relivant.
10684  *
10685  * Fork - LGPL
10686  * <script type="text/javascript">
10687  */
10688
10689
10690
10691 /**
10692  * @class Roo.data.Store
10693  * @extends Roo.util.Observable
10694  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10695  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10696  * <p>
10697  * 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
10698  * has no knowledge of the format of the data returned by the Proxy.<br>
10699  * <p>
10700  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10701  * instances from the data object. These records are cached and made available through accessor functions.
10702  * @constructor
10703  * Creates a new Store.
10704  * @param {Object} config A config object containing the objects needed for the Store to access data,
10705  * and read the data into Records.
10706  */
10707 Roo.data.Store = function(config){
10708     this.data = new Roo.util.MixedCollection(false);
10709     this.data.getKey = function(o){
10710         return o.id;
10711     };
10712     this.baseParams = {};
10713     // private
10714     this.paramNames = {
10715         "start" : "start",
10716         "limit" : "limit",
10717         "sort" : "sort",
10718         "dir" : "dir",
10719         "multisort" : "_multisort"
10720     };
10721
10722     if(config && config.data){
10723         this.inlineData = config.data;
10724         delete config.data;
10725     }
10726
10727     Roo.apply(this, config);
10728     
10729     if(this.reader){ // reader passed
10730         this.reader = Roo.factory(this.reader, Roo.data);
10731         this.reader.xmodule = this.xmodule || false;
10732         if(!this.recordType){
10733             this.recordType = this.reader.recordType;
10734         }
10735         if(this.reader.onMetaChange){
10736             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10737         }
10738     }
10739
10740     if(this.recordType){
10741         this.fields = this.recordType.prototype.fields;
10742     }
10743     this.modified = [];
10744
10745     this.addEvents({
10746         /**
10747          * @event datachanged
10748          * Fires when the data cache has changed, and a widget which is using this Store
10749          * as a Record cache should refresh its view.
10750          * @param {Store} this
10751          */
10752         datachanged : true,
10753         /**
10754          * @event metachange
10755          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10756          * @param {Store} this
10757          * @param {Object} meta The JSON metadata
10758          */
10759         metachange : true,
10760         /**
10761          * @event add
10762          * Fires when Records have been added to the Store
10763          * @param {Store} this
10764          * @param {Roo.data.Record[]} records The array of Records added
10765          * @param {Number} index The index at which the record(s) were added
10766          */
10767         add : true,
10768         /**
10769          * @event remove
10770          * Fires when a Record has been removed from the Store
10771          * @param {Store} this
10772          * @param {Roo.data.Record} record The Record that was removed
10773          * @param {Number} index The index at which the record was removed
10774          */
10775         remove : true,
10776         /**
10777          * @event update
10778          * Fires when a Record has been updated
10779          * @param {Store} this
10780          * @param {Roo.data.Record} record The Record that was updated
10781          * @param {String} operation The update operation being performed.  Value may be one of:
10782          * <pre><code>
10783  Roo.data.Record.EDIT
10784  Roo.data.Record.REJECT
10785  Roo.data.Record.COMMIT
10786          * </code></pre>
10787          */
10788         update : true,
10789         /**
10790          * @event clear
10791          * Fires when the data cache has been cleared.
10792          * @param {Store} this
10793          */
10794         clear : true,
10795         /**
10796          * @event beforeload
10797          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10798          * the load action will be canceled.
10799          * @param {Store} this
10800          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10801          */
10802         beforeload : true,
10803         /**
10804          * @event beforeloadadd
10805          * Fires after a new set of Records has been loaded.
10806          * @param {Store} this
10807          * @param {Roo.data.Record[]} records The Records that were loaded
10808          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10809          */
10810         beforeloadadd : true,
10811         /**
10812          * @event load
10813          * Fires after a new set of Records has been loaded, before they are added to the store.
10814          * @param {Store} this
10815          * @param {Roo.data.Record[]} records The Records that were loaded
10816          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10817          * @params {Object} return from reader
10818          */
10819         load : true,
10820         /**
10821          * @event loadexception
10822          * Fires if an exception occurs in the Proxy during loading.
10823          * Called with the signature of the Proxy's "loadexception" event.
10824          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10825          * 
10826          * @param {Proxy} 
10827          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10828          * @param {Object} load options 
10829          * @param {Object} jsonData from your request (normally this contains the Exception)
10830          */
10831         loadexception : true
10832     });
10833     
10834     if(this.proxy){
10835         this.proxy = Roo.factory(this.proxy, Roo.data);
10836         this.proxy.xmodule = this.xmodule || false;
10837         this.relayEvents(this.proxy,  ["loadexception"]);
10838     }
10839     this.sortToggle = {};
10840     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10841
10842     Roo.data.Store.superclass.constructor.call(this);
10843
10844     if(this.inlineData){
10845         this.loadData(this.inlineData);
10846         delete this.inlineData;
10847     }
10848 };
10849
10850 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10851      /**
10852     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10853     * without a remote query - used by combo/forms at present.
10854     */
10855     
10856     /**
10857     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10858     */
10859     /**
10860     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10861     */
10862     /**
10863     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10864     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10865     */
10866     /**
10867     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10868     * on any HTTP request
10869     */
10870     /**
10871     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10872     */
10873     /**
10874     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10875     */
10876     multiSort: false,
10877     /**
10878     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10879     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10880     */
10881     remoteSort : false,
10882
10883     /**
10884     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10885      * loaded or when a record is removed. (defaults to false).
10886     */
10887     pruneModifiedRecords : false,
10888
10889     // private
10890     lastOptions : null,
10891
10892     /**
10893      * Add Records to the Store and fires the add event.
10894      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10895      */
10896     add : function(records){
10897         records = [].concat(records);
10898         for(var i = 0, len = records.length; i < len; i++){
10899             records[i].join(this);
10900         }
10901         var index = this.data.length;
10902         this.data.addAll(records);
10903         this.fireEvent("add", this, records, index);
10904     },
10905
10906     /**
10907      * Remove a Record from the Store and fires the remove event.
10908      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10909      */
10910     remove : function(record){
10911         var index = this.data.indexOf(record);
10912         this.data.removeAt(index);
10913         if(this.pruneModifiedRecords){
10914             this.modified.remove(record);
10915         }
10916         this.fireEvent("remove", this, record, index);
10917     },
10918
10919     /**
10920      * Remove all Records from the Store and fires the clear event.
10921      */
10922     removeAll : function(){
10923         this.data.clear();
10924         if(this.pruneModifiedRecords){
10925             this.modified = [];
10926         }
10927         this.fireEvent("clear", this);
10928     },
10929
10930     /**
10931      * Inserts Records to the Store at the given index and fires the add event.
10932      * @param {Number} index The start index at which to insert the passed Records.
10933      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10934      */
10935     insert : function(index, records){
10936         records = [].concat(records);
10937         for(var i = 0, len = records.length; i < len; i++){
10938             this.data.insert(index, records[i]);
10939             records[i].join(this);
10940         }
10941         this.fireEvent("add", this, records, index);
10942     },
10943
10944     /**
10945      * Get the index within the cache of the passed Record.
10946      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10947      * @return {Number} The index of the passed Record. Returns -1 if not found.
10948      */
10949     indexOf : function(record){
10950         return this.data.indexOf(record);
10951     },
10952
10953     /**
10954      * Get the index within the cache of the Record with the passed id.
10955      * @param {String} id The id of the Record to find.
10956      * @return {Number} The index of the Record. Returns -1 if not found.
10957      */
10958     indexOfId : function(id){
10959         return this.data.indexOfKey(id);
10960     },
10961
10962     /**
10963      * Get the Record with the specified id.
10964      * @param {String} id The id of the Record to find.
10965      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10966      */
10967     getById : function(id){
10968         return this.data.key(id);
10969     },
10970
10971     /**
10972      * Get the Record at the specified index.
10973      * @param {Number} index The index of the Record to find.
10974      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10975      */
10976     getAt : function(index){
10977         return this.data.itemAt(index);
10978     },
10979
10980     /**
10981      * Returns a range of Records between specified indices.
10982      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10983      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10984      * @return {Roo.data.Record[]} An array of Records
10985      */
10986     getRange : function(start, end){
10987         return this.data.getRange(start, end);
10988     },
10989
10990     // private
10991     storeOptions : function(o){
10992         o = Roo.apply({}, o);
10993         delete o.callback;
10994         delete o.scope;
10995         this.lastOptions = o;
10996     },
10997
10998     /**
10999      * Loads the Record cache from the configured Proxy using the configured Reader.
11000      * <p>
11001      * If using remote paging, then the first load call must specify the <em>start</em>
11002      * and <em>limit</em> properties in the options.params property to establish the initial
11003      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11004      * <p>
11005      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11006      * and this call will return before the new data has been loaded. Perform any post-processing
11007      * in a callback function, or in a "load" event handler.</strong>
11008      * <p>
11009      * @param {Object} options An object containing properties which control loading options:<ul>
11010      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11011      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11012      * passed the following arguments:<ul>
11013      * <li>r : Roo.data.Record[]</li>
11014      * <li>options: Options object from the load call</li>
11015      * <li>success: Boolean success indicator</li></ul></li>
11016      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11017      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11018      * </ul>
11019      */
11020     load : function(options){
11021         options = options || {};
11022         if(this.fireEvent("beforeload", this, options) !== false){
11023             this.storeOptions(options);
11024             var p = Roo.apply(options.params || {}, this.baseParams);
11025             // if meta was not loaded from remote source.. try requesting it.
11026             if (!this.reader.metaFromRemote) {
11027                 p._requestMeta = 1;
11028             }
11029             if(this.sortInfo && this.remoteSort){
11030                 var pn = this.paramNames;
11031                 p[pn["sort"]] = this.sortInfo.field;
11032                 p[pn["dir"]] = this.sortInfo.direction;
11033             }
11034             if (this.multiSort) {
11035                 var pn = this.paramNames;
11036                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11037             }
11038             
11039             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11040         }
11041     },
11042
11043     /**
11044      * Reloads the Record cache from the configured Proxy using the configured Reader and
11045      * the options from the last load operation performed.
11046      * @param {Object} options (optional) An object containing properties which may override the options
11047      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11048      * the most recently used options are reused).
11049      */
11050     reload : function(options){
11051         this.load(Roo.applyIf(options||{}, this.lastOptions));
11052     },
11053
11054     // private
11055     // Called as a callback by the Reader during a load operation.
11056     loadRecords : function(o, options, success){
11057         if(!o || success === false){
11058             if(success !== false){
11059                 this.fireEvent("load", this, [], options, o);
11060             }
11061             if(options.callback){
11062                 options.callback.call(options.scope || this, [], options, false);
11063             }
11064             return;
11065         }
11066         // if data returned failure - throw an exception.
11067         if (o.success === false) {
11068             // show a message if no listener is registered.
11069             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11070                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11071             }
11072             // loadmask wil be hooked into this..
11073             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11074             return;
11075         }
11076         var r = o.records, t = o.totalRecords || r.length;
11077         
11078         this.fireEvent("beforeloadadd", this, r, options, o);
11079         
11080         if(!options || options.add !== true){
11081             if(this.pruneModifiedRecords){
11082                 this.modified = [];
11083             }
11084             for(var i = 0, len = r.length; i < len; i++){
11085                 r[i].join(this);
11086             }
11087             if(this.snapshot){
11088                 this.data = this.snapshot;
11089                 delete this.snapshot;
11090             }
11091             this.data.clear();
11092             this.data.addAll(r);
11093             this.totalLength = t;
11094             this.applySort();
11095             this.fireEvent("datachanged", this);
11096         }else{
11097             this.totalLength = Math.max(t, this.data.length+r.length);
11098             this.add(r);
11099         }
11100         
11101         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11102                 
11103             var e = new Roo.data.Record({});
11104
11105             e.set(this.parent.displayField, this.parent.emptyTitle);
11106             e.set(this.parent.valueField, '');
11107
11108             this.insert(0, e);
11109         }
11110             
11111         this.fireEvent("load", this, r, options, o);
11112         if(options.callback){
11113             options.callback.call(options.scope || this, r, options, true);
11114         }
11115     },
11116
11117
11118     /**
11119      * Loads data from a passed data block. A Reader which understands the format of the data
11120      * must have been configured in the constructor.
11121      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11122      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11123      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11124      */
11125     loadData : function(o, append){
11126         var r = this.reader.readRecords(o);
11127         this.loadRecords(r, {add: append}, true);
11128     },
11129
11130     /**
11131      * Gets the number of cached records.
11132      * <p>
11133      * <em>If using paging, this may not be the total size of the dataset. If the data object
11134      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11135      * the data set size</em>
11136      */
11137     getCount : function(){
11138         return this.data.length || 0;
11139     },
11140
11141     /**
11142      * Gets the total number of records in the dataset as returned by the server.
11143      * <p>
11144      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11145      * the dataset size</em>
11146      */
11147     getTotalCount : function(){
11148         return this.totalLength || 0;
11149     },
11150
11151     /**
11152      * Returns the sort state of the Store as an object with two properties:
11153      * <pre><code>
11154  field {String} The name of the field by which the Records are sorted
11155  direction {String} The sort order, "ASC" or "DESC"
11156      * </code></pre>
11157      */
11158     getSortState : function(){
11159         return this.sortInfo;
11160     },
11161
11162     // private
11163     applySort : function(){
11164         if(this.sortInfo && !this.remoteSort){
11165             var s = this.sortInfo, f = s.field;
11166             var st = this.fields.get(f).sortType;
11167             var fn = function(r1, r2){
11168                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11169                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11170             };
11171             this.data.sort(s.direction, fn);
11172             if(this.snapshot && this.snapshot != this.data){
11173                 this.snapshot.sort(s.direction, fn);
11174             }
11175         }
11176     },
11177
11178     /**
11179      * Sets the default sort column and order to be used by the next load operation.
11180      * @param {String} fieldName The name of the field to sort by.
11181      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11182      */
11183     setDefaultSort : function(field, dir){
11184         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11185     },
11186
11187     /**
11188      * Sort the Records.
11189      * If remote sorting is used, the sort is performed on the server, and the cache is
11190      * reloaded. If local sorting is used, the cache is sorted internally.
11191      * @param {String} fieldName The name of the field to sort by.
11192      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11193      */
11194     sort : function(fieldName, dir){
11195         var f = this.fields.get(fieldName);
11196         if(!dir){
11197             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11198             
11199             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11200                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11201             }else{
11202                 dir = f.sortDir;
11203             }
11204         }
11205         this.sortToggle[f.name] = dir;
11206         this.sortInfo = {field: f.name, direction: dir};
11207         if(!this.remoteSort){
11208             this.applySort();
11209             this.fireEvent("datachanged", this);
11210         }else{
11211             this.load(this.lastOptions);
11212         }
11213     },
11214
11215     /**
11216      * Calls the specified function for each of the Records in the cache.
11217      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11218      * Returning <em>false</em> aborts and exits the iteration.
11219      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11220      */
11221     each : function(fn, scope){
11222         this.data.each(fn, scope);
11223     },
11224
11225     /**
11226      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11227      * (e.g., during paging).
11228      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11229      */
11230     getModifiedRecords : function(){
11231         return this.modified;
11232     },
11233
11234     // private
11235     createFilterFn : function(property, value, anyMatch){
11236         if(!value.exec){ // not a regex
11237             value = String(value);
11238             if(value.length == 0){
11239                 return false;
11240             }
11241             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11242         }
11243         return function(r){
11244             return value.test(r.data[property]);
11245         };
11246     },
11247
11248     /**
11249      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11250      * @param {String} property A field on your records
11251      * @param {Number} start The record index to start at (defaults to 0)
11252      * @param {Number} end The last record index to include (defaults to length - 1)
11253      * @return {Number} The sum
11254      */
11255     sum : function(property, start, end){
11256         var rs = this.data.items, v = 0;
11257         start = start || 0;
11258         end = (end || end === 0) ? end : rs.length-1;
11259
11260         for(var i = start; i <= end; i++){
11261             v += (rs[i].data[property] || 0);
11262         }
11263         return v;
11264     },
11265
11266     /**
11267      * Filter the records by a specified property.
11268      * @param {String} field A field on your records
11269      * @param {String/RegExp} value Either a string that the field
11270      * should start with or a RegExp to test against the field
11271      * @param {Boolean} anyMatch True to match any part not just the beginning
11272      */
11273     filter : function(property, value, anyMatch){
11274         var fn = this.createFilterFn(property, value, anyMatch);
11275         return fn ? this.filterBy(fn) : this.clearFilter();
11276     },
11277
11278     /**
11279      * Filter by a function. The specified function will be called with each
11280      * record in this data source. If the function returns true the record is included,
11281      * otherwise it is filtered.
11282      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11283      * @param {Object} scope (optional) The scope of the function (defaults to this)
11284      */
11285     filterBy : function(fn, scope){
11286         this.snapshot = this.snapshot || this.data;
11287         this.data = this.queryBy(fn, scope||this);
11288         this.fireEvent("datachanged", this);
11289     },
11290
11291     /**
11292      * Query the records by a specified property.
11293      * @param {String} field A field on your records
11294      * @param {String/RegExp} value Either a string that the field
11295      * should start with or a RegExp to test against the field
11296      * @param {Boolean} anyMatch True to match any part not just the beginning
11297      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11298      */
11299     query : function(property, value, anyMatch){
11300         var fn = this.createFilterFn(property, value, anyMatch);
11301         return fn ? this.queryBy(fn) : this.data.clone();
11302     },
11303
11304     /**
11305      * Query by a function. The specified function will be called with each
11306      * record in this data source. If the function returns true the record is included
11307      * in the results.
11308      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11309      * @param {Object} scope (optional) The scope of the function (defaults to this)
11310       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11311      **/
11312     queryBy : function(fn, scope){
11313         var data = this.snapshot || this.data;
11314         return data.filterBy(fn, scope||this);
11315     },
11316
11317     /**
11318      * Collects unique values for a particular dataIndex from this store.
11319      * @param {String} dataIndex The property to collect
11320      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11321      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11322      * @return {Array} An array of the unique values
11323      **/
11324     collect : function(dataIndex, allowNull, bypassFilter){
11325         var d = (bypassFilter === true && this.snapshot) ?
11326                 this.snapshot.items : this.data.items;
11327         var v, sv, r = [], l = {};
11328         for(var i = 0, len = d.length; i < len; i++){
11329             v = d[i].data[dataIndex];
11330             sv = String(v);
11331             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11332                 l[sv] = true;
11333                 r[r.length] = v;
11334             }
11335         }
11336         return r;
11337     },
11338
11339     /**
11340      * Revert to a view of the Record cache with no filtering applied.
11341      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11342      */
11343     clearFilter : function(suppressEvent){
11344         if(this.snapshot && this.snapshot != this.data){
11345             this.data = this.snapshot;
11346             delete this.snapshot;
11347             if(suppressEvent !== true){
11348                 this.fireEvent("datachanged", this);
11349             }
11350         }
11351     },
11352
11353     // private
11354     afterEdit : function(record){
11355         if(this.modified.indexOf(record) == -1){
11356             this.modified.push(record);
11357         }
11358         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11359     },
11360     
11361     // private
11362     afterReject : function(record){
11363         this.modified.remove(record);
11364         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11365     },
11366
11367     // private
11368     afterCommit : function(record){
11369         this.modified.remove(record);
11370         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11371     },
11372
11373     /**
11374      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11375      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11376      */
11377     commitChanges : function(){
11378         var m = this.modified.slice(0);
11379         this.modified = [];
11380         for(var i = 0, len = m.length; i < len; i++){
11381             m[i].commit();
11382         }
11383     },
11384
11385     /**
11386      * Cancel outstanding changes on all changed records.
11387      */
11388     rejectChanges : function(){
11389         var m = this.modified.slice(0);
11390         this.modified = [];
11391         for(var i = 0, len = m.length; i < len; i++){
11392             m[i].reject();
11393         }
11394     },
11395
11396     onMetaChange : function(meta, rtype, o){
11397         this.recordType = rtype;
11398         this.fields = rtype.prototype.fields;
11399         delete this.snapshot;
11400         this.sortInfo = meta.sortInfo || this.sortInfo;
11401         this.modified = [];
11402         this.fireEvent('metachange', this, this.reader.meta);
11403     },
11404     
11405     moveIndex : function(data, type)
11406     {
11407         var index = this.indexOf(data);
11408         
11409         var newIndex = index + type;
11410         
11411         this.remove(data);
11412         
11413         this.insert(newIndex, data);
11414         
11415     }
11416 });/*
11417  * Based on:
11418  * Ext JS Library 1.1.1
11419  * Copyright(c) 2006-2007, Ext JS, LLC.
11420  *
11421  * Originally Released Under LGPL - original licence link has changed is not relivant.
11422  *
11423  * Fork - LGPL
11424  * <script type="text/javascript">
11425  */
11426
11427 /**
11428  * @class Roo.data.SimpleStore
11429  * @extends Roo.data.Store
11430  * Small helper class to make creating Stores from Array data easier.
11431  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11432  * @cfg {Array} fields An array of field definition objects, or field name strings.
11433  * @cfg {Array} data The multi-dimensional array of data
11434  * @constructor
11435  * @param {Object} config
11436  */
11437 Roo.data.SimpleStore = function(config){
11438     Roo.data.SimpleStore.superclass.constructor.call(this, {
11439         isLocal : true,
11440         reader: new Roo.data.ArrayReader({
11441                 id: config.id
11442             },
11443             Roo.data.Record.create(config.fields)
11444         ),
11445         proxy : new Roo.data.MemoryProxy(config.data)
11446     });
11447     this.load();
11448 };
11449 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11450  * Based on:
11451  * Ext JS Library 1.1.1
11452  * Copyright(c) 2006-2007, Ext JS, LLC.
11453  *
11454  * Originally Released Under LGPL - original licence link has changed is not relivant.
11455  *
11456  * Fork - LGPL
11457  * <script type="text/javascript">
11458  */
11459
11460 /**
11461 /**
11462  * @extends Roo.data.Store
11463  * @class Roo.data.JsonStore
11464  * Small helper class to make creating Stores for JSON data easier. <br/>
11465 <pre><code>
11466 var store = new Roo.data.JsonStore({
11467     url: 'get-images.php',
11468     root: 'images',
11469     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11470 });
11471 </code></pre>
11472  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11473  * JsonReader and HttpProxy (unless inline data is provided).</b>
11474  * @cfg {Array} fields An array of field definition objects, or field name strings.
11475  * @constructor
11476  * @param {Object} config
11477  */
11478 Roo.data.JsonStore = function(c){
11479     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11480         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11481         reader: new Roo.data.JsonReader(c, c.fields)
11482     }));
11483 };
11484 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11485  * Based on:
11486  * Ext JS Library 1.1.1
11487  * Copyright(c) 2006-2007, Ext JS, LLC.
11488  *
11489  * Originally Released Under LGPL - original licence link has changed is not relivant.
11490  *
11491  * Fork - LGPL
11492  * <script type="text/javascript">
11493  */
11494
11495  
11496 Roo.data.Field = function(config){
11497     if(typeof config == "string"){
11498         config = {name: config};
11499     }
11500     Roo.apply(this, config);
11501     
11502     if(!this.type){
11503         this.type = "auto";
11504     }
11505     
11506     var st = Roo.data.SortTypes;
11507     // named sortTypes are supported, here we look them up
11508     if(typeof this.sortType == "string"){
11509         this.sortType = st[this.sortType];
11510     }
11511     
11512     // set default sortType for strings and dates
11513     if(!this.sortType){
11514         switch(this.type){
11515             case "string":
11516                 this.sortType = st.asUCString;
11517                 break;
11518             case "date":
11519                 this.sortType = st.asDate;
11520                 break;
11521             default:
11522                 this.sortType = st.none;
11523         }
11524     }
11525
11526     // define once
11527     var stripRe = /[\$,%]/g;
11528
11529     // prebuilt conversion function for this field, instead of
11530     // switching every time we're reading a value
11531     if(!this.convert){
11532         var cv, dateFormat = this.dateFormat;
11533         switch(this.type){
11534             case "":
11535             case "auto":
11536             case undefined:
11537                 cv = function(v){ return v; };
11538                 break;
11539             case "string":
11540                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11541                 break;
11542             case "int":
11543                 cv = function(v){
11544                     return v !== undefined && v !== null && v !== '' ?
11545                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11546                     };
11547                 break;
11548             case "float":
11549                 cv = function(v){
11550                     return v !== undefined && v !== null && v !== '' ?
11551                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11552                     };
11553                 break;
11554             case "bool":
11555             case "boolean":
11556                 cv = function(v){ return v === true || v === "true" || v == 1; };
11557                 break;
11558             case "date":
11559                 cv = function(v){
11560                     if(!v){
11561                         return '';
11562                     }
11563                     if(v instanceof Date){
11564                         return v;
11565                     }
11566                     if(dateFormat){
11567                         if(dateFormat == "timestamp"){
11568                             return new Date(v*1000);
11569                         }
11570                         return Date.parseDate(v, dateFormat);
11571                     }
11572                     var parsed = Date.parse(v);
11573                     return parsed ? new Date(parsed) : null;
11574                 };
11575              break;
11576             
11577         }
11578         this.convert = cv;
11579     }
11580 };
11581
11582 Roo.data.Field.prototype = {
11583     dateFormat: null,
11584     defaultValue: "",
11585     mapping: null,
11586     sortType : null,
11587     sortDir : "ASC"
11588 };/*
11589  * Based on:
11590  * Ext JS Library 1.1.1
11591  * Copyright(c) 2006-2007, Ext JS, LLC.
11592  *
11593  * Originally Released Under LGPL - original licence link has changed is not relivant.
11594  *
11595  * Fork - LGPL
11596  * <script type="text/javascript">
11597  */
11598  
11599 // Base class for reading structured data from a data source.  This class is intended to be
11600 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11601
11602 /**
11603  * @class Roo.data.DataReader
11604  * Base class for reading structured data from a data source.  This class is intended to be
11605  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11606  */
11607
11608 Roo.data.DataReader = function(meta, recordType){
11609     
11610     this.meta = meta;
11611     
11612     this.recordType = recordType instanceof Array ? 
11613         Roo.data.Record.create(recordType) : recordType;
11614 };
11615
11616 Roo.data.DataReader.prototype = {
11617      /**
11618      * Create an empty record
11619      * @param {Object} data (optional) - overlay some values
11620      * @return {Roo.data.Record} record created.
11621      */
11622     newRow :  function(d) {
11623         var da =  {};
11624         this.recordType.prototype.fields.each(function(c) {
11625             switch( c.type) {
11626                 case 'int' : da[c.name] = 0; break;
11627                 case 'date' : da[c.name] = new Date(); break;
11628                 case 'float' : da[c.name] = 0.0; break;
11629                 case 'boolean' : da[c.name] = false; break;
11630                 default : da[c.name] = ""; break;
11631             }
11632             
11633         });
11634         return new this.recordType(Roo.apply(da, d));
11635     }
11636     
11637 };/*
11638  * Based on:
11639  * Ext JS Library 1.1.1
11640  * Copyright(c) 2006-2007, Ext JS, LLC.
11641  *
11642  * Originally Released Under LGPL - original licence link has changed is not relivant.
11643  *
11644  * Fork - LGPL
11645  * <script type="text/javascript">
11646  */
11647
11648 /**
11649  * @class Roo.data.DataProxy
11650  * @extends Roo.data.Observable
11651  * This class is an abstract base class for implementations which provide retrieval of
11652  * unformatted data objects.<br>
11653  * <p>
11654  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11655  * (of the appropriate type which knows how to parse the data object) to provide a block of
11656  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11657  * <p>
11658  * Custom implementations must implement the load method as described in
11659  * {@link Roo.data.HttpProxy#load}.
11660  */
11661 Roo.data.DataProxy = function(){
11662     this.addEvents({
11663         /**
11664          * @event beforeload
11665          * Fires before a network request is made to retrieve a data object.
11666          * @param {Object} This DataProxy object.
11667          * @param {Object} params The params parameter to the load function.
11668          */
11669         beforeload : true,
11670         /**
11671          * @event load
11672          * Fires before the load method's callback is called.
11673          * @param {Object} This DataProxy object.
11674          * @param {Object} o The data object.
11675          * @param {Object} arg The callback argument object passed to the load function.
11676          */
11677         load : true,
11678         /**
11679          * @event loadexception
11680          * Fires if an Exception occurs during data retrieval.
11681          * @param {Object} This DataProxy object.
11682          * @param {Object} o The data object.
11683          * @param {Object} arg The callback argument object passed to the load function.
11684          * @param {Object} e The Exception.
11685          */
11686         loadexception : true
11687     });
11688     Roo.data.DataProxy.superclass.constructor.call(this);
11689 };
11690
11691 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11692
11693     /**
11694      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11695      */
11696 /*
11697  * Based on:
11698  * Ext JS Library 1.1.1
11699  * Copyright(c) 2006-2007, Ext JS, LLC.
11700  *
11701  * Originally Released Under LGPL - original licence link has changed is not relivant.
11702  *
11703  * Fork - LGPL
11704  * <script type="text/javascript">
11705  */
11706 /**
11707  * @class Roo.data.MemoryProxy
11708  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11709  * to the Reader when its load method is called.
11710  * @constructor
11711  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11712  */
11713 Roo.data.MemoryProxy = function(data){
11714     if (data.data) {
11715         data = data.data;
11716     }
11717     Roo.data.MemoryProxy.superclass.constructor.call(this);
11718     this.data = data;
11719 };
11720
11721 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11722     
11723     /**
11724      * Load data from the requested source (in this case an in-memory
11725      * data object passed to the constructor), read the data object into
11726      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11727      * process that block using the passed callback.
11728      * @param {Object} params This parameter is not used by the MemoryProxy class.
11729      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11730      * object into a block of Roo.data.Records.
11731      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11732      * The function must be passed <ul>
11733      * <li>The Record block object</li>
11734      * <li>The "arg" argument from the load function</li>
11735      * <li>A boolean success indicator</li>
11736      * </ul>
11737      * @param {Object} scope The scope in which to call the callback
11738      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11739      */
11740     load : function(params, reader, callback, scope, arg){
11741         params = params || {};
11742         var result;
11743         try {
11744             result = reader.readRecords(this.data);
11745         }catch(e){
11746             this.fireEvent("loadexception", this, arg, null, e);
11747             callback.call(scope, null, arg, false);
11748             return;
11749         }
11750         callback.call(scope, result, arg, true);
11751     },
11752     
11753     // private
11754     update : function(params, records){
11755         
11756     }
11757 });/*
11758  * Based on:
11759  * Ext JS Library 1.1.1
11760  * Copyright(c) 2006-2007, Ext JS, LLC.
11761  *
11762  * Originally Released Under LGPL - original licence link has changed is not relivant.
11763  *
11764  * Fork - LGPL
11765  * <script type="text/javascript">
11766  */
11767 /**
11768  * @class Roo.data.HttpProxy
11769  * @extends Roo.data.DataProxy
11770  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11771  * configured to reference a certain URL.<br><br>
11772  * <p>
11773  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11774  * from which the running page was served.<br><br>
11775  * <p>
11776  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11777  * <p>
11778  * Be aware that to enable the browser to parse an XML document, the server must set
11779  * the Content-Type header in the HTTP response to "text/xml".
11780  * @constructor
11781  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11782  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11783  * will be used to make the request.
11784  */
11785 Roo.data.HttpProxy = function(conn){
11786     Roo.data.HttpProxy.superclass.constructor.call(this);
11787     // is conn a conn config or a real conn?
11788     this.conn = conn;
11789     this.useAjax = !conn || !conn.events;
11790   
11791 };
11792
11793 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11794     // thse are take from connection...
11795     
11796     /**
11797      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11798      */
11799     /**
11800      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11801      * extra parameters to each request made by this object. (defaults to undefined)
11802      */
11803     /**
11804      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11805      *  to each request made by this object. (defaults to undefined)
11806      */
11807     /**
11808      * @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)
11809      */
11810     /**
11811      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11812      */
11813      /**
11814      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11815      * @type Boolean
11816      */
11817   
11818
11819     /**
11820      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11821      * @type Boolean
11822      */
11823     /**
11824      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11825      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11826      * a finer-grained basis than the DataProxy events.
11827      */
11828     getConnection : function(){
11829         return this.useAjax ? Roo.Ajax : this.conn;
11830     },
11831
11832     /**
11833      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11834      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11835      * process that block using the passed callback.
11836      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11837      * for the request to the remote server.
11838      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11839      * object into a block of Roo.data.Records.
11840      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11841      * The function must be passed <ul>
11842      * <li>The Record block object</li>
11843      * <li>The "arg" argument from the load function</li>
11844      * <li>A boolean success indicator</li>
11845      * </ul>
11846      * @param {Object} scope The scope in which to call the callback
11847      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11848      */
11849     load : function(params, reader, callback, scope, arg){
11850         if(this.fireEvent("beforeload", this, params) !== false){
11851             var  o = {
11852                 params : params || {},
11853                 request: {
11854                     callback : callback,
11855                     scope : scope,
11856                     arg : arg
11857                 },
11858                 reader: reader,
11859                 callback : this.loadResponse,
11860                 scope: this
11861             };
11862             if(this.useAjax){
11863                 Roo.applyIf(o, this.conn);
11864                 if(this.activeRequest){
11865                     Roo.Ajax.abort(this.activeRequest);
11866                 }
11867                 this.activeRequest = Roo.Ajax.request(o);
11868             }else{
11869                 this.conn.request(o);
11870             }
11871         }else{
11872             callback.call(scope||this, null, arg, false);
11873         }
11874     },
11875
11876     // private
11877     loadResponse : function(o, success, response){
11878         delete this.activeRequest;
11879         if(!success){
11880             this.fireEvent("loadexception", this, o, response);
11881             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11882             return;
11883         }
11884         var result;
11885         try {
11886             result = o.reader.read(response);
11887         }catch(e){
11888             this.fireEvent("loadexception", this, o, response, e);
11889             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11890             return;
11891         }
11892         
11893         this.fireEvent("load", this, o, o.request.arg);
11894         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11895     },
11896
11897     // private
11898     update : function(dataSet){
11899
11900     },
11901
11902     // private
11903     updateResponse : function(dataSet){
11904
11905     }
11906 });/*
11907  * Based on:
11908  * Ext JS Library 1.1.1
11909  * Copyright(c) 2006-2007, Ext JS, LLC.
11910  *
11911  * Originally Released Under LGPL - original licence link has changed is not relivant.
11912  *
11913  * Fork - LGPL
11914  * <script type="text/javascript">
11915  */
11916
11917 /**
11918  * @class Roo.data.ScriptTagProxy
11919  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11920  * other than the originating domain of the running page.<br><br>
11921  * <p>
11922  * <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
11923  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11924  * <p>
11925  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11926  * source code that is used as the source inside a &lt;script> tag.<br><br>
11927  * <p>
11928  * In order for the browser to process the returned data, the server must wrap the data object
11929  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11930  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11931  * depending on whether the callback name was passed:
11932  * <p>
11933  * <pre><code>
11934 boolean scriptTag = false;
11935 String cb = request.getParameter("callback");
11936 if (cb != null) {
11937     scriptTag = true;
11938     response.setContentType("text/javascript");
11939 } else {
11940     response.setContentType("application/x-json");
11941 }
11942 Writer out = response.getWriter();
11943 if (scriptTag) {
11944     out.write(cb + "(");
11945 }
11946 out.print(dataBlock.toJsonString());
11947 if (scriptTag) {
11948     out.write(");");
11949 }
11950 </pre></code>
11951  *
11952  * @constructor
11953  * @param {Object} config A configuration object.
11954  */
11955 Roo.data.ScriptTagProxy = function(config){
11956     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11957     Roo.apply(this, config);
11958     this.head = document.getElementsByTagName("head")[0];
11959 };
11960
11961 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11962
11963 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11964     /**
11965      * @cfg {String} url The URL from which to request the data object.
11966      */
11967     /**
11968      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11969      */
11970     timeout : 30000,
11971     /**
11972      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11973      * the server the name of the callback function set up by the load call to process the returned data object.
11974      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11975      * javascript output which calls this named function passing the data object as its only parameter.
11976      */
11977     callbackParam : "callback",
11978     /**
11979      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11980      * name to the request.
11981      */
11982     nocache : true,
11983
11984     /**
11985      * Load data from the configured URL, read the data object into
11986      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11987      * process that block using the passed callback.
11988      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11989      * for the request to the remote server.
11990      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11991      * object into a block of Roo.data.Records.
11992      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11993      * The function must be passed <ul>
11994      * <li>The Record block object</li>
11995      * <li>The "arg" argument from the load function</li>
11996      * <li>A boolean success indicator</li>
11997      * </ul>
11998      * @param {Object} scope The scope in which to call the callback
11999      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12000      */
12001     load : function(params, reader, callback, scope, arg){
12002         if(this.fireEvent("beforeload", this, params) !== false){
12003
12004             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12005
12006             var url = this.url;
12007             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12008             if(this.nocache){
12009                 url += "&_dc=" + (new Date().getTime());
12010             }
12011             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12012             var trans = {
12013                 id : transId,
12014                 cb : "stcCallback"+transId,
12015                 scriptId : "stcScript"+transId,
12016                 params : params,
12017                 arg : arg,
12018                 url : url,
12019                 callback : callback,
12020                 scope : scope,
12021                 reader : reader
12022             };
12023             var conn = this;
12024
12025             window[trans.cb] = function(o){
12026                 conn.handleResponse(o, trans);
12027             };
12028
12029             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12030
12031             if(this.autoAbort !== false){
12032                 this.abort();
12033             }
12034
12035             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12036
12037             var script = document.createElement("script");
12038             script.setAttribute("src", url);
12039             script.setAttribute("type", "text/javascript");
12040             script.setAttribute("id", trans.scriptId);
12041             this.head.appendChild(script);
12042
12043             this.trans = trans;
12044         }else{
12045             callback.call(scope||this, null, arg, false);
12046         }
12047     },
12048
12049     // private
12050     isLoading : function(){
12051         return this.trans ? true : false;
12052     },
12053
12054     /**
12055      * Abort the current server request.
12056      */
12057     abort : function(){
12058         if(this.isLoading()){
12059             this.destroyTrans(this.trans);
12060         }
12061     },
12062
12063     // private
12064     destroyTrans : function(trans, isLoaded){
12065         this.head.removeChild(document.getElementById(trans.scriptId));
12066         clearTimeout(trans.timeoutId);
12067         if(isLoaded){
12068             window[trans.cb] = undefined;
12069             try{
12070                 delete window[trans.cb];
12071             }catch(e){}
12072         }else{
12073             // if hasn't been loaded, wait for load to remove it to prevent script error
12074             window[trans.cb] = function(){
12075                 window[trans.cb] = undefined;
12076                 try{
12077                     delete window[trans.cb];
12078                 }catch(e){}
12079             };
12080         }
12081     },
12082
12083     // private
12084     handleResponse : function(o, trans){
12085         this.trans = false;
12086         this.destroyTrans(trans, true);
12087         var result;
12088         try {
12089             result = trans.reader.readRecords(o);
12090         }catch(e){
12091             this.fireEvent("loadexception", this, o, trans.arg, e);
12092             trans.callback.call(trans.scope||window, null, trans.arg, false);
12093             return;
12094         }
12095         this.fireEvent("load", this, o, trans.arg);
12096         trans.callback.call(trans.scope||window, result, trans.arg, true);
12097     },
12098
12099     // private
12100     handleFailure : function(trans){
12101         this.trans = false;
12102         this.destroyTrans(trans, false);
12103         this.fireEvent("loadexception", this, null, trans.arg);
12104         trans.callback.call(trans.scope||window, null, trans.arg, false);
12105     }
12106 });/*
12107  * Based on:
12108  * Ext JS Library 1.1.1
12109  * Copyright(c) 2006-2007, Ext JS, LLC.
12110  *
12111  * Originally Released Under LGPL - original licence link has changed is not relivant.
12112  *
12113  * Fork - LGPL
12114  * <script type="text/javascript">
12115  */
12116
12117 /**
12118  * @class Roo.data.JsonReader
12119  * @extends Roo.data.DataReader
12120  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12121  * based on mappings in a provided Roo.data.Record constructor.
12122  * 
12123  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12124  * in the reply previously. 
12125  * 
12126  * <p>
12127  * Example code:
12128  * <pre><code>
12129 var RecordDef = Roo.data.Record.create([
12130     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12131     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12132 ]);
12133 var myReader = new Roo.data.JsonReader({
12134     totalProperty: "results",    // The property which contains the total dataset size (optional)
12135     root: "rows",                // The property which contains an Array of row objects
12136     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12137 }, RecordDef);
12138 </code></pre>
12139  * <p>
12140  * This would consume a JSON file like this:
12141  * <pre><code>
12142 { 'results': 2, 'rows': [
12143     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12144     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12145 }
12146 </code></pre>
12147  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12148  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12149  * paged from the remote server.
12150  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12151  * @cfg {String} root name of the property which contains the Array of row objects.
12152  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12153  * @cfg {Array} fields Array of field definition objects
12154  * @constructor
12155  * Create a new JsonReader
12156  * @param {Object} meta Metadata configuration options
12157  * @param {Object} recordType Either an Array of field definition objects,
12158  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12159  */
12160 Roo.data.JsonReader = function(meta, recordType){
12161     
12162     meta = meta || {};
12163     // set some defaults:
12164     Roo.applyIf(meta, {
12165         totalProperty: 'total',
12166         successProperty : 'success',
12167         root : 'data',
12168         id : 'id'
12169     });
12170     
12171     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12172 };
12173 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12174     
12175     /**
12176      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12177      * Used by Store query builder to append _requestMeta to params.
12178      * 
12179      */
12180     metaFromRemote : false,
12181     /**
12182      * This method is only used by a DataProxy which has retrieved data from a remote server.
12183      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12184      * @return {Object} data A data block which is used by an Roo.data.Store object as
12185      * a cache of Roo.data.Records.
12186      */
12187     read : function(response){
12188         var json = response.responseText;
12189        
12190         var o = /* eval:var:o */ eval("("+json+")");
12191         if(!o) {
12192             throw {message: "JsonReader.read: Json object not found"};
12193         }
12194         
12195         if(o.metaData){
12196             
12197             delete this.ef;
12198             this.metaFromRemote = true;
12199             this.meta = o.metaData;
12200             this.recordType = Roo.data.Record.create(o.metaData.fields);
12201             this.onMetaChange(this.meta, this.recordType, o);
12202         }
12203         return this.readRecords(o);
12204     },
12205
12206     // private function a store will implement
12207     onMetaChange : function(meta, recordType, o){
12208
12209     },
12210
12211     /**
12212          * @ignore
12213          */
12214     simpleAccess: function(obj, subsc) {
12215         return obj[subsc];
12216     },
12217
12218         /**
12219          * @ignore
12220          */
12221     getJsonAccessor: function(){
12222         var re = /[\[\.]/;
12223         return function(expr) {
12224             try {
12225                 return(re.test(expr))
12226                     ? new Function("obj", "return obj." + expr)
12227                     : function(obj){
12228                         return obj[expr];
12229                     };
12230             } catch(e){}
12231             return Roo.emptyFn;
12232         };
12233     }(),
12234
12235     /**
12236      * Create a data block containing Roo.data.Records from an XML document.
12237      * @param {Object} o An object which contains an Array of row objects in the property specified
12238      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12239      * which contains the total size of the dataset.
12240      * @return {Object} data A data block which is used by an Roo.data.Store object as
12241      * a cache of Roo.data.Records.
12242      */
12243     readRecords : function(o){
12244         /**
12245          * After any data loads, the raw JSON data is available for further custom processing.
12246          * @type Object
12247          */
12248         this.o = o;
12249         var s = this.meta, Record = this.recordType,
12250             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12251
12252 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12253         if (!this.ef) {
12254             if(s.totalProperty) {
12255                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12256                 }
12257                 if(s.successProperty) {
12258                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12259                 }
12260                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12261                 if (s.id) {
12262                         var g = this.getJsonAccessor(s.id);
12263                         this.getId = function(rec) {
12264                                 var r = g(rec);  
12265                                 return (r === undefined || r === "") ? null : r;
12266                         };
12267                 } else {
12268                         this.getId = function(){return null;};
12269                 }
12270             this.ef = [];
12271             for(var jj = 0; jj < fl; jj++){
12272                 f = fi[jj];
12273                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12274                 this.ef[jj] = this.getJsonAccessor(map);
12275             }
12276         }
12277
12278         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12279         if(s.totalProperty){
12280             var vt = parseInt(this.getTotal(o), 10);
12281             if(!isNaN(vt)){
12282                 totalRecords = vt;
12283             }
12284         }
12285         if(s.successProperty){
12286             var vs = this.getSuccess(o);
12287             if(vs === false || vs === 'false'){
12288                 success = false;
12289             }
12290         }
12291         var records = [];
12292         for(var i = 0; i < c; i++){
12293                 var n = root[i];
12294             var values = {};
12295             var id = this.getId(n);
12296             for(var j = 0; j < fl; j++){
12297                 f = fi[j];
12298             var v = this.ef[j](n);
12299             if (!f.convert) {
12300                 Roo.log('missing convert for ' + f.name);
12301                 Roo.log(f);
12302                 continue;
12303             }
12304             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12305             }
12306             var record = new Record(values, id);
12307             record.json = n;
12308             records[i] = record;
12309         }
12310         return {
12311             raw : o,
12312             success : success,
12313             records : records,
12314             totalRecords : totalRecords
12315         };
12316     }
12317 });/*
12318  * Based on:
12319  * Ext JS Library 1.1.1
12320  * Copyright(c) 2006-2007, Ext JS, LLC.
12321  *
12322  * Originally Released Under LGPL - original licence link has changed is not relivant.
12323  *
12324  * Fork - LGPL
12325  * <script type="text/javascript">
12326  */
12327
12328 /**
12329  * @class Roo.data.ArrayReader
12330  * @extends Roo.data.DataReader
12331  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12332  * Each element of that Array represents a row of data fields. The
12333  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12334  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12335  * <p>
12336  * Example code:.
12337  * <pre><code>
12338 var RecordDef = Roo.data.Record.create([
12339     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12340     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12341 ]);
12342 var myReader = new Roo.data.ArrayReader({
12343     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12344 }, RecordDef);
12345 </code></pre>
12346  * <p>
12347  * This would consume an Array like this:
12348  * <pre><code>
12349 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12350   </code></pre>
12351  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12352  * @constructor
12353  * Create a new JsonReader
12354  * @param {Object} meta Metadata configuration options.
12355  * @param {Object} recordType Either an Array of field definition objects
12356  * as specified to {@link Roo.data.Record#create},
12357  * or an {@link Roo.data.Record} object
12358  * created using {@link Roo.data.Record#create}.
12359  */
12360 Roo.data.ArrayReader = function(meta, recordType){
12361     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12362 };
12363
12364 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12365     /**
12366      * Create a data block containing Roo.data.Records from an XML document.
12367      * @param {Object} o An Array of row objects which represents the dataset.
12368      * @return {Object} data A data block which is used by an Roo.data.Store object as
12369      * a cache of Roo.data.Records.
12370      */
12371     readRecords : function(o){
12372         var sid = this.meta ? this.meta.id : null;
12373         var recordType = this.recordType, fields = recordType.prototype.fields;
12374         var records = [];
12375         var root = o;
12376             for(var i = 0; i < root.length; i++){
12377                     var n = root[i];
12378                 var values = {};
12379                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12380                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12381                 var f = fields.items[j];
12382                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12383                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12384                 v = f.convert(v);
12385                 values[f.name] = v;
12386             }
12387                 var record = new recordType(values, id);
12388                 record.json = n;
12389                 records[records.length] = record;
12390             }
12391             return {
12392                 records : records,
12393                 totalRecords : records.length
12394             };
12395     }
12396 });/*
12397  * - LGPL
12398  * * 
12399  */
12400
12401 /**
12402  * @class Roo.bootstrap.ComboBox
12403  * @extends Roo.bootstrap.TriggerField
12404  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12405  * @cfg {Boolean} append (true|false) default false
12406  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12407  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12408  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12409  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12410  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12411  * @cfg {Boolean} animate default true
12412  * @cfg {Boolean} emptyResultText only for touch device
12413  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12414  * @cfg {String} emptyTitle default ''
12415  * @constructor
12416  * Create a new ComboBox.
12417  * @param {Object} config Configuration options
12418  */
12419 Roo.bootstrap.ComboBox = function(config){
12420     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12421     this.addEvents({
12422         /**
12423          * @event expand
12424          * Fires when the dropdown list is expanded
12425         * @param {Roo.bootstrap.ComboBox} combo This combo box
12426         */
12427         'expand' : true,
12428         /**
12429          * @event collapse
12430          * Fires when the dropdown list is collapsed
12431         * @param {Roo.bootstrap.ComboBox} combo This combo box
12432         */
12433         'collapse' : true,
12434         /**
12435          * @event beforeselect
12436          * Fires before a list item is selected. Return false to cancel the selection.
12437         * @param {Roo.bootstrap.ComboBox} combo This combo box
12438         * @param {Roo.data.Record} record The data record returned from the underlying store
12439         * @param {Number} index The index of the selected item in the dropdown list
12440         */
12441         'beforeselect' : true,
12442         /**
12443          * @event select
12444          * Fires when a list item is selected
12445         * @param {Roo.bootstrap.ComboBox} combo This combo box
12446         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12447         * @param {Number} index The index of the selected item in the dropdown list
12448         */
12449         'select' : true,
12450         /**
12451          * @event beforequery
12452          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12453          * The event object passed has these properties:
12454         * @param {Roo.bootstrap.ComboBox} combo This combo box
12455         * @param {String} query The query
12456         * @param {Boolean} forceAll true to force "all" query
12457         * @param {Boolean} cancel true to cancel the query
12458         * @param {Object} e The query event object
12459         */
12460         'beforequery': true,
12461          /**
12462          * @event add
12463          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12464         * @param {Roo.bootstrap.ComboBox} combo This combo box
12465         */
12466         'add' : true,
12467         /**
12468          * @event edit
12469          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12470         * @param {Roo.bootstrap.ComboBox} combo This combo box
12471         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12472         */
12473         'edit' : true,
12474         /**
12475          * @event remove
12476          * Fires when the remove value from the combobox array
12477         * @param {Roo.bootstrap.ComboBox} combo This combo box
12478         */
12479         'remove' : true,
12480         /**
12481          * @event afterremove
12482          * Fires when the remove value from the combobox array
12483         * @param {Roo.bootstrap.ComboBox} combo This combo box
12484         */
12485         'afterremove' : true,
12486         /**
12487          * @event specialfilter
12488          * Fires when specialfilter
12489             * @param {Roo.bootstrap.ComboBox} combo This combo box
12490             */
12491         'specialfilter' : true,
12492         /**
12493          * @event tick
12494          * Fires when tick the element
12495             * @param {Roo.bootstrap.ComboBox} combo This combo box
12496             */
12497         'tick' : true,
12498         /**
12499          * @event touchviewdisplay
12500          * Fires when touch view require special display (default is using displayField)
12501             * @param {Roo.bootstrap.ComboBox} combo This combo box
12502             * @param {Object} cfg set html .
12503             */
12504         'touchviewdisplay' : true
12505         
12506     });
12507     
12508     this.item = [];
12509     this.tickItems = [];
12510     
12511     this.selectedIndex = -1;
12512     if(this.mode == 'local'){
12513         if(config.queryDelay === undefined){
12514             this.queryDelay = 10;
12515         }
12516         if(config.minChars === undefined){
12517             this.minChars = 0;
12518         }
12519     }
12520 };
12521
12522 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12523      
12524     /**
12525      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12526      * rendering into an Roo.Editor, defaults to false)
12527      */
12528     /**
12529      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12530      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12531      */
12532     /**
12533      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12534      */
12535     /**
12536      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12537      * the dropdown list (defaults to undefined, with no header element)
12538      */
12539
12540      /**
12541      * @cfg {String/Roo.Template} tpl The template to use to render the output
12542      */
12543      
12544      /**
12545      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12546      */
12547     listWidth: undefined,
12548     /**
12549      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12550      * mode = 'remote' or 'text' if mode = 'local')
12551      */
12552     displayField: undefined,
12553     
12554     /**
12555      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12556      * mode = 'remote' or 'value' if mode = 'local'). 
12557      * Note: use of a valueField requires the user make a selection
12558      * in order for a value to be mapped.
12559      */
12560     valueField: undefined,
12561     /**
12562      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12563      */
12564     modalTitle : '',
12565     
12566     /**
12567      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12568      * field's data value (defaults to the underlying DOM element's name)
12569      */
12570     hiddenName: undefined,
12571     /**
12572      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12573      */
12574     listClass: '',
12575     /**
12576      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12577      */
12578     selectedClass: 'active',
12579     
12580     /**
12581      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12582      */
12583     shadow:'sides',
12584     /**
12585      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12586      * anchor positions (defaults to 'tl-bl')
12587      */
12588     listAlign: 'tl-bl?',
12589     /**
12590      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12591      */
12592     maxHeight: 300,
12593     /**
12594      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12595      * query specified by the allQuery config option (defaults to 'query')
12596      */
12597     triggerAction: 'query',
12598     /**
12599      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12600      * (defaults to 4, does not apply if editable = false)
12601      */
12602     minChars : 4,
12603     /**
12604      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12605      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12606      */
12607     typeAhead: false,
12608     /**
12609      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12610      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12611      */
12612     queryDelay: 500,
12613     /**
12614      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12615      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12616      */
12617     pageSize: 0,
12618     /**
12619      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12620      * when editable = true (defaults to false)
12621      */
12622     selectOnFocus:false,
12623     /**
12624      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12625      */
12626     queryParam: 'query',
12627     /**
12628      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12629      * when mode = 'remote' (defaults to 'Loading...')
12630      */
12631     loadingText: 'Loading...',
12632     /**
12633      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12634      */
12635     resizable: false,
12636     /**
12637      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12638      */
12639     handleHeight : 8,
12640     /**
12641      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12642      * traditional select (defaults to true)
12643      */
12644     editable: true,
12645     /**
12646      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12647      */
12648     allQuery: '',
12649     /**
12650      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12651      */
12652     mode: 'remote',
12653     /**
12654      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12655      * listWidth has a higher value)
12656      */
12657     minListWidth : 70,
12658     /**
12659      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12660      * allow the user to set arbitrary text into the field (defaults to false)
12661      */
12662     forceSelection:false,
12663     /**
12664      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12665      * if typeAhead = true (defaults to 250)
12666      */
12667     typeAheadDelay : 250,
12668     /**
12669      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12670      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12671      */
12672     valueNotFoundText : undefined,
12673     /**
12674      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12675      */
12676     blockFocus : false,
12677     
12678     /**
12679      * @cfg {Boolean} disableClear Disable showing of clear button.
12680      */
12681     disableClear : false,
12682     /**
12683      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12684      */
12685     alwaysQuery : false,
12686     
12687     /**
12688      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12689      */
12690     multiple : false,
12691     
12692     /**
12693      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12694      */
12695     invalidClass : "has-warning",
12696     
12697     /**
12698      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12699      */
12700     validClass : "has-success",
12701     
12702     /**
12703      * @cfg {Boolean} specialFilter (true|false) special filter default false
12704      */
12705     specialFilter : false,
12706     
12707     /**
12708      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12709      */
12710     mobileTouchView : true,
12711     
12712     /**
12713      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12714      */
12715     useNativeIOS : false,
12716     
12717     ios_options : false,
12718     
12719     //private
12720     addicon : false,
12721     editicon: false,
12722     
12723     page: 0,
12724     hasQuery: false,
12725     append: false,
12726     loadNext: false,
12727     autoFocus : true,
12728     tickable : false,
12729     btnPosition : 'right',
12730     triggerList : true,
12731     showToggleBtn : true,
12732     animate : true,
12733     emptyResultText: 'Empty',
12734     triggerText : 'Select',
12735     emptyTitle : '',
12736     
12737     // element that contains real text value.. (when hidden is used..)
12738     
12739     getAutoCreate : function()
12740     {   
12741         var cfg = false;
12742         //render
12743         /*
12744          * Render classic select for iso
12745          */
12746         
12747         if(Roo.isIOS && this.useNativeIOS){
12748             cfg = this.getAutoCreateNativeIOS();
12749             return cfg;
12750         }
12751         
12752         /*
12753          * Touch Devices
12754          */
12755         
12756         if(Roo.isTouch && this.mobileTouchView){
12757             cfg = this.getAutoCreateTouchView();
12758             return cfg;;
12759         }
12760         
12761         /*
12762          *  Normal ComboBox
12763          */
12764         if(!this.tickable){
12765             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12766             return cfg;
12767         }
12768         
12769         /*
12770          *  ComboBox with tickable selections
12771          */
12772              
12773         var align = this.labelAlign || this.parentLabelAlign();
12774         
12775         cfg = {
12776             cls : 'form-group roo-combobox-tickable' //input-group
12777         };
12778         
12779         var btn_text_select = '';
12780         var btn_text_done = '';
12781         var btn_text_cancel = '';
12782         
12783         if (this.btn_text_show) {
12784             btn_text_select = 'Select';
12785             btn_text_done = 'Done';
12786             btn_text_cancel = 'Cancel'; 
12787         }
12788         
12789         var buttons = {
12790             tag : 'div',
12791             cls : 'tickable-buttons',
12792             cn : [
12793                 {
12794                     tag : 'button',
12795                     type : 'button',
12796                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12797                     //html : this.triggerText
12798                     html: btn_text_select
12799                 },
12800                 {
12801                     tag : 'button',
12802                     type : 'button',
12803                     name : 'ok',
12804                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12805                     //html : 'Done'
12806                     html: btn_text_done
12807                 },
12808                 {
12809                     tag : 'button',
12810                     type : 'button',
12811                     name : 'cancel',
12812                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12813                     //html : 'Cancel'
12814                     html: btn_text_cancel
12815                 }
12816             ]
12817         };
12818         
12819         if(this.editable){
12820             buttons.cn.unshift({
12821                 tag: 'input',
12822                 cls: 'roo-select2-search-field-input'
12823             });
12824         }
12825         
12826         var _this = this;
12827         
12828         Roo.each(buttons.cn, function(c){
12829             if (_this.size) {
12830                 c.cls += ' btn-' + _this.size;
12831             }
12832
12833             if (_this.disabled) {
12834                 c.disabled = true;
12835             }
12836         });
12837         
12838         var box = {
12839             tag: 'div',
12840             cn: [
12841                 {
12842                     tag: 'input',
12843                     type : 'hidden',
12844                     cls: 'form-hidden-field'
12845                 },
12846                 {
12847                     tag: 'ul',
12848                     cls: 'roo-select2-choices',
12849                     cn:[
12850                         {
12851                             tag: 'li',
12852                             cls: 'roo-select2-search-field',
12853                             cn: [
12854                                 buttons
12855                             ]
12856                         }
12857                     ]
12858                 }
12859             ]
12860         };
12861         
12862         var combobox = {
12863             cls: 'roo-select2-container input-group roo-select2-container-multi',
12864             cn: [
12865                 box
12866 //                {
12867 //                    tag: 'ul',
12868 //                    cls: 'typeahead typeahead-long dropdown-menu',
12869 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12870 //                }
12871             ]
12872         };
12873         
12874         if(this.hasFeedback && !this.allowBlank){
12875             
12876             var feedback = {
12877                 tag: 'span',
12878                 cls: 'glyphicon form-control-feedback'
12879             };
12880
12881             combobox.cn.push(feedback);
12882         }
12883         
12884         
12885         if (align ==='left' && this.fieldLabel.length) {
12886             
12887             cfg.cls += ' roo-form-group-label-left';
12888             
12889             cfg.cn = [
12890                 {
12891                     tag : 'i',
12892                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12893                     tooltip : 'This field is required'
12894                 },
12895                 {
12896                     tag: 'label',
12897                     'for' :  id,
12898                     cls : 'control-label',
12899                     html : this.fieldLabel
12900
12901                 },
12902                 {
12903                     cls : "", 
12904                     cn: [
12905                         combobox
12906                     ]
12907                 }
12908
12909             ];
12910             
12911             var labelCfg = cfg.cn[1];
12912             var contentCfg = cfg.cn[2];
12913             
12914
12915             if(this.indicatorpos == 'right'){
12916                 
12917                 cfg.cn = [
12918                     {
12919                         tag: 'label',
12920                         'for' :  id,
12921                         cls : 'control-label',
12922                         cn : [
12923                             {
12924                                 tag : 'span',
12925                                 html : this.fieldLabel
12926                             },
12927                             {
12928                                 tag : 'i',
12929                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12930                                 tooltip : 'This field is required'
12931                             }
12932                         ]
12933                     },
12934                     {
12935                         cls : "",
12936                         cn: [
12937                             combobox
12938                         ]
12939                     }
12940
12941                 ];
12942                 
12943                 
12944                 
12945                 labelCfg = cfg.cn[0];
12946                 contentCfg = cfg.cn[1];
12947             
12948             }
12949             
12950             if(this.labelWidth > 12){
12951                 labelCfg.style = "width: " + this.labelWidth + 'px';
12952             }
12953             
12954             if(this.labelWidth < 13 && this.labelmd == 0){
12955                 this.labelmd = this.labelWidth;
12956             }
12957             
12958             if(this.labellg > 0){
12959                 labelCfg.cls += ' col-lg-' + this.labellg;
12960                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12961             }
12962             
12963             if(this.labelmd > 0){
12964                 labelCfg.cls += ' col-md-' + this.labelmd;
12965                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12966             }
12967             
12968             if(this.labelsm > 0){
12969                 labelCfg.cls += ' col-sm-' + this.labelsm;
12970                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12971             }
12972             
12973             if(this.labelxs > 0){
12974                 labelCfg.cls += ' col-xs-' + this.labelxs;
12975                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12976             }
12977                 
12978                 
12979         } else if ( this.fieldLabel.length) {
12980 //                Roo.log(" label");
12981                  cfg.cn = [
12982                     {
12983                         tag : 'i',
12984                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12985                         tooltip : 'This field is required'
12986                     },
12987                     {
12988                         tag: 'label',
12989                         //cls : 'input-group-addon',
12990                         html : this.fieldLabel
12991                     },
12992                     combobox
12993                 ];
12994                 
12995                 if(this.indicatorpos == 'right'){
12996                     cfg.cn = [
12997                         {
12998                             tag: 'label',
12999                             //cls : 'input-group-addon',
13000                             html : this.fieldLabel
13001                         },
13002                         {
13003                             tag : 'i',
13004                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13005                             tooltip : 'This field is required'
13006                         },
13007                         combobox
13008                     ];
13009                     
13010                 }
13011
13012         } else {
13013             
13014 //                Roo.log(" no label && no align");
13015                 cfg = combobox
13016                      
13017                 
13018         }
13019          
13020         var settings=this;
13021         ['xs','sm','md','lg'].map(function(size){
13022             if (settings[size]) {
13023                 cfg.cls += ' col-' + size + '-' + settings[size];
13024             }
13025         });
13026         
13027         return cfg;
13028         
13029     },
13030     
13031     _initEventsCalled : false,
13032     
13033     // private
13034     initEvents: function()
13035     {   
13036         if (this._initEventsCalled) { // as we call render... prevent looping...
13037             return;
13038         }
13039         this._initEventsCalled = true;
13040         
13041         if (!this.store) {
13042             throw "can not find store for combo";
13043         }
13044         
13045         this.indicator = this.indicatorEl();
13046         
13047         this.store = Roo.factory(this.store, Roo.data);
13048         this.store.parent = this;
13049         
13050         // if we are building from html. then this element is so complex, that we can not really
13051         // use the rendered HTML.
13052         // so we have to trash and replace the previous code.
13053         if (Roo.XComponent.build_from_html) {
13054             // remove this element....
13055             var e = this.el.dom, k=0;
13056             while (e ) { e = e.previousSibling;  ++k;}
13057
13058             this.el.remove();
13059             
13060             this.el=false;
13061             this.rendered = false;
13062             
13063             this.render(this.parent().getChildContainer(true), k);
13064         }
13065         
13066         if(Roo.isIOS && this.useNativeIOS){
13067             this.initIOSView();
13068             return;
13069         }
13070         
13071         /*
13072          * Touch Devices
13073          */
13074         
13075         if(Roo.isTouch && this.mobileTouchView){
13076             this.initTouchView();
13077             return;
13078         }
13079         
13080         if(this.tickable){
13081             this.initTickableEvents();
13082             return;
13083         }
13084         
13085         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13086         
13087         if(this.hiddenName){
13088             
13089             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13090             
13091             this.hiddenField.dom.value =
13092                 this.hiddenValue !== undefined ? this.hiddenValue :
13093                 this.value !== undefined ? this.value : '';
13094
13095             // prevent input submission
13096             this.el.dom.removeAttribute('name');
13097             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13098              
13099              
13100         }
13101         //if(Roo.isGecko){
13102         //    this.el.dom.setAttribute('autocomplete', 'off');
13103         //}
13104         
13105         var cls = 'x-combo-list';
13106         
13107         //this.list = new Roo.Layer({
13108         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13109         //});
13110         
13111         var _this = this;
13112         
13113         (function(){
13114             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13115             _this.list.setWidth(lw);
13116         }).defer(100);
13117         
13118         this.list.on('mouseover', this.onViewOver, this);
13119         this.list.on('mousemove', this.onViewMove, this);
13120         this.list.on('scroll', this.onViewScroll, this);
13121         
13122         /*
13123         this.list.swallowEvent('mousewheel');
13124         this.assetHeight = 0;
13125
13126         if(this.title){
13127             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13128             this.assetHeight += this.header.getHeight();
13129         }
13130
13131         this.innerList = this.list.createChild({cls:cls+'-inner'});
13132         this.innerList.on('mouseover', this.onViewOver, this);
13133         this.innerList.on('mousemove', this.onViewMove, this);
13134         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13135         
13136         if(this.allowBlank && !this.pageSize && !this.disableClear){
13137             this.footer = this.list.createChild({cls:cls+'-ft'});
13138             this.pageTb = new Roo.Toolbar(this.footer);
13139            
13140         }
13141         if(this.pageSize){
13142             this.footer = this.list.createChild({cls:cls+'-ft'});
13143             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13144                     {pageSize: this.pageSize});
13145             
13146         }
13147         
13148         if (this.pageTb && this.allowBlank && !this.disableClear) {
13149             var _this = this;
13150             this.pageTb.add(new Roo.Toolbar.Fill(), {
13151                 cls: 'x-btn-icon x-btn-clear',
13152                 text: '&#160;',
13153                 handler: function()
13154                 {
13155                     _this.collapse();
13156                     _this.clearValue();
13157                     _this.onSelect(false, -1);
13158                 }
13159             });
13160         }
13161         if (this.footer) {
13162             this.assetHeight += this.footer.getHeight();
13163         }
13164         */
13165             
13166         if(!this.tpl){
13167             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13168         }
13169
13170         this.view = new Roo.View(this.list, this.tpl, {
13171             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13172         });
13173         //this.view.wrapEl.setDisplayed(false);
13174         this.view.on('click', this.onViewClick, this);
13175         
13176         
13177         this.store.on('beforeload', this.onBeforeLoad, this);
13178         this.store.on('load', this.onLoad, this);
13179         this.store.on('loadexception', this.onLoadException, this);
13180         /*
13181         if(this.resizable){
13182             this.resizer = new Roo.Resizable(this.list,  {
13183                pinned:true, handles:'se'
13184             });
13185             this.resizer.on('resize', function(r, w, h){
13186                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13187                 this.listWidth = w;
13188                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13189                 this.restrictHeight();
13190             }, this);
13191             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13192         }
13193         */
13194         if(!this.editable){
13195             this.editable = true;
13196             this.setEditable(false);
13197         }
13198         
13199         /*
13200         
13201         if (typeof(this.events.add.listeners) != 'undefined') {
13202             
13203             this.addicon = this.wrap.createChild(
13204                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13205        
13206             this.addicon.on('click', function(e) {
13207                 this.fireEvent('add', this);
13208             }, this);
13209         }
13210         if (typeof(this.events.edit.listeners) != 'undefined') {
13211             
13212             this.editicon = this.wrap.createChild(
13213                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13214             if (this.addicon) {
13215                 this.editicon.setStyle('margin-left', '40px');
13216             }
13217             this.editicon.on('click', function(e) {
13218                 
13219                 // we fire even  if inothing is selected..
13220                 this.fireEvent('edit', this, this.lastData );
13221                 
13222             }, this);
13223         }
13224         */
13225         
13226         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13227             "up" : function(e){
13228                 this.inKeyMode = true;
13229                 this.selectPrev();
13230             },
13231
13232             "down" : function(e){
13233                 if(!this.isExpanded()){
13234                     this.onTriggerClick();
13235                 }else{
13236                     this.inKeyMode = true;
13237                     this.selectNext();
13238                 }
13239             },
13240
13241             "enter" : function(e){
13242 //                this.onViewClick();
13243                 //return true;
13244                 this.collapse();
13245                 
13246                 if(this.fireEvent("specialkey", this, e)){
13247                     this.onViewClick(false);
13248                 }
13249                 
13250                 return true;
13251             },
13252
13253             "esc" : function(e){
13254                 this.collapse();
13255             },
13256
13257             "tab" : function(e){
13258                 this.collapse();
13259                 
13260                 if(this.fireEvent("specialkey", this, e)){
13261                     this.onViewClick(false);
13262                 }
13263                 
13264                 return true;
13265             },
13266
13267             scope : this,
13268
13269             doRelay : function(foo, bar, hname){
13270                 if(hname == 'down' || this.scope.isExpanded()){
13271                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13272                 }
13273                 return true;
13274             },
13275
13276             forceKeyDown: true
13277         });
13278         
13279         
13280         this.queryDelay = Math.max(this.queryDelay || 10,
13281                 this.mode == 'local' ? 10 : 250);
13282         
13283         
13284         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13285         
13286         if(this.typeAhead){
13287             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13288         }
13289         if(this.editable !== false){
13290             this.inputEl().on("keyup", this.onKeyUp, this);
13291         }
13292         if(this.forceSelection){
13293             this.inputEl().on('blur', this.doForce, this);
13294         }
13295         
13296         if(this.multiple){
13297             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13298             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13299         }
13300     },
13301     
13302     initTickableEvents: function()
13303     {   
13304         this.createList();
13305         
13306         if(this.hiddenName){
13307             
13308             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13309             
13310             this.hiddenField.dom.value =
13311                 this.hiddenValue !== undefined ? this.hiddenValue :
13312                 this.value !== undefined ? this.value : '';
13313
13314             // prevent input submission
13315             this.el.dom.removeAttribute('name');
13316             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13317              
13318              
13319         }
13320         
13321 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13322         
13323         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13324         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13325         if(this.triggerList){
13326             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13327         }
13328          
13329         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13330         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13331         
13332         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13333         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13334         
13335         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13336         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13337         
13338         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13339         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13340         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13341         
13342         this.okBtn.hide();
13343         this.cancelBtn.hide();
13344         
13345         var _this = this;
13346         
13347         (function(){
13348             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13349             _this.list.setWidth(lw);
13350         }).defer(100);
13351         
13352         this.list.on('mouseover', this.onViewOver, this);
13353         this.list.on('mousemove', this.onViewMove, this);
13354         
13355         this.list.on('scroll', this.onViewScroll, this);
13356         
13357         if(!this.tpl){
13358             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>';
13359         }
13360
13361         this.view = new Roo.View(this.list, this.tpl, {
13362             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13363         });
13364         
13365         //this.view.wrapEl.setDisplayed(false);
13366         this.view.on('click', this.onViewClick, this);
13367         
13368         
13369         
13370         this.store.on('beforeload', this.onBeforeLoad, this);
13371         this.store.on('load', this.onLoad, this);
13372         this.store.on('loadexception', this.onLoadException, this);
13373         
13374         if(this.editable){
13375             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13376                 "up" : function(e){
13377                     this.inKeyMode = true;
13378                     this.selectPrev();
13379                 },
13380
13381                 "down" : function(e){
13382                     this.inKeyMode = true;
13383                     this.selectNext();
13384                 },
13385
13386                 "enter" : function(e){
13387                     if(this.fireEvent("specialkey", this, e)){
13388                         this.onViewClick(false);
13389                     }
13390                     
13391                     return true;
13392                 },
13393
13394                 "esc" : function(e){
13395                     this.onTickableFooterButtonClick(e, false, false);
13396                 },
13397
13398                 "tab" : function(e){
13399                     this.fireEvent("specialkey", this, e);
13400                     
13401                     this.onTickableFooterButtonClick(e, false, false);
13402                     
13403                     return true;
13404                 },
13405
13406                 scope : this,
13407
13408                 doRelay : function(e, fn, key){
13409                     if(this.scope.isExpanded()){
13410                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13411                     }
13412                     return true;
13413                 },
13414
13415                 forceKeyDown: true
13416             });
13417         }
13418         
13419         this.queryDelay = Math.max(this.queryDelay || 10,
13420                 this.mode == 'local' ? 10 : 250);
13421         
13422         
13423         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13424         
13425         if(this.typeAhead){
13426             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13427         }
13428         
13429         if(this.editable !== false){
13430             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13431         }
13432         
13433         this.indicator = this.indicatorEl();
13434         
13435         if(this.indicator){
13436             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13437             this.indicator.hide();
13438         }
13439         
13440     },
13441
13442     onDestroy : function(){
13443         if(this.view){
13444             this.view.setStore(null);
13445             this.view.el.removeAllListeners();
13446             this.view.el.remove();
13447             this.view.purgeListeners();
13448         }
13449         if(this.list){
13450             this.list.dom.innerHTML  = '';
13451         }
13452         
13453         if(this.store){
13454             this.store.un('beforeload', this.onBeforeLoad, this);
13455             this.store.un('load', this.onLoad, this);
13456             this.store.un('loadexception', this.onLoadException, this);
13457         }
13458         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13459     },
13460
13461     // private
13462     fireKey : function(e){
13463         if(e.isNavKeyPress() && !this.list.isVisible()){
13464             this.fireEvent("specialkey", this, e);
13465         }
13466     },
13467
13468     // private
13469     onResize: function(w, h){
13470 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13471 //        
13472 //        if(typeof w != 'number'){
13473 //            // we do not handle it!?!?
13474 //            return;
13475 //        }
13476 //        var tw = this.trigger.getWidth();
13477 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13478 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13479 //        var x = w - tw;
13480 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13481 //            
13482 //        //this.trigger.setStyle('left', x+'px');
13483 //        
13484 //        if(this.list && this.listWidth === undefined){
13485 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13486 //            this.list.setWidth(lw);
13487 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13488 //        }
13489         
13490     
13491         
13492     },
13493
13494     /**
13495      * Allow or prevent the user from directly editing the field text.  If false is passed,
13496      * the user will only be able to select from the items defined in the dropdown list.  This method
13497      * is the runtime equivalent of setting the 'editable' config option at config time.
13498      * @param {Boolean} value True to allow the user to directly edit the field text
13499      */
13500     setEditable : function(value){
13501         if(value == this.editable){
13502             return;
13503         }
13504         this.editable = value;
13505         if(!value){
13506             this.inputEl().dom.setAttribute('readOnly', true);
13507             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13508             this.inputEl().addClass('x-combo-noedit');
13509         }else{
13510             this.inputEl().dom.setAttribute('readOnly', false);
13511             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13512             this.inputEl().removeClass('x-combo-noedit');
13513         }
13514     },
13515
13516     // private
13517     
13518     onBeforeLoad : function(combo,opts){
13519         if(!this.hasFocus){
13520             return;
13521         }
13522          if (!opts.add) {
13523             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13524          }
13525         this.restrictHeight();
13526         this.selectedIndex = -1;
13527     },
13528
13529     // private
13530     onLoad : function(){
13531         
13532         this.hasQuery = false;
13533         
13534         if(!this.hasFocus){
13535             return;
13536         }
13537         
13538         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13539             this.loading.hide();
13540         }
13541         
13542         if(this.store.getCount() > 0){
13543             
13544             this.expand();
13545             this.restrictHeight();
13546             if(this.lastQuery == this.allQuery){
13547                 if(this.editable && !this.tickable){
13548                     this.inputEl().dom.select();
13549                 }
13550                 
13551                 if(
13552                     !this.selectByValue(this.value, true) &&
13553                     this.autoFocus && 
13554                     (
13555                         !this.store.lastOptions ||
13556                         typeof(this.store.lastOptions.add) == 'undefined' || 
13557                         this.store.lastOptions.add != true
13558                     )
13559                 ){
13560                     this.select(0, true);
13561                 }
13562             }else{
13563                 if(this.autoFocus){
13564                     this.selectNext();
13565                 }
13566                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13567                     this.taTask.delay(this.typeAheadDelay);
13568                 }
13569             }
13570         }else{
13571             this.onEmptyResults();
13572         }
13573         
13574         //this.el.focus();
13575     },
13576     // private
13577     onLoadException : function()
13578     {
13579         this.hasQuery = false;
13580         
13581         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13582             this.loading.hide();
13583         }
13584         
13585         if(this.tickable && this.editable){
13586             return;
13587         }
13588         
13589         this.collapse();
13590         // only causes errors at present
13591         //Roo.log(this.store.reader.jsonData);
13592         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13593             // fixme
13594             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13595         //}
13596         
13597         
13598     },
13599     // private
13600     onTypeAhead : function(){
13601         if(this.store.getCount() > 0){
13602             var r = this.store.getAt(0);
13603             var newValue = r.data[this.displayField];
13604             var len = newValue.length;
13605             var selStart = this.getRawValue().length;
13606             
13607             if(selStart != len){
13608                 this.setRawValue(newValue);
13609                 this.selectText(selStart, newValue.length);
13610             }
13611         }
13612     },
13613
13614     // private
13615     onSelect : function(record, index){
13616         
13617         if(this.fireEvent('beforeselect', this, record, index) !== false){
13618         
13619             this.setFromData(index > -1 ? record.data : false);
13620             
13621             this.collapse();
13622             this.fireEvent('select', this, record, index);
13623         }
13624     },
13625
13626     /**
13627      * Returns the currently selected field value or empty string if no value is set.
13628      * @return {String} value The selected value
13629      */
13630     getValue : function()
13631     {
13632         if(Roo.isIOS && this.useNativeIOS){
13633             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13634         }
13635         
13636         if(this.multiple){
13637             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13638         }
13639         
13640         if(this.valueField){
13641             return typeof this.value != 'undefined' ? this.value : '';
13642         }else{
13643             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13644         }
13645     },
13646     
13647     getRawValue : function()
13648     {
13649         if(Roo.isIOS && this.useNativeIOS){
13650             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13651         }
13652         
13653         var v = this.inputEl().getValue();
13654         
13655         return v;
13656     },
13657
13658     /**
13659      * Clears any text/value currently set in the field
13660      */
13661     clearValue : function(){
13662         
13663         if(this.hiddenField){
13664             this.hiddenField.dom.value = '';
13665         }
13666         this.value = '';
13667         this.setRawValue('');
13668         this.lastSelectionText = '';
13669         this.lastData = false;
13670         
13671         var close = this.closeTriggerEl();
13672         
13673         if(close){
13674             close.hide();
13675         }
13676         
13677         this.validate();
13678         
13679     },
13680
13681     /**
13682      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13683      * will be displayed in the field.  If the value does not match the data value of an existing item,
13684      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13685      * Otherwise the field will be blank (although the value will still be set).
13686      * @param {String} value The value to match
13687      */
13688     setValue : function(v)
13689     {
13690         if(Roo.isIOS && this.useNativeIOS){
13691             this.setIOSValue(v);
13692             return;
13693         }
13694         
13695         if(this.multiple){
13696             this.syncValue();
13697             return;
13698         }
13699         
13700         var text = v;
13701         if(this.valueField){
13702             var r = this.findRecord(this.valueField, v);
13703             if(r){
13704                 text = r.data[this.displayField];
13705             }else if(this.valueNotFoundText !== undefined){
13706                 text = this.valueNotFoundText;
13707             }
13708         }
13709         this.lastSelectionText = text;
13710         if(this.hiddenField){
13711             this.hiddenField.dom.value = v;
13712         }
13713         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13714         this.value = v;
13715         
13716         var close = this.closeTriggerEl();
13717         
13718         if(close){
13719             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13720         }
13721         
13722         this.validate();
13723     },
13724     /**
13725      * @property {Object} the last set data for the element
13726      */
13727     
13728     lastData : false,
13729     /**
13730      * Sets the value of the field based on a object which is related to the record format for the store.
13731      * @param {Object} value the value to set as. or false on reset?
13732      */
13733     setFromData : function(o){
13734         
13735         if(this.multiple){
13736             this.addItem(o);
13737             return;
13738         }
13739             
13740         var dv = ''; // display value
13741         var vv = ''; // value value..
13742         this.lastData = o;
13743         if (this.displayField) {
13744             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13745         } else {
13746             // this is an error condition!!!
13747             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13748         }
13749         
13750         if(this.valueField){
13751             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13752         }
13753         
13754         var close = this.closeTriggerEl();
13755         
13756         if(close){
13757             if(dv.length || vv * 1 > 0){
13758                 close.show() ;
13759                 this.blockFocus=true;
13760             } else {
13761                 close.hide();
13762             }             
13763         }
13764         
13765         if(this.hiddenField){
13766             this.hiddenField.dom.value = vv;
13767             
13768             this.lastSelectionText = dv;
13769             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13770             this.value = vv;
13771             return;
13772         }
13773         // no hidden field.. - we store the value in 'value', but still display
13774         // display field!!!!
13775         this.lastSelectionText = dv;
13776         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13777         this.value = vv;
13778         
13779         
13780         
13781     },
13782     // private
13783     reset : function(){
13784         // overridden so that last data is reset..
13785         
13786         if(this.multiple){
13787             this.clearItem();
13788             return;
13789         }
13790         
13791         this.setValue(this.originalValue);
13792         //this.clearInvalid();
13793         this.lastData = false;
13794         if (this.view) {
13795             this.view.clearSelections();
13796         }
13797         
13798         this.validate();
13799     },
13800     // private
13801     findRecord : function(prop, value){
13802         var record;
13803         if(this.store.getCount() > 0){
13804             this.store.each(function(r){
13805                 if(r.data[prop] == value){
13806                     record = r;
13807                     return false;
13808                 }
13809                 return true;
13810             });
13811         }
13812         return record;
13813     },
13814     
13815     getName: function()
13816     {
13817         // returns hidden if it's set..
13818         if (!this.rendered) {return ''};
13819         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13820         
13821     },
13822     // private
13823     onViewMove : function(e, t){
13824         this.inKeyMode = false;
13825     },
13826
13827     // private
13828     onViewOver : function(e, t){
13829         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13830             return;
13831         }
13832         var item = this.view.findItemFromChild(t);
13833         
13834         if(item){
13835             var index = this.view.indexOf(item);
13836             this.select(index, false);
13837         }
13838     },
13839
13840     // private
13841     onViewClick : function(view, doFocus, el, e)
13842     {
13843         var index = this.view.getSelectedIndexes()[0];
13844         
13845         var r = this.store.getAt(index);
13846         
13847         if(this.tickable){
13848             
13849             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13850                 return;
13851             }
13852             
13853             var rm = false;
13854             var _this = this;
13855             
13856             Roo.each(this.tickItems, function(v,k){
13857                 
13858                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13859                     Roo.log(v);
13860                     _this.tickItems.splice(k, 1);
13861                     
13862                     if(typeof(e) == 'undefined' && view == false){
13863                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13864                     }
13865                     
13866                     rm = true;
13867                     return;
13868                 }
13869             });
13870             
13871             if(rm){
13872                 return;
13873             }
13874             
13875             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13876                 this.tickItems.push(r.data);
13877             }
13878             
13879             if(typeof(e) == 'undefined' && view == false){
13880                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13881             }
13882                     
13883             return;
13884         }
13885         
13886         if(r){
13887             this.onSelect(r, index);
13888         }
13889         if(doFocus !== false && !this.blockFocus){
13890             this.inputEl().focus();
13891         }
13892     },
13893
13894     // private
13895     restrictHeight : function(){
13896         //this.innerList.dom.style.height = '';
13897         //var inner = this.innerList.dom;
13898         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13899         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13900         //this.list.beginUpdate();
13901         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13902         this.list.alignTo(this.inputEl(), this.listAlign);
13903         this.list.alignTo(this.inputEl(), this.listAlign);
13904         //this.list.endUpdate();
13905     },
13906
13907     // private
13908     onEmptyResults : function(){
13909         
13910         if(this.tickable && this.editable){
13911             this.restrictHeight();
13912             return;
13913         }
13914         
13915         this.collapse();
13916     },
13917
13918     /**
13919      * Returns true if the dropdown list is expanded, else false.
13920      */
13921     isExpanded : function(){
13922         return this.list.isVisible();
13923     },
13924
13925     /**
13926      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13927      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13928      * @param {String} value The data value of the item to select
13929      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13930      * selected item if it is not currently in view (defaults to true)
13931      * @return {Boolean} True if the value matched an item in the list, else false
13932      */
13933     selectByValue : function(v, scrollIntoView){
13934         if(v !== undefined && v !== null){
13935             var r = this.findRecord(this.valueField || this.displayField, v);
13936             if(r){
13937                 this.select(this.store.indexOf(r), scrollIntoView);
13938                 return true;
13939             }
13940         }
13941         return false;
13942     },
13943
13944     /**
13945      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13946      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13947      * @param {Number} index The zero-based index of the list item to select
13948      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13949      * selected item if it is not currently in view (defaults to true)
13950      */
13951     select : function(index, scrollIntoView){
13952         this.selectedIndex = index;
13953         this.view.select(index);
13954         if(scrollIntoView !== false){
13955             var el = this.view.getNode(index);
13956             /*
13957              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13958              */
13959             if(el){
13960                 this.list.scrollChildIntoView(el, false);
13961             }
13962         }
13963     },
13964
13965     // private
13966     selectNext : function(){
13967         var ct = this.store.getCount();
13968         if(ct > 0){
13969             if(this.selectedIndex == -1){
13970                 this.select(0);
13971             }else if(this.selectedIndex < ct-1){
13972                 this.select(this.selectedIndex+1);
13973             }
13974         }
13975     },
13976
13977     // private
13978     selectPrev : function(){
13979         var ct = this.store.getCount();
13980         if(ct > 0){
13981             if(this.selectedIndex == -1){
13982                 this.select(0);
13983             }else if(this.selectedIndex != 0){
13984                 this.select(this.selectedIndex-1);
13985             }
13986         }
13987     },
13988
13989     // private
13990     onKeyUp : function(e){
13991         if(this.editable !== false && !e.isSpecialKey()){
13992             this.lastKey = e.getKey();
13993             this.dqTask.delay(this.queryDelay);
13994         }
13995     },
13996
13997     // private
13998     validateBlur : function(){
13999         return !this.list || !this.list.isVisible();   
14000     },
14001
14002     // private
14003     initQuery : function(){
14004         
14005         var v = this.getRawValue();
14006         
14007         if(this.tickable && this.editable){
14008             v = this.tickableInputEl().getValue();
14009         }
14010         
14011         this.doQuery(v);
14012     },
14013
14014     // private
14015     doForce : function(){
14016         if(this.inputEl().dom.value.length > 0){
14017             this.inputEl().dom.value =
14018                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14019              
14020         }
14021     },
14022
14023     /**
14024      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14025      * query allowing the query action to be canceled if needed.
14026      * @param {String} query The SQL query to execute
14027      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14028      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14029      * saved in the current store (defaults to false)
14030      */
14031     doQuery : function(q, forceAll){
14032         
14033         if(q === undefined || q === null){
14034             q = '';
14035         }
14036         var qe = {
14037             query: q,
14038             forceAll: forceAll,
14039             combo: this,
14040             cancel:false
14041         };
14042         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14043             return false;
14044         }
14045         q = qe.query;
14046         
14047         forceAll = qe.forceAll;
14048         if(forceAll === true || (q.length >= this.minChars)){
14049             
14050             this.hasQuery = true;
14051             
14052             if(this.lastQuery != q || this.alwaysQuery){
14053                 this.lastQuery = q;
14054                 if(this.mode == 'local'){
14055                     this.selectedIndex = -1;
14056                     if(forceAll){
14057                         this.store.clearFilter();
14058                     }else{
14059                         
14060                         if(this.specialFilter){
14061                             this.fireEvent('specialfilter', this);
14062                             this.onLoad();
14063                             return;
14064                         }
14065                         
14066                         this.store.filter(this.displayField, q);
14067                     }
14068                     
14069                     this.store.fireEvent("datachanged", this.store);
14070                     
14071                     this.onLoad();
14072                     
14073                     
14074                 }else{
14075                     
14076                     this.store.baseParams[this.queryParam] = q;
14077                     
14078                     var options = {params : this.getParams(q)};
14079                     
14080                     if(this.loadNext){
14081                         options.add = true;
14082                         options.params.start = this.page * this.pageSize;
14083                     }
14084                     
14085                     this.store.load(options);
14086                     
14087                     /*
14088                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14089                      *  we should expand the list on onLoad
14090                      *  so command out it
14091                      */
14092 //                    this.expand();
14093                 }
14094             }else{
14095                 this.selectedIndex = -1;
14096                 this.onLoad();   
14097             }
14098         }
14099         
14100         this.loadNext = false;
14101     },
14102     
14103     // private
14104     getParams : function(q){
14105         var p = {};
14106         //p[this.queryParam] = q;
14107         
14108         if(this.pageSize){
14109             p.start = 0;
14110             p.limit = this.pageSize;
14111         }
14112         return p;
14113     },
14114
14115     /**
14116      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14117      */
14118     collapse : function(){
14119         if(!this.isExpanded()){
14120             return;
14121         }
14122         
14123         this.list.hide();
14124         
14125         this.hasFocus = false;
14126         
14127         if(this.tickable){
14128             this.okBtn.hide();
14129             this.cancelBtn.hide();
14130             this.trigger.show();
14131             
14132             if(this.editable){
14133                 this.tickableInputEl().dom.value = '';
14134                 this.tickableInputEl().blur();
14135             }
14136             
14137         }
14138         
14139         Roo.get(document).un('mousedown', this.collapseIf, this);
14140         Roo.get(document).un('mousewheel', this.collapseIf, this);
14141         if (!this.editable) {
14142             Roo.get(document).un('keydown', this.listKeyPress, this);
14143         }
14144         this.fireEvent('collapse', this);
14145         
14146         this.validate();
14147     },
14148
14149     // private
14150     collapseIf : function(e){
14151         var in_combo  = e.within(this.el);
14152         var in_list =  e.within(this.list);
14153         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14154         
14155         if (in_combo || in_list || is_list) {
14156             //e.stopPropagation();
14157             return;
14158         }
14159         
14160         if(this.tickable){
14161             this.onTickableFooterButtonClick(e, false, false);
14162         }
14163
14164         this.collapse();
14165         
14166     },
14167
14168     /**
14169      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14170      */
14171     expand : function(){
14172        
14173         if(this.isExpanded() || !this.hasFocus){
14174             return;
14175         }
14176         
14177         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14178         this.list.setWidth(lw);
14179         
14180         Roo.log('expand');
14181         
14182         this.list.show();
14183         
14184         this.restrictHeight();
14185         
14186         if(this.tickable){
14187             
14188             this.tickItems = Roo.apply([], this.item);
14189             
14190             this.okBtn.show();
14191             this.cancelBtn.show();
14192             this.trigger.hide();
14193             
14194             if(this.editable){
14195                 this.tickableInputEl().focus();
14196             }
14197             
14198         }
14199         
14200         Roo.get(document).on('mousedown', this.collapseIf, this);
14201         Roo.get(document).on('mousewheel', this.collapseIf, this);
14202         if (!this.editable) {
14203             Roo.get(document).on('keydown', this.listKeyPress, this);
14204         }
14205         
14206         this.fireEvent('expand', this);
14207     },
14208
14209     // private
14210     // Implements the default empty TriggerField.onTriggerClick function
14211     onTriggerClick : function(e)
14212     {
14213         Roo.log('trigger click');
14214         
14215         if(this.disabled || !this.triggerList){
14216             return;
14217         }
14218         
14219         this.page = 0;
14220         this.loadNext = false;
14221         
14222         if(this.isExpanded()){
14223             this.collapse();
14224             if (!this.blockFocus) {
14225                 this.inputEl().focus();
14226             }
14227             
14228         }else {
14229             this.hasFocus = true;
14230             if(this.triggerAction == 'all') {
14231                 this.doQuery(this.allQuery, true);
14232             } else {
14233                 this.doQuery(this.getRawValue());
14234             }
14235             if (!this.blockFocus) {
14236                 this.inputEl().focus();
14237             }
14238         }
14239     },
14240     
14241     onTickableTriggerClick : function(e)
14242     {
14243         if(this.disabled){
14244             return;
14245         }
14246         
14247         this.page = 0;
14248         this.loadNext = false;
14249         this.hasFocus = true;
14250         
14251         if(this.triggerAction == 'all') {
14252             this.doQuery(this.allQuery, true);
14253         } else {
14254             this.doQuery(this.getRawValue());
14255         }
14256     },
14257     
14258     onSearchFieldClick : function(e)
14259     {
14260         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14261             this.onTickableFooterButtonClick(e, false, false);
14262             return;
14263         }
14264         
14265         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14266             return;
14267         }
14268         
14269         this.page = 0;
14270         this.loadNext = false;
14271         this.hasFocus = true;
14272         
14273         if(this.triggerAction == 'all') {
14274             this.doQuery(this.allQuery, true);
14275         } else {
14276             this.doQuery(this.getRawValue());
14277         }
14278     },
14279     
14280     listKeyPress : function(e)
14281     {
14282         //Roo.log('listkeypress');
14283         // scroll to first matching element based on key pres..
14284         if (e.isSpecialKey()) {
14285             return false;
14286         }
14287         var k = String.fromCharCode(e.getKey()).toUpperCase();
14288         //Roo.log(k);
14289         var match  = false;
14290         var csel = this.view.getSelectedNodes();
14291         var cselitem = false;
14292         if (csel.length) {
14293             var ix = this.view.indexOf(csel[0]);
14294             cselitem  = this.store.getAt(ix);
14295             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14296                 cselitem = false;
14297             }
14298             
14299         }
14300         
14301         this.store.each(function(v) { 
14302             if (cselitem) {
14303                 // start at existing selection.
14304                 if (cselitem.id == v.id) {
14305                     cselitem = false;
14306                 }
14307                 return true;
14308             }
14309                 
14310             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14311                 match = this.store.indexOf(v);
14312                 return false;
14313             }
14314             return true;
14315         }, this);
14316         
14317         if (match === false) {
14318             return true; // no more action?
14319         }
14320         // scroll to?
14321         this.view.select(match);
14322         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14323         sn.scrollIntoView(sn.dom.parentNode, false);
14324     },
14325     
14326     onViewScroll : function(e, t){
14327         
14328         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){
14329             return;
14330         }
14331         
14332         this.hasQuery = true;
14333         
14334         this.loading = this.list.select('.loading', true).first();
14335         
14336         if(this.loading === null){
14337             this.list.createChild({
14338                 tag: 'div',
14339                 cls: 'loading roo-select2-more-results roo-select2-active',
14340                 html: 'Loading more results...'
14341             });
14342             
14343             this.loading = this.list.select('.loading', true).first();
14344             
14345             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14346             
14347             this.loading.hide();
14348         }
14349         
14350         this.loading.show();
14351         
14352         var _combo = this;
14353         
14354         this.page++;
14355         this.loadNext = true;
14356         
14357         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14358         
14359         return;
14360     },
14361     
14362     addItem : function(o)
14363     {   
14364         var dv = ''; // display value
14365         
14366         if (this.displayField) {
14367             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14368         } else {
14369             // this is an error condition!!!
14370             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14371         }
14372         
14373         if(!dv.length){
14374             return;
14375         }
14376         
14377         var choice = this.choices.createChild({
14378             tag: 'li',
14379             cls: 'roo-select2-search-choice',
14380             cn: [
14381                 {
14382                     tag: 'div',
14383                     html: dv
14384                 },
14385                 {
14386                     tag: 'a',
14387                     href: '#',
14388                     cls: 'roo-select2-search-choice-close fa fa-times',
14389                     tabindex: '-1'
14390                 }
14391             ]
14392             
14393         }, this.searchField);
14394         
14395         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14396         
14397         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14398         
14399         this.item.push(o);
14400         
14401         this.lastData = o;
14402         
14403         this.syncValue();
14404         
14405         this.inputEl().dom.value = '';
14406         
14407         this.validate();
14408     },
14409     
14410     onRemoveItem : function(e, _self, o)
14411     {
14412         e.preventDefault();
14413         
14414         this.lastItem = Roo.apply([], this.item);
14415         
14416         var index = this.item.indexOf(o.data) * 1;
14417         
14418         if( index < 0){
14419             Roo.log('not this item?!');
14420             return;
14421         }
14422         
14423         this.item.splice(index, 1);
14424         o.item.remove();
14425         
14426         this.syncValue();
14427         
14428         this.fireEvent('remove', this, e);
14429         
14430         this.validate();
14431         
14432     },
14433     
14434     syncValue : function()
14435     {
14436         if(!this.item.length){
14437             this.clearValue();
14438             return;
14439         }
14440             
14441         var value = [];
14442         var _this = this;
14443         Roo.each(this.item, function(i){
14444             if(_this.valueField){
14445                 value.push(i[_this.valueField]);
14446                 return;
14447             }
14448
14449             value.push(i);
14450         });
14451
14452         this.value = value.join(',');
14453
14454         if(this.hiddenField){
14455             this.hiddenField.dom.value = this.value;
14456         }
14457         
14458         this.store.fireEvent("datachanged", this.store);
14459         
14460         this.validate();
14461     },
14462     
14463     clearItem : function()
14464     {
14465         if(!this.multiple){
14466             return;
14467         }
14468         
14469         this.item = [];
14470         
14471         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14472            c.remove();
14473         });
14474         
14475         this.syncValue();
14476         
14477         this.validate();
14478         
14479         if(this.tickable && !Roo.isTouch){
14480             this.view.refresh();
14481         }
14482     },
14483     
14484     inputEl: function ()
14485     {
14486         if(Roo.isIOS && this.useNativeIOS){
14487             return this.el.select('select.roo-ios-select', true).first();
14488         }
14489         
14490         if(Roo.isTouch && this.mobileTouchView){
14491             return this.el.select('input.form-control',true).first();
14492         }
14493         
14494         if(this.tickable){
14495             return this.searchField;
14496         }
14497         
14498         return this.el.select('input.form-control',true).first();
14499     },
14500     
14501     onTickableFooterButtonClick : function(e, btn, el)
14502     {
14503         e.preventDefault();
14504         
14505         this.lastItem = Roo.apply([], this.item);
14506         
14507         if(btn && btn.name == 'cancel'){
14508             this.tickItems = Roo.apply([], this.item);
14509             this.collapse();
14510             return;
14511         }
14512         
14513         this.clearItem();
14514         
14515         var _this = this;
14516         
14517         Roo.each(this.tickItems, function(o){
14518             _this.addItem(o);
14519         });
14520         
14521         this.collapse();
14522         
14523     },
14524     
14525     validate : function()
14526     {
14527         var v = this.getRawValue();
14528         
14529         if(this.multiple){
14530             v = this.getValue();
14531         }
14532         
14533         if(this.disabled || this.allowBlank || v.length){
14534             this.markValid();
14535             return true;
14536         }
14537         
14538         this.markInvalid();
14539         return false;
14540     },
14541     
14542     tickableInputEl : function()
14543     {
14544         if(!this.tickable || !this.editable){
14545             return this.inputEl();
14546         }
14547         
14548         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14549     },
14550     
14551     
14552     getAutoCreateTouchView : function()
14553     {
14554         var id = Roo.id();
14555         
14556         var cfg = {
14557             cls: 'form-group' //input-group
14558         };
14559         
14560         var input =  {
14561             tag: 'input',
14562             id : id,
14563             type : this.inputType,
14564             cls : 'form-control x-combo-noedit',
14565             autocomplete: 'new-password',
14566             placeholder : this.placeholder || '',
14567             readonly : true
14568         };
14569         
14570         if (this.name) {
14571             input.name = this.name;
14572         }
14573         
14574         if (this.size) {
14575             input.cls += ' input-' + this.size;
14576         }
14577         
14578         if (this.disabled) {
14579             input.disabled = true;
14580         }
14581         
14582         var inputblock = {
14583             cls : '',
14584             cn : [
14585                 input
14586             ]
14587         };
14588         
14589         if(this.before){
14590             inputblock.cls += ' input-group';
14591             
14592             inputblock.cn.unshift({
14593                 tag :'span',
14594                 cls : 'input-group-addon',
14595                 html : this.before
14596             });
14597         }
14598         
14599         if(this.removable && !this.multiple){
14600             inputblock.cls += ' roo-removable';
14601             
14602             inputblock.cn.push({
14603                 tag: 'button',
14604                 html : 'x',
14605                 cls : 'roo-combo-removable-btn close'
14606             });
14607         }
14608
14609         if(this.hasFeedback && !this.allowBlank){
14610             
14611             inputblock.cls += ' has-feedback';
14612             
14613             inputblock.cn.push({
14614                 tag: 'span',
14615                 cls: 'glyphicon form-control-feedback'
14616             });
14617             
14618         }
14619         
14620         if (this.after) {
14621             
14622             inputblock.cls += (this.before) ? '' : ' input-group';
14623             
14624             inputblock.cn.push({
14625                 tag :'span',
14626                 cls : 'input-group-addon',
14627                 html : this.after
14628             });
14629         }
14630
14631         var box = {
14632             tag: 'div',
14633             cn: [
14634                 {
14635                     tag: 'input',
14636                     type : 'hidden',
14637                     cls: 'form-hidden-field'
14638                 },
14639                 inputblock
14640             ]
14641             
14642         };
14643         
14644         if(this.multiple){
14645             box = {
14646                 tag: 'div',
14647                 cn: [
14648                     {
14649                         tag: 'input',
14650                         type : 'hidden',
14651                         cls: 'form-hidden-field'
14652                     },
14653                     {
14654                         tag: 'ul',
14655                         cls: 'roo-select2-choices',
14656                         cn:[
14657                             {
14658                                 tag: 'li',
14659                                 cls: 'roo-select2-search-field',
14660                                 cn: [
14661
14662                                     inputblock
14663                                 ]
14664                             }
14665                         ]
14666                     }
14667                 ]
14668             }
14669         };
14670         
14671         var combobox = {
14672             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14673             cn: [
14674                 box
14675             ]
14676         };
14677         
14678         if(!this.multiple && this.showToggleBtn){
14679             
14680             var caret = {
14681                         tag: 'span',
14682                         cls: 'caret'
14683             };
14684             
14685             if (this.caret != false) {
14686                 caret = {
14687                      tag: 'i',
14688                      cls: 'fa fa-' + this.caret
14689                 };
14690                 
14691             }
14692             
14693             combobox.cn.push({
14694                 tag :'span',
14695                 cls : 'input-group-addon btn dropdown-toggle',
14696                 cn : [
14697                     caret,
14698                     {
14699                         tag: 'span',
14700                         cls: 'combobox-clear',
14701                         cn  : [
14702                             {
14703                                 tag : 'i',
14704                                 cls: 'icon-remove'
14705                             }
14706                         ]
14707                     }
14708                 ]
14709
14710             })
14711         }
14712         
14713         if(this.multiple){
14714             combobox.cls += ' roo-select2-container-multi';
14715         }
14716         
14717         var align = this.labelAlign || this.parentLabelAlign();
14718         
14719         if (align ==='left' && this.fieldLabel.length) {
14720
14721             cfg.cn = [
14722                 {
14723                    tag : 'i',
14724                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14725                    tooltip : 'This field is required'
14726                 },
14727                 {
14728                     tag: 'label',
14729                     cls : 'control-label',
14730                     html : this.fieldLabel
14731
14732                 },
14733                 {
14734                     cls : '', 
14735                     cn: [
14736                         combobox
14737                     ]
14738                 }
14739             ];
14740             
14741             var labelCfg = cfg.cn[1];
14742             var contentCfg = cfg.cn[2];
14743             
14744
14745             if(this.indicatorpos == 'right'){
14746                 cfg.cn = [
14747                     {
14748                         tag: 'label',
14749                         'for' :  id,
14750                         cls : 'control-label',
14751                         cn : [
14752                             {
14753                                 tag : 'span',
14754                                 html : this.fieldLabel
14755                             },
14756                             {
14757                                 tag : 'i',
14758                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14759                                 tooltip : 'This field is required'
14760                             }
14761                         ]
14762                     },
14763                     {
14764                         cls : "",
14765                         cn: [
14766                             combobox
14767                         ]
14768                     }
14769
14770                 ];
14771                 
14772                 labelCfg = cfg.cn[0];
14773                 contentCfg = cfg.cn[1];
14774             }
14775             
14776            
14777             
14778             if(this.labelWidth > 12){
14779                 labelCfg.style = "width: " + this.labelWidth + 'px';
14780             }
14781             
14782             if(this.labelWidth < 13 && this.labelmd == 0){
14783                 this.labelmd = this.labelWidth;
14784             }
14785             
14786             if(this.labellg > 0){
14787                 labelCfg.cls += ' col-lg-' + this.labellg;
14788                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14789             }
14790             
14791             if(this.labelmd > 0){
14792                 labelCfg.cls += ' col-md-' + this.labelmd;
14793                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14794             }
14795             
14796             if(this.labelsm > 0){
14797                 labelCfg.cls += ' col-sm-' + this.labelsm;
14798                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14799             }
14800             
14801             if(this.labelxs > 0){
14802                 labelCfg.cls += ' col-xs-' + this.labelxs;
14803                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14804             }
14805                 
14806                 
14807         } else if ( this.fieldLabel.length) {
14808             cfg.cn = [
14809                 {
14810                    tag : 'i',
14811                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14812                    tooltip : 'This field is required'
14813                 },
14814                 {
14815                     tag: 'label',
14816                     cls : 'control-label',
14817                     html : this.fieldLabel
14818
14819                 },
14820                 {
14821                     cls : '', 
14822                     cn: [
14823                         combobox
14824                     ]
14825                 }
14826             ];
14827             
14828             if(this.indicatorpos == 'right'){
14829                 cfg.cn = [
14830                     {
14831                         tag: 'label',
14832                         cls : 'control-label',
14833                         html : this.fieldLabel,
14834                         cn : [
14835                             {
14836                                tag : 'i',
14837                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14838                                tooltip : 'This field is required'
14839                             }
14840                         ]
14841                     },
14842                     {
14843                         cls : '', 
14844                         cn: [
14845                             combobox
14846                         ]
14847                     }
14848                 ];
14849             }
14850         } else {
14851             cfg.cn = combobox;    
14852         }
14853         
14854         
14855         var settings = this;
14856         
14857         ['xs','sm','md','lg'].map(function(size){
14858             if (settings[size]) {
14859                 cfg.cls += ' col-' + size + '-' + settings[size];
14860             }
14861         });
14862         
14863         return cfg;
14864     },
14865     
14866     initTouchView : function()
14867     {
14868         this.renderTouchView();
14869         
14870         this.touchViewEl.on('scroll', function(){
14871             this.el.dom.scrollTop = 0;
14872         }, this);
14873         
14874         this.originalValue = this.getValue();
14875         
14876         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14877         
14878         this.inputEl().on("click", this.showTouchView, this);
14879         if (this.triggerEl) {
14880             this.triggerEl.on("click", this.showTouchView, this);
14881         }
14882         
14883         
14884         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14885         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14886         
14887         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14888         
14889         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14890         this.store.on('load', this.onTouchViewLoad, this);
14891         this.store.on('loadexception', this.onTouchViewLoadException, this);
14892         
14893         if(this.hiddenName){
14894             
14895             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14896             
14897             this.hiddenField.dom.value =
14898                 this.hiddenValue !== undefined ? this.hiddenValue :
14899                 this.value !== undefined ? this.value : '';
14900         
14901             this.el.dom.removeAttribute('name');
14902             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14903         }
14904         
14905         if(this.multiple){
14906             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14907             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14908         }
14909         
14910         if(this.removable && !this.multiple){
14911             var close = this.closeTriggerEl();
14912             if(close){
14913                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14914                 close.on('click', this.removeBtnClick, this, close);
14915             }
14916         }
14917         /*
14918          * fix the bug in Safari iOS8
14919          */
14920         this.inputEl().on("focus", function(e){
14921             document.activeElement.blur();
14922         }, this);
14923         
14924         return;
14925         
14926         
14927     },
14928     
14929     renderTouchView : function()
14930     {
14931         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14932         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14933         
14934         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14935         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14936         
14937         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14938         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14939         this.touchViewBodyEl.setStyle('overflow', 'auto');
14940         
14941         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14942         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14943         
14944         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14945         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14946         
14947     },
14948     
14949     showTouchView : function()
14950     {
14951         if(this.disabled){
14952             return;
14953         }
14954         
14955         this.touchViewHeaderEl.hide();
14956
14957         if(this.modalTitle.length){
14958             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14959             this.touchViewHeaderEl.show();
14960         }
14961
14962         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14963         this.touchViewEl.show();
14964
14965         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14966         
14967         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14968         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14969
14970         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14971
14972         if(this.modalTitle.length){
14973             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14974         }
14975         
14976         this.touchViewBodyEl.setHeight(bodyHeight);
14977
14978         if(this.animate){
14979             var _this = this;
14980             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14981         }else{
14982             this.touchViewEl.addClass('in');
14983         }
14984
14985         this.doTouchViewQuery();
14986         
14987     },
14988     
14989     hideTouchView : function()
14990     {
14991         this.touchViewEl.removeClass('in');
14992
14993         if(this.animate){
14994             var _this = this;
14995             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14996         }else{
14997             this.touchViewEl.setStyle('display', 'none');
14998         }
14999         
15000     },
15001     
15002     setTouchViewValue : function()
15003     {
15004         if(this.multiple){
15005             this.clearItem();
15006         
15007             var _this = this;
15008
15009             Roo.each(this.tickItems, function(o){
15010                 this.addItem(o);
15011             }, this);
15012         }
15013         
15014         this.hideTouchView();
15015     },
15016     
15017     doTouchViewQuery : function()
15018     {
15019         var qe = {
15020             query: '',
15021             forceAll: true,
15022             combo: this,
15023             cancel:false
15024         };
15025         
15026         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15027             return false;
15028         }
15029         
15030         if(!this.alwaysQuery || this.mode == 'local'){
15031             this.onTouchViewLoad();
15032             return;
15033         }
15034         
15035         this.store.load();
15036     },
15037     
15038     onTouchViewBeforeLoad : function(combo,opts)
15039     {
15040         return;
15041     },
15042
15043     // private
15044     onTouchViewLoad : function()
15045     {
15046         if(this.store.getCount() < 1){
15047             this.onTouchViewEmptyResults();
15048             return;
15049         }
15050         
15051         this.clearTouchView();
15052         
15053         var rawValue = this.getRawValue();
15054         
15055         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15056         
15057         this.tickItems = [];
15058         
15059         this.store.data.each(function(d, rowIndex){
15060             var row = this.touchViewListGroup.createChild(template);
15061             
15062             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15063                 row.addClass(d.data.cls);
15064             }
15065             
15066             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15067                 var cfg = {
15068                     data : d.data,
15069                     html : d.data[this.displayField]
15070                 };
15071                 
15072                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15073                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15074                 }
15075             }
15076             row.removeClass('selected');
15077             if(!this.multiple && this.valueField &&
15078                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15079             {
15080                 // radio buttons..
15081                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15082                 row.addClass('selected');
15083             }
15084             
15085             if(this.multiple && this.valueField &&
15086                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15087             {
15088                 
15089                 // checkboxes...
15090                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15091                 this.tickItems.push(d.data);
15092             }
15093             
15094             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15095             
15096         }, this);
15097         
15098         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15099         
15100         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15101
15102         if(this.modalTitle.length){
15103             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15104         }
15105
15106         var listHeight = this.touchViewListGroup.getHeight();
15107         
15108         var _this = this;
15109         
15110         if(firstChecked && listHeight > bodyHeight){
15111             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15112         }
15113         
15114     },
15115     
15116     onTouchViewLoadException : function()
15117     {
15118         this.hideTouchView();
15119     },
15120     
15121     onTouchViewEmptyResults : function()
15122     {
15123         this.clearTouchView();
15124         
15125         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15126         
15127         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15128         
15129     },
15130     
15131     clearTouchView : function()
15132     {
15133         this.touchViewListGroup.dom.innerHTML = '';
15134     },
15135     
15136     onTouchViewClick : function(e, el, o)
15137     {
15138         e.preventDefault();
15139         
15140         var row = o.row;
15141         var rowIndex = o.rowIndex;
15142         
15143         var r = this.store.getAt(rowIndex);
15144         
15145         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15146             
15147             if(!this.multiple){
15148                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15149                     c.dom.removeAttribute('checked');
15150                 }, this);
15151
15152                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15153
15154                 this.setFromData(r.data);
15155
15156                 var close = this.closeTriggerEl();
15157
15158                 if(close){
15159                     close.show();
15160                 }
15161
15162                 this.hideTouchView();
15163
15164                 this.fireEvent('select', this, r, rowIndex);
15165
15166                 return;
15167             }
15168
15169             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15170                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15171                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15172                 return;
15173             }
15174
15175             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15176             this.addItem(r.data);
15177             this.tickItems.push(r.data);
15178         }
15179     },
15180     
15181     getAutoCreateNativeIOS : function()
15182     {
15183         var cfg = {
15184             cls: 'form-group' //input-group,
15185         };
15186         
15187         var combobox =  {
15188             tag: 'select',
15189             cls : 'roo-ios-select'
15190         };
15191         
15192         if (this.name) {
15193             combobox.name = this.name;
15194         }
15195         
15196         if (this.disabled) {
15197             combobox.disabled = true;
15198         }
15199         
15200         var settings = this;
15201         
15202         ['xs','sm','md','lg'].map(function(size){
15203             if (settings[size]) {
15204                 cfg.cls += ' col-' + size + '-' + settings[size];
15205             }
15206         });
15207         
15208         cfg.cn = combobox;
15209         
15210         return cfg;
15211         
15212     },
15213     
15214     initIOSView : function()
15215     {
15216         this.store.on('load', this.onIOSViewLoad, this);
15217         
15218         return;
15219     },
15220     
15221     onIOSViewLoad : function()
15222     {
15223         if(this.store.getCount() < 1){
15224             return;
15225         }
15226         
15227         this.clearIOSView();
15228         
15229         if(this.allowBlank) {
15230             
15231             var default_text = '-- SELECT --';
15232             
15233             if(this.placeholder.length){
15234                 default_text = this.placeholder;
15235             }
15236             
15237             if(this.emptyTitle.length){
15238                 default_text += ' - ' + this.emptyTitle + ' -';
15239             }
15240             
15241             var opt = this.inputEl().createChild({
15242                 tag: 'option',
15243                 value : 0,
15244                 html : default_text
15245             });
15246             
15247             var o = {};
15248             o[this.valueField] = 0;
15249             o[this.displayField] = default_text;
15250             
15251             this.ios_options.push({
15252                 data : o,
15253                 el : opt
15254             });
15255             
15256         }
15257         
15258         this.store.data.each(function(d, rowIndex){
15259             
15260             var html = '';
15261             
15262             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15263                 html = d.data[this.displayField];
15264             }
15265             
15266             var value = '';
15267             
15268             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15269                 value = d.data[this.valueField];
15270             }
15271             
15272             var option = {
15273                 tag: 'option',
15274                 value : value,
15275                 html : html
15276             };
15277             
15278             if(this.value == d.data[this.valueField]){
15279                 option['selected'] = true;
15280             }
15281             
15282             var opt = this.inputEl().createChild(option);
15283             
15284             this.ios_options.push({
15285                 data : d.data,
15286                 el : opt
15287             });
15288             
15289         }, this);
15290         
15291         this.inputEl().on('change', function(){
15292            this.fireEvent('select', this);
15293         }, this);
15294         
15295     },
15296     
15297     clearIOSView: function()
15298     {
15299         this.inputEl().dom.innerHTML = '';
15300         
15301         this.ios_options = [];
15302     },
15303     
15304     setIOSValue: function(v)
15305     {
15306         this.value = v;
15307         
15308         if(!this.ios_options){
15309             return;
15310         }
15311         
15312         Roo.each(this.ios_options, function(opts){
15313            
15314            opts.el.dom.removeAttribute('selected');
15315            
15316            if(opts.data[this.valueField] != v){
15317                return;
15318            }
15319            
15320            opts.el.dom.setAttribute('selected', true);
15321            
15322         }, this);
15323     }
15324
15325     /** 
15326     * @cfg {Boolean} grow 
15327     * @hide 
15328     */
15329     /** 
15330     * @cfg {Number} growMin 
15331     * @hide 
15332     */
15333     /** 
15334     * @cfg {Number} growMax 
15335     * @hide 
15336     */
15337     /**
15338      * @hide
15339      * @method autoSize
15340      */
15341 });
15342
15343 Roo.apply(Roo.bootstrap.ComboBox,  {
15344     
15345     header : {
15346         tag: 'div',
15347         cls: 'modal-header',
15348         cn: [
15349             {
15350                 tag: 'h4',
15351                 cls: 'modal-title'
15352             }
15353         ]
15354     },
15355     
15356     body : {
15357         tag: 'div',
15358         cls: 'modal-body',
15359         cn: [
15360             {
15361                 tag: 'ul',
15362                 cls: 'list-group'
15363             }
15364         ]
15365     },
15366     
15367     listItemRadio : {
15368         tag: 'li',
15369         cls: 'list-group-item',
15370         cn: [
15371             {
15372                 tag: 'span',
15373                 cls: 'roo-combobox-list-group-item-value'
15374             },
15375             {
15376                 tag: 'div',
15377                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15378                 cn: [
15379                     {
15380                         tag: 'input',
15381                         type: 'radio'
15382                     },
15383                     {
15384                         tag: 'label'
15385                     }
15386                 ]
15387             }
15388         ]
15389     },
15390     
15391     listItemCheckbox : {
15392         tag: 'li',
15393         cls: 'list-group-item',
15394         cn: [
15395             {
15396                 tag: 'span',
15397                 cls: 'roo-combobox-list-group-item-value'
15398             },
15399             {
15400                 tag: 'div',
15401                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15402                 cn: [
15403                     {
15404                         tag: 'input',
15405                         type: 'checkbox'
15406                     },
15407                     {
15408                         tag: 'label'
15409                     }
15410                 ]
15411             }
15412         ]
15413     },
15414     
15415     emptyResult : {
15416         tag: 'div',
15417         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15418     },
15419     
15420     footer : {
15421         tag: 'div',
15422         cls: 'modal-footer',
15423         cn: [
15424             {
15425                 tag: 'div',
15426                 cls: 'row',
15427                 cn: [
15428                     {
15429                         tag: 'div',
15430                         cls: 'col-xs-6 text-left',
15431                         cn: {
15432                             tag: 'button',
15433                             cls: 'btn btn-danger roo-touch-view-cancel',
15434                             html: 'Cancel'
15435                         }
15436                     },
15437                     {
15438                         tag: 'div',
15439                         cls: 'col-xs-6 text-right',
15440                         cn: {
15441                             tag: 'button',
15442                             cls: 'btn btn-success roo-touch-view-ok',
15443                             html: 'OK'
15444                         }
15445                     }
15446                 ]
15447             }
15448         ]
15449         
15450     }
15451 });
15452
15453 Roo.apply(Roo.bootstrap.ComboBox,  {
15454     
15455     touchViewTemplate : {
15456         tag: 'div',
15457         cls: 'modal fade roo-combobox-touch-view',
15458         cn: [
15459             {
15460                 tag: 'div',
15461                 cls: 'modal-dialog',
15462                 style : 'position:fixed', // we have to fix position....
15463                 cn: [
15464                     {
15465                         tag: 'div',
15466                         cls: 'modal-content',
15467                         cn: [
15468                             Roo.bootstrap.ComboBox.header,
15469                             Roo.bootstrap.ComboBox.body,
15470                             Roo.bootstrap.ComboBox.footer
15471                         ]
15472                     }
15473                 ]
15474             }
15475         ]
15476     }
15477 });/*
15478  * Based on:
15479  * Ext JS Library 1.1.1
15480  * Copyright(c) 2006-2007, Ext JS, LLC.
15481  *
15482  * Originally Released Under LGPL - original licence link has changed is not relivant.
15483  *
15484  * Fork - LGPL
15485  * <script type="text/javascript">
15486  */
15487
15488 /**
15489  * @class Roo.View
15490  * @extends Roo.util.Observable
15491  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15492  * This class also supports single and multi selection modes. <br>
15493  * Create a data model bound view:
15494  <pre><code>
15495  var store = new Roo.data.Store(...);
15496
15497  var view = new Roo.View({
15498     el : "my-element",
15499     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15500  
15501     singleSelect: true,
15502     selectedClass: "ydataview-selected",
15503     store: store
15504  });
15505
15506  // listen for node click?
15507  view.on("click", function(vw, index, node, e){
15508  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15509  });
15510
15511  // load XML data
15512  dataModel.load("foobar.xml");
15513  </code></pre>
15514  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15515  * <br><br>
15516  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15517  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15518  * 
15519  * Note: old style constructor is still suported (container, template, config)
15520  * 
15521  * @constructor
15522  * Create a new View
15523  * @param {Object} config The config object
15524  * 
15525  */
15526 Roo.View = function(config, depreciated_tpl, depreciated_config){
15527     
15528     this.parent = false;
15529     
15530     if (typeof(depreciated_tpl) == 'undefined') {
15531         // new way.. - universal constructor.
15532         Roo.apply(this, config);
15533         this.el  = Roo.get(this.el);
15534     } else {
15535         // old format..
15536         this.el  = Roo.get(config);
15537         this.tpl = depreciated_tpl;
15538         Roo.apply(this, depreciated_config);
15539     }
15540     this.wrapEl  = this.el.wrap().wrap();
15541     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15542     
15543     
15544     if(typeof(this.tpl) == "string"){
15545         this.tpl = new Roo.Template(this.tpl);
15546     } else {
15547         // support xtype ctors..
15548         this.tpl = new Roo.factory(this.tpl, Roo);
15549     }
15550     
15551     
15552     this.tpl.compile();
15553     
15554     /** @private */
15555     this.addEvents({
15556         /**
15557          * @event beforeclick
15558          * Fires before a click is processed. Returns false to cancel the default action.
15559          * @param {Roo.View} this
15560          * @param {Number} index The index of the target node
15561          * @param {HTMLElement} node The target node
15562          * @param {Roo.EventObject} e The raw event object
15563          */
15564             "beforeclick" : true,
15565         /**
15566          * @event click
15567          * Fires when a template node is clicked.
15568          * @param {Roo.View} this
15569          * @param {Number} index The index of the target node
15570          * @param {HTMLElement} node The target node
15571          * @param {Roo.EventObject} e The raw event object
15572          */
15573             "click" : true,
15574         /**
15575          * @event dblclick
15576          * Fires when a template node is double clicked.
15577          * @param {Roo.View} this
15578          * @param {Number} index The index of the target node
15579          * @param {HTMLElement} node The target node
15580          * @param {Roo.EventObject} e The raw event object
15581          */
15582             "dblclick" : true,
15583         /**
15584          * @event contextmenu
15585          * Fires when a template node is right clicked.
15586          * @param {Roo.View} this
15587          * @param {Number} index The index of the target node
15588          * @param {HTMLElement} node The target node
15589          * @param {Roo.EventObject} e The raw event object
15590          */
15591             "contextmenu" : true,
15592         /**
15593          * @event selectionchange
15594          * Fires when the selected nodes change.
15595          * @param {Roo.View} this
15596          * @param {Array} selections Array of the selected nodes
15597          */
15598             "selectionchange" : true,
15599     
15600         /**
15601          * @event beforeselect
15602          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15603          * @param {Roo.View} this
15604          * @param {HTMLElement} node The node to be selected
15605          * @param {Array} selections Array of currently selected nodes
15606          */
15607             "beforeselect" : true,
15608         /**
15609          * @event preparedata
15610          * Fires on every row to render, to allow you to change the data.
15611          * @param {Roo.View} this
15612          * @param {Object} data to be rendered (change this)
15613          */
15614           "preparedata" : true
15615           
15616           
15617         });
15618
15619
15620
15621     this.el.on({
15622         "click": this.onClick,
15623         "dblclick": this.onDblClick,
15624         "contextmenu": this.onContextMenu,
15625         scope:this
15626     });
15627
15628     this.selections = [];
15629     this.nodes = [];
15630     this.cmp = new Roo.CompositeElementLite([]);
15631     if(this.store){
15632         this.store = Roo.factory(this.store, Roo.data);
15633         this.setStore(this.store, true);
15634     }
15635     
15636     if ( this.footer && this.footer.xtype) {
15637            
15638          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15639         
15640         this.footer.dataSource = this.store;
15641         this.footer.container = fctr;
15642         this.footer = Roo.factory(this.footer, Roo);
15643         fctr.insertFirst(this.el);
15644         
15645         // this is a bit insane - as the paging toolbar seems to detach the el..
15646 //        dom.parentNode.parentNode.parentNode
15647          // they get detached?
15648     }
15649     
15650     
15651     Roo.View.superclass.constructor.call(this);
15652     
15653     
15654 };
15655
15656 Roo.extend(Roo.View, Roo.util.Observable, {
15657     
15658      /**
15659      * @cfg {Roo.data.Store} store Data store to load data from.
15660      */
15661     store : false,
15662     
15663     /**
15664      * @cfg {String|Roo.Element} el The container element.
15665      */
15666     el : '',
15667     
15668     /**
15669      * @cfg {String|Roo.Template} tpl The template used by this View 
15670      */
15671     tpl : false,
15672     /**
15673      * @cfg {String} dataName the named area of the template to use as the data area
15674      *                          Works with domtemplates roo-name="name"
15675      */
15676     dataName: false,
15677     /**
15678      * @cfg {String} selectedClass The css class to add to selected nodes
15679      */
15680     selectedClass : "x-view-selected",
15681      /**
15682      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15683      */
15684     emptyText : "",
15685     
15686     /**
15687      * @cfg {String} text to display on mask (default Loading)
15688      */
15689     mask : false,
15690     /**
15691      * @cfg {Boolean} multiSelect Allow multiple selection
15692      */
15693     multiSelect : false,
15694     /**
15695      * @cfg {Boolean} singleSelect Allow single selection
15696      */
15697     singleSelect:  false,
15698     
15699     /**
15700      * @cfg {Boolean} toggleSelect - selecting 
15701      */
15702     toggleSelect : false,
15703     
15704     /**
15705      * @cfg {Boolean} tickable - selecting 
15706      */
15707     tickable : false,
15708     
15709     /**
15710      * Returns the element this view is bound to.
15711      * @return {Roo.Element}
15712      */
15713     getEl : function(){
15714         return this.wrapEl;
15715     },
15716     
15717     
15718
15719     /**
15720      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15721      */
15722     refresh : function(){
15723         //Roo.log('refresh');
15724         var t = this.tpl;
15725         
15726         // if we are using something like 'domtemplate', then
15727         // the what gets used is:
15728         // t.applySubtemplate(NAME, data, wrapping data..)
15729         // the outer template then get' applied with
15730         //     the store 'extra data'
15731         // and the body get's added to the
15732         //      roo-name="data" node?
15733         //      <span class='roo-tpl-{name}'></span> ?????
15734         
15735         
15736         
15737         this.clearSelections();
15738         this.el.update("");
15739         var html = [];
15740         var records = this.store.getRange();
15741         if(records.length < 1) {
15742             
15743             // is this valid??  = should it render a template??
15744             
15745             this.el.update(this.emptyText);
15746             return;
15747         }
15748         var el = this.el;
15749         if (this.dataName) {
15750             this.el.update(t.apply(this.store.meta)); //????
15751             el = this.el.child('.roo-tpl-' + this.dataName);
15752         }
15753         
15754         for(var i = 0, len = records.length; i < len; i++){
15755             var data = this.prepareData(records[i].data, i, records[i]);
15756             this.fireEvent("preparedata", this, data, i, records[i]);
15757             
15758             var d = Roo.apply({}, data);
15759             
15760             if(this.tickable){
15761                 Roo.apply(d, {'roo-id' : Roo.id()});
15762                 
15763                 var _this = this;
15764             
15765                 Roo.each(this.parent.item, function(item){
15766                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15767                         return;
15768                     }
15769                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15770                 });
15771             }
15772             
15773             html[html.length] = Roo.util.Format.trim(
15774                 this.dataName ?
15775                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15776                     t.apply(d)
15777             );
15778         }
15779         
15780         
15781         
15782         el.update(html.join(""));
15783         this.nodes = el.dom.childNodes;
15784         this.updateIndexes(0);
15785     },
15786     
15787
15788     /**
15789      * Function to override to reformat the data that is sent to
15790      * the template for each node.
15791      * DEPRICATED - use the preparedata event handler.
15792      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15793      * a JSON object for an UpdateManager bound view).
15794      */
15795     prepareData : function(data, index, record)
15796     {
15797         this.fireEvent("preparedata", this, data, index, record);
15798         return data;
15799     },
15800
15801     onUpdate : function(ds, record){
15802         // Roo.log('on update');   
15803         this.clearSelections();
15804         var index = this.store.indexOf(record);
15805         var n = this.nodes[index];
15806         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15807         n.parentNode.removeChild(n);
15808         this.updateIndexes(index, index);
15809     },
15810
15811     
15812     
15813 // --------- FIXME     
15814     onAdd : function(ds, records, index)
15815     {
15816         //Roo.log(['on Add', ds, records, index] );        
15817         this.clearSelections();
15818         if(this.nodes.length == 0){
15819             this.refresh();
15820             return;
15821         }
15822         var n = this.nodes[index];
15823         for(var i = 0, len = records.length; i < len; i++){
15824             var d = this.prepareData(records[i].data, i, records[i]);
15825             if(n){
15826                 this.tpl.insertBefore(n, d);
15827             }else{
15828                 
15829                 this.tpl.append(this.el, d);
15830             }
15831         }
15832         this.updateIndexes(index);
15833     },
15834
15835     onRemove : function(ds, record, index){
15836        // Roo.log('onRemove');
15837         this.clearSelections();
15838         var el = this.dataName  ?
15839             this.el.child('.roo-tpl-' + this.dataName) :
15840             this.el; 
15841         
15842         el.dom.removeChild(this.nodes[index]);
15843         this.updateIndexes(index);
15844     },
15845
15846     /**
15847      * Refresh an individual node.
15848      * @param {Number} index
15849      */
15850     refreshNode : function(index){
15851         this.onUpdate(this.store, this.store.getAt(index));
15852     },
15853
15854     updateIndexes : function(startIndex, endIndex){
15855         var ns = this.nodes;
15856         startIndex = startIndex || 0;
15857         endIndex = endIndex || ns.length - 1;
15858         for(var i = startIndex; i <= endIndex; i++){
15859             ns[i].nodeIndex = i;
15860         }
15861     },
15862
15863     /**
15864      * Changes the data store this view uses and refresh the view.
15865      * @param {Store} store
15866      */
15867     setStore : function(store, initial){
15868         if(!initial && this.store){
15869             this.store.un("datachanged", this.refresh);
15870             this.store.un("add", this.onAdd);
15871             this.store.un("remove", this.onRemove);
15872             this.store.un("update", this.onUpdate);
15873             this.store.un("clear", this.refresh);
15874             this.store.un("beforeload", this.onBeforeLoad);
15875             this.store.un("load", this.onLoad);
15876             this.store.un("loadexception", this.onLoad);
15877         }
15878         if(store){
15879           
15880             store.on("datachanged", this.refresh, this);
15881             store.on("add", this.onAdd, this);
15882             store.on("remove", this.onRemove, this);
15883             store.on("update", this.onUpdate, this);
15884             store.on("clear", this.refresh, this);
15885             store.on("beforeload", this.onBeforeLoad, this);
15886             store.on("load", this.onLoad, this);
15887             store.on("loadexception", this.onLoad, this);
15888         }
15889         
15890         if(store){
15891             this.refresh();
15892         }
15893     },
15894     /**
15895      * onbeforeLoad - masks the loading area.
15896      *
15897      */
15898     onBeforeLoad : function(store,opts)
15899     {
15900          //Roo.log('onBeforeLoad');   
15901         if (!opts.add) {
15902             this.el.update("");
15903         }
15904         this.el.mask(this.mask ? this.mask : "Loading" ); 
15905     },
15906     onLoad : function ()
15907     {
15908         this.el.unmask();
15909     },
15910     
15911
15912     /**
15913      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15914      * @param {HTMLElement} node
15915      * @return {HTMLElement} The template node
15916      */
15917     findItemFromChild : function(node){
15918         var el = this.dataName  ?
15919             this.el.child('.roo-tpl-' + this.dataName,true) :
15920             this.el.dom; 
15921         
15922         if(!node || node.parentNode == el){
15923                     return node;
15924             }
15925             var p = node.parentNode;
15926             while(p && p != el){
15927             if(p.parentNode == el){
15928                 return p;
15929             }
15930             p = p.parentNode;
15931         }
15932             return null;
15933     },
15934
15935     /** @ignore */
15936     onClick : function(e){
15937         var item = this.findItemFromChild(e.getTarget());
15938         if(item){
15939             var index = this.indexOf(item);
15940             if(this.onItemClick(item, index, e) !== false){
15941                 this.fireEvent("click", this, index, item, e);
15942             }
15943         }else{
15944             this.clearSelections();
15945         }
15946     },
15947
15948     /** @ignore */
15949     onContextMenu : function(e){
15950         var item = this.findItemFromChild(e.getTarget());
15951         if(item){
15952             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15953         }
15954     },
15955
15956     /** @ignore */
15957     onDblClick : function(e){
15958         var item = this.findItemFromChild(e.getTarget());
15959         if(item){
15960             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15961         }
15962     },
15963
15964     onItemClick : function(item, index, e)
15965     {
15966         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15967             return false;
15968         }
15969         if (this.toggleSelect) {
15970             var m = this.isSelected(item) ? 'unselect' : 'select';
15971             //Roo.log(m);
15972             var _t = this;
15973             _t[m](item, true, false);
15974             return true;
15975         }
15976         if(this.multiSelect || this.singleSelect){
15977             if(this.multiSelect && e.shiftKey && this.lastSelection){
15978                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15979             }else{
15980                 this.select(item, this.multiSelect && e.ctrlKey);
15981                 this.lastSelection = item;
15982             }
15983             
15984             if(!this.tickable){
15985                 e.preventDefault();
15986             }
15987             
15988         }
15989         return true;
15990     },
15991
15992     /**
15993      * Get the number of selected nodes.
15994      * @return {Number}
15995      */
15996     getSelectionCount : function(){
15997         return this.selections.length;
15998     },
15999
16000     /**
16001      * Get the currently selected nodes.
16002      * @return {Array} An array of HTMLElements
16003      */
16004     getSelectedNodes : function(){
16005         return this.selections;
16006     },
16007
16008     /**
16009      * Get the indexes of the selected nodes.
16010      * @return {Array}
16011      */
16012     getSelectedIndexes : function(){
16013         var indexes = [], s = this.selections;
16014         for(var i = 0, len = s.length; i < len; i++){
16015             indexes.push(s[i].nodeIndex);
16016         }
16017         return indexes;
16018     },
16019
16020     /**
16021      * Clear all selections
16022      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16023      */
16024     clearSelections : function(suppressEvent){
16025         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16026             this.cmp.elements = this.selections;
16027             this.cmp.removeClass(this.selectedClass);
16028             this.selections = [];
16029             if(!suppressEvent){
16030                 this.fireEvent("selectionchange", this, this.selections);
16031             }
16032         }
16033     },
16034
16035     /**
16036      * Returns true if the passed node is selected
16037      * @param {HTMLElement/Number} node The node or node index
16038      * @return {Boolean}
16039      */
16040     isSelected : function(node){
16041         var s = this.selections;
16042         if(s.length < 1){
16043             return false;
16044         }
16045         node = this.getNode(node);
16046         return s.indexOf(node) !== -1;
16047     },
16048
16049     /**
16050      * Selects nodes.
16051      * @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
16052      * @param {Boolean} keepExisting (optional) true to keep existing selections
16053      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16054      */
16055     select : function(nodeInfo, keepExisting, suppressEvent){
16056         if(nodeInfo instanceof Array){
16057             if(!keepExisting){
16058                 this.clearSelections(true);
16059             }
16060             for(var i = 0, len = nodeInfo.length; i < len; i++){
16061                 this.select(nodeInfo[i], true, true);
16062             }
16063             return;
16064         } 
16065         var node = this.getNode(nodeInfo);
16066         if(!node || this.isSelected(node)){
16067             return; // already selected.
16068         }
16069         if(!keepExisting){
16070             this.clearSelections(true);
16071         }
16072         
16073         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16074             Roo.fly(node).addClass(this.selectedClass);
16075             this.selections.push(node);
16076             if(!suppressEvent){
16077                 this.fireEvent("selectionchange", this, this.selections);
16078             }
16079         }
16080         
16081         
16082     },
16083       /**
16084      * Unselects nodes.
16085      * @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
16086      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16087      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16088      */
16089     unselect : function(nodeInfo, keepExisting, suppressEvent)
16090     {
16091         if(nodeInfo instanceof Array){
16092             Roo.each(this.selections, function(s) {
16093                 this.unselect(s, nodeInfo);
16094             }, this);
16095             return;
16096         }
16097         var node = this.getNode(nodeInfo);
16098         if(!node || !this.isSelected(node)){
16099             //Roo.log("not selected");
16100             return; // not selected.
16101         }
16102         // fireevent???
16103         var ns = [];
16104         Roo.each(this.selections, function(s) {
16105             if (s == node ) {
16106                 Roo.fly(node).removeClass(this.selectedClass);
16107
16108                 return;
16109             }
16110             ns.push(s);
16111         },this);
16112         
16113         this.selections= ns;
16114         this.fireEvent("selectionchange", this, this.selections);
16115     },
16116
16117     /**
16118      * Gets a template node.
16119      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16120      * @return {HTMLElement} The node or null if it wasn't found
16121      */
16122     getNode : function(nodeInfo){
16123         if(typeof nodeInfo == "string"){
16124             return document.getElementById(nodeInfo);
16125         }else if(typeof nodeInfo == "number"){
16126             return this.nodes[nodeInfo];
16127         }
16128         return nodeInfo;
16129     },
16130
16131     /**
16132      * Gets a range template nodes.
16133      * @param {Number} startIndex
16134      * @param {Number} endIndex
16135      * @return {Array} An array of nodes
16136      */
16137     getNodes : function(start, end){
16138         var ns = this.nodes;
16139         start = start || 0;
16140         end = typeof end == "undefined" ? ns.length - 1 : end;
16141         var nodes = [];
16142         if(start <= end){
16143             for(var i = start; i <= end; i++){
16144                 nodes.push(ns[i]);
16145             }
16146         } else{
16147             for(var i = start; i >= end; i--){
16148                 nodes.push(ns[i]);
16149             }
16150         }
16151         return nodes;
16152     },
16153
16154     /**
16155      * Finds the index of the passed node
16156      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16157      * @return {Number} The index of the node or -1
16158      */
16159     indexOf : function(node){
16160         node = this.getNode(node);
16161         if(typeof node.nodeIndex == "number"){
16162             return node.nodeIndex;
16163         }
16164         var ns = this.nodes;
16165         for(var i = 0, len = ns.length; i < len; i++){
16166             if(ns[i] == node){
16167                 return i;
16168             }
16169         }
16170         return -1;
16171     }
16172 });
16173 /*
16174  * - LGPL
16175  *
16176  * based on jquery fullcalendar
16177  * 
16178  */
16179
16180 Roo.bootstrap = Roo.bootstrap || {};
16181 /**
16182  * @class Roo.bootstrap.Calendar
16183  * @extends Roo.bootstrap.Component
16184  * Bootstrap Calendar class
16185  * @cfg {Boolean} loadMask (true|false) default false
16186  * @cfg {Object} header generate the user specific header of the calendar, default false
16187
16188  * @constructor
16189  * Create a new Container
16190  * @param {Object} config The config object
16191  */
16192
16193
16194
16195 Roo.bootstrap.Calendar = function(config){
16196     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16197      this.addEvents({
16198         /**
16199              * @event select
16200              * Fires when a date is selected
16201              * @param {DatePicker} this
16202              * @param {Date} date The selected date
16203              */
16204         'select': true,
16205         /**
16206              * @event monthchange
16207              * Fires when the displayed month changes 
16208              * @param {DatePicker} this
16209              * @param {Date} date The selected month
16210              */
16211         'monthchange': true,
16212         /**
16213              * @event evententer
16214              * Fires when mouse over an event
16215              * @param {Calendar} this
16216              * @param {event} Event
16217              */
16218         'evententer': true,
16219         /**
16220              * @event eventleave
16221              * Fires when the mouse leaves an
16222              * @param {Calendar} this
16223              * @param {event}
16224              */
16225         'eventleave': true,
16226         /**
16227              * @event eventclick
16228              * Fires when the mouse click an
16229              * @param {Calendar} this
16230              * @param {event}
16231              */
16232         'eventclick': true
16233         
16234     });
16235
16236 };
16237
16238 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16239     
16240      /**
16241      * @cfg {Number} startDay
16242      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16243      */
16244     startDay : 0,
16245     
16246     loadMask : false,
16247     
16248     header : false,
16249       
16250     getAutoCreate : function(){
16251         
16252         
16253         var fc_button = function(name, corner, style, content ) {
16254             return Roo.apply({},{
16255                 tag : 'span',
16256                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16257                          (corner.length ?
16258                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16259                             ''
16260                         ),
16261                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16262                 unselectable: 'on'
16263             });
16264         };
16265         
16266         var header = {};
16267         
16268         if(!this.header){
16269             header = {
16270                 tag : 'table',
16271                 cls : 'fc-header',
16272                 style : 'width:100%',
16273                 cn : [
16274                     {
16275                         tag: 'tr',
16276                         cn : [
16277                             {
16278                                 tag : 'td',
16279                                 cls : 'fc-header-left',
16280                                 cn : [
16281                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16282                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16283                                     { tag: 'span', cls: 'fc-header-space' },
16284                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16285
16286
16287                                 ]
16288                             },
16289
16290                             {
16291                                 tag : 'td',
16292                                 cls : 'fc-header-center',
16293                                 cn : [
16294                                     {
16295                                         tag: 'span',
16296                                         cls: 'fc-header-title',
16297                                         cn : {
16298                                             tag: 'H2',
16299                                             html : 'month / year'
16300                                         }
16301                                     }
16302
16303                                 ]
16304                             },
16305                             {
16306                                 tag : 'td',
16307                                 cls : 'fc-header-right',
16308                                 cn : [
16309                               /*      fc_button('month', 'left', '', 'month' ),
16310                                     fc_button('week', '', '', 'week' ),
16311                                     fc_button('day', 'right', '', 'day' )
16312                                 */    
16313
16314                                 ]
16315                             }
16316
16317                         ]
16318                     }
16319                 ]
16320             };
16321         }
16322         
16323         header = this.header;
16324         
16325        
16326         var cal_heads = function() {
16327             var ret = [];
16328             // fixme - handle this.
16329             
16330             for (var i =0; i < Date.dayNames.length; i++) {
16331                 var d = Date.dayNames[i];
16332                 ret.push({
16333                     tag: 'th',
16334                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16335                     html : d.substring(0,3)
16336                 });
16337                 
16338             }
16339             ret[0].cls += ' fc-first';
16340             ret[6].cls += ' fc-last';
16341             return ret;
16342         };
16343         var cal_cell = function(n) {
16344             return  {
16345                 tag: 'td',
16346                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16347                 cn : [
16348                     {
16349                         cn : [
16350                             {
16351                                 cls: 'fc-day-number',
16352                                 html: 'D'
16353                             },
16354                             {
16355                                 cls: 'fc-day-content',
16356                              
16357                                 cn : [
16358                                      {
16359                                         style: 'position: relative;' // height: 17px;
16360                                     }
16361                                 ]
16362                             }
16363                             
16364                             
16365                         ]
16366                     }
16367                 ]
16368                 
16369             }
16370         };
16371         var cal_rows = function() {
16372             
16373             var ret = [];
16374             for (var r = 0; r < 6; r++) {
16375                 var row= {
16376                     tag : 'tr',
16377                     cls : 'fc-week',
16378                     cn : []
16379                 };
16380                 
16381                 for (var i =0; i < Date.dayNames.length; i++) {
16382                     var d = Date.dayNames[i];
16383                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16384
16385                 }
16386                 row.cn[0].cls+=' fc-first';
16387                 row.cn[0].cn[0].style = 'min-height:90px';
16388                 row.cn[6].cls+=' fc-last';
16389                 ret.push(row);
16390                 
16391             }
16392             ret[0].cls += ' fc-first';
16393             ret[4].cls += ' fc-prev-last';
16394             ret[5].cls += ' fc-last';
16395             return ret;
16396             
16397         };
16398         
16399         var cal_table = {
16400             tag: 'table',
16401             cls: 'fc-border-separate',
16402             style : 'width:100%',
16403             cellspacing  : 0,
16404             cn : [
16405                 { 
16406                     tag: 'thead',
16407                     cn : [
16408                         { 
16409                             tag: 'tr',
16410                             cls : 'fc-first fc-last',
16411                             cn : cal_heads()
16412                         }
16413                     ]
16414                 },
16415                 { 
16416                     tag: 'tbody',
16417                     cn : cal_rows()
16418                 }
16419                   
16420             ]
16421         };
16422          
16423          var cfg = {
16424             cls : 'fc fc-ltr',
16425             cn : [
16426                 header,
16427                 {
16428                     cls : 'fc-content',
16429                     style : "position: relative;",
16430                     cn : [
16431                         {
16432                             cls : 'fc-view fc-view-month fc-grid',
16433                             style : 'position: relative',
16434                             unselectable : 'on',
16435                             cn : [
16436                                 {
16437                                     cls : 'fc-event-container',
16438                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16439                                 },
16440                                 cal_table
16441                             ]
16442                         }
16443                     ]
16444     
16445                 }
16446            ] 
16447             
16448         };
16449         
16450          
16451         
16452         return cfg;
16453     },
16454     
16455     
16456     initEvents : function()
16457     {
16458         if(!this.store){
16459             throw "can not find store for calendar";
16460         }
16461         
16462         var mark = {
16463             tag: "div",
16464             cls:"x-dlg-mask",
16465             style: "text-align:center",
16466             cn: [
16467                 {
16468                     tag: "div",
16469                     style: "background-color:white;width:50%;margin:250 auto",
16470                     cn: [
16471                         {
16472                             tag: "img",
16473                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16474                         },
16475                         {
16476                             tag: "span",
16477                             html: "Loading"
16478                         }
16479                         
16480                     ]
16481                 }
16482             ]
16483         };
16484         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16485         
16486         var size = this.el.select('.fc-content', true).first().getSize();
16487         this.maskEl.setSize(size.width, size.height);
16488         this.maskEl.enableDisplayMode("block");
16489         if(!this.loadMask){
16490             this.maskEl.hide();
16491         }
16492         
16493         this.store = Roo.factory(this.store, Roo.data);
16494         this.store.on('load', this.onLoad, this);
16495         this.store.on('beforeload', this.onBeforeLoad, this);
16496         
16497         this.resize();
16498         
16499         this.cells = this.el.select('.fc-day',true);
16500         //Roo.log(this.cells);
16501         this.textNodes = this.el.query('.fc-day-number');
16502         this.cells.addClassOnOver('fc-state-hover');
16503         
16504         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16505         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16506         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16507         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16508         
16509         this.on('monthchange', this.onMonthChange, this);
16510         
16511         this.update(new Date().clearTime());
16512     },
16513     
16514     resize : function() {
16515         var sz  = this.el.getSize();
16516         
16517         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16518         this.el.select('.fc-day-content div',true).setHeight(34);
16519     },
16520     
16521     
16522     // private
16523     showPrevMonth : function(e){
16524         this.update(this.activeDate.add("mo", -1));
16525     },
16526     showToday : function(e){
16527         this.update(new Date().clearTime());
16528     },
16529     // private
16530     showNextMonth : function(e){
16531         this.update(this.activeDate.add("mo", 1));
16532     },
16533
16534     // private
16535     showPrevYear : function(){
16536         this.update(this.activeDate.add("y", -1));
16537     },
16538
16539     // private
16540     showNextYear : function(){
16541         this.update(this.activeDate.add("y", 1));
16542     },
16543
16544     
16545    // private
16546     update : function(date)
16547     {
16548         var vd = this.activeDate;
16549         this.activeDate = date;
16550 //        if(vd && this.el){
16551 //            var t = date.getTime();
16552 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16553 //                Roo.log('using add remove');
16554 //                
16555 //                this.fireEvent('monthchange', this, date);
16556 //                
16557 //                this.cells.removeClass("fc-state-highlight");
16558 //                this.cells.each(function(c){
16559 //                   if(c.dateValue == t){
16560 //                       c.addClass("fc-state-highlight");
16561 //                       setTimeout(function(){
16562 //                            try{c.dom.firstChild.focus();}catch(e){}
16563 //                       }, 50);
16564 //                       return false;
16565 //                   }
16566 //                   return true;
16567 //                });
16568 //                return;
16569 //            }
16570 //        }
16571         
16572         var days = date.getDaysInMonth();
16573         
16574         var firstOfMonth = date.getFirstDateOfMonth();
16575         var startingPos = firstOfMonth.getDay()-this.startDay;
16576         
16577         if(startingPos < this.startDay){
16578             startingPos += 7;
16579         }
16580         
16581         var pm = date.add(Date.MONTH, -1);
16582         var prevStart = pm.getDaysInMonth()-startingPos;
16583 //        
16584         this.cells = this.el.select('.fc-day',true);
16585         this.textNodes = this.el.query('.fc-day-number');
16586         this.cells.addClassOnOver('fc-state-hover');
16587         
16588         var cells = this.cells.elements;
16589         var textEls = this.textNodes;
16590         
16591         Roo.each(cells, function(cell){
16592             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16593         });
16594         
16595         days += startingPos;
16596
16597         // convert everything to numbers so it's fast
16598         var day = 86400000;
16599         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16600         //Roo.log(d);
16601         //Roo.log(pm);
16602         //Roo.log(prevStart);
16603         
16604         var today = new Date().clearTime().getTime();
16605         var sel = date.clearTime().getTime();
16606         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16607         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16608         var ddMatch = this.disabledDatesRE;
16609         var ddText = this.disabledDatesText;
16610         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16611         var ddaysText = this.disabledDaysText;
16612         var format = this.format;
16613         
16614         var setCellClass = function(cal, cell){
16615             cell.row = 0;
16616             cell.events = [];
16617             cell.more = [];
16618             //Roo.log('set Cell Class');
16619             cell.title = "";
16620             var t = d.getTime();
16621             
16622             //Roo.log(d);
16623             
16624             cell.dateValue = t;
16625             if(t == today){
16626                 cell.className += " fc-today";
16627                 cell.className += " fc-state-highlight";
16628                 cell.title = cal.todayText;
16629             }
16630             if(t == sel){
16631                 // disable highlight in other month..
16632                 //cell.className += " fc-state-highlight";
16633                 
16634             }
16635             // disabling
16636             if(t < min) {
16637                 cell.className = " fc-state-disabled";
16638                 cell.title = cal.minText;
16639                 return;
16640             }
16641             if(t > max) {
16642                 cell.className = " fc-state-disabled";
16643                 cell.title = cal.maxText;
16644                 return;
16645             }
16646             if(ddays){
16647                 if(ddays.indexOf(d.getDay()) != -1){
16648                     cell.title = ddaysText;
16649                     cell.className = " fc-state-disabled";
16650                 }
16651             }
16652             if(ddMatch && format){
16653                 var fvalue = d.dateFormat(format);
16654                 if(ddMatch.test(fvalue)){
16655                     cell.title = ddText.replace("%0", fvalue);
16656                     cell.className = " fc-state-disabled";
16657                 }
16658             }
16659             
16660             if (!cell.initialClassName) {
16661                 cell.initialClassName = cell.dom.className;
16662             }
16663             
16664             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16665         };
16666
16667         var i = 0;
16668         
16669         for(; i < startingPos; i++) {
16670             textEls[i].innerHTML = (++prevStart);
16671             d.setDate(d.getDate()+1);
16672             
16673             cells[i].className = "fc-past fc-other-month";
16674             setCellClass(this, cells[i]);
16675         }
16676         
16677         var intDay = 0;
16678         
16679         for(; i < days; i++){
16680             intDay = i - startingPos + 1;
16681             textEls[i].innerHTML = (intDay);
16682             d.setDate(d.getDate()+1);
16683             
16684             cells[i].className = ''; // "x-date-active";
16685             setCellClass(this, cells[i]);
16686         }
16687         var extraDays = 0;
16688         
16689         for(; i < 42; i++) {
16690             textEls[i].innerHTML = (++extraDays);
16691             d.setDate(d.getDate()+1);
16692             
16693             cells[i].className = "fc-future fc-other-month";
16694             setCellClass(this, cells[i]);
16695         }
16696         
16697         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16698         
16699         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16700         
16701         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16702         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16703         
16704         if(totalRows != 6){
16705             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16706             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16707         }
16708         
16709         this.fireEvent('monthchange', this, date);
16710         
16711         
16712         /*
16713         if(!this.internalRender){
16714             var main = this.el.dom.firstChild;
16715             var w = main.offsetWidth;
16716             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16717             Roo.fly(main).setWidth(w);
16718             this.internalRender = true;
16719             // opera does not respect the auto grow header center column
16720             // then, after it gets a width opera refuses to recalculate
16721             // without a second pass
16722             if(Roo.isOpera && !this.secondPass){
16723                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16724                 this.secondPass = true;
16725                 this.update.defer(10, this, [date]);
16726             }
16727         }
16728         */
16729         
16730     },
16731     
16732     findCell : function(dt) {
16733         dt = dt.clearTime().getTime();
16734         var ret = false;
16735         this.cells.each(function(c){
16736             //Roo.log("check " +c.dateValue + '?=' + dt);
16737             if(c.dateValue == dt){
16738                 ret = c;
16739                 return false;
16740             }
16741             return true;
16742         });
16743         
16744         return ret;
16745     },
16746     
16747     findCells : function(ev) {
16748         var s = ev.start.clone().clearTime().getTime();
16749        // Roo.log(s);
16750         var e= ev.end.clone().clearTime().getTime();
16751        // Roo.log(e);
16752         var ret = [];
16753         this.cells.each(function(c){
16754              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16755             
16756             if(c.dateValue > e){
16757                 return ;
16758             }
16759             if(c.dateValue < s){
16760                 return ;
16761             }
16762             ret.push(c);
16763         });
16764         
16765         return ret;    
16766     },
16767     
16768 //    findBestRow: function(cells)
16769 //    {
16770 //        var ret = 0;
16771 //        
16772 //        for (var i =0 ; i < cells.length;i++) {
16773 //            ret  = Math.max(cells[i].rows || 0,ret);
16774 //        }
16775 //        return ret;
16776 //        
16777 //    },
16778     
16779     
16780     addItem : function(ev)
16781     {
16782         // look for vertical location slot in
16783         var cells = this.findCells(ev);
16784         
16785 //        ev.row = this.findBestRow(cells);
16786         
16787         // work out the location.
16788         
16789         var crow = false;
16790         var rows = [];
16791         for(var i =0; i < cells.length; i++) {
16792             
16793             cells[i].row = cells[0].row;
16794             
16795             if(i == 0){
16796                 cells[i].row = cells[i].row + 1;
16797             }
16798             
16799             if (!crow) {
16800                 crow = {
16801                     start : cells[i],
16802                     end :  cells[i]
16803                 };
16804                 continue;
16805             }
16806             if (crow.start.getY() == cells[i].getY()) {
16807                 // on same row.
16808                 crow.end = cells[i];
16809                 continue;
16810             }
16811             // different row.
16812             rows.push(crow);
16813             crow = {
16814                 start: cells[i],
16815                 end : cells[i]
16816             };
16817             
16818         }
16819         
16820         rows.push(crow);
16821         ev.els = [];
16822         ev.rows = rows;
16823         ev.cells = cells;
16824         
16825         cells[0].events.push(ev);
16826         
16827         this.calevents.push(ev);
16828     },
16829     
16830     clearEvents: function() {
16831         
16832         if(!this.calevents){
16833             return;
16834         }
16835         
16836         Roo.each(this.cells.elements, function(c){
16837             c.row = 0;
16838             c.events = [];
16839             c.more = [];
16840         });
16841         
16842         Roo.each(this.calevents, function(e) {
16843             Roo.each(e.els, function(el) {
16844                 el.un('mouseenter' ,this.onEventEnter, this);
16845                 el.un('mouseleave' ,this.onEventLeave, this);
16846                 el.remove();
16847             },this);
16848         },this);
16849         
16850         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16851             e.remove();
16852         });
16853         
16854     },
16855     
16856     renderEvents: function()
16857     {   
16858         var _this = this;
16859         
16860         this.cells.each(function(c) {
16861             
16862             if(c.row < 5){
16863                 return;
16864             }
16865             
16866             var ev = c.events;
16867             
16868             var r = 4;
16869             if(c.row != c.events.length){
16870                 r = 4 - (4 - (c.row - c.events.length));
16871             }
16872             
16873             c.events = ev.slice(0, r);
16874             c.more = ev.slice(r);
16875             
16876             if(c.more.length && c.more.length == 1){
16877                 c.events.push(c.more.pop());
16878             }
16879             
16880             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16881             
16882         });
16883             
16884         this.cells.each(function(c) {
16885             
16886             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16887             
16888             
16889             for (var e = 0; e < c.events.length; e++){
16890                 var ev = c.events[e];
16891                 var rows = ev.rows;
16892                 
16893                 for(var i = 0; i < rows.length; i++) {
16894                 
16895                     // how many rows should it span..
16896
16897                     var  cfg = {
16898                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16899                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16900
16901                         unselectable : "on",
16902                         cn : [
16903                             {
16904                                 cls: 'fc-event-inner',
16905                                 cn : [
16906     //                                {
16907     //                                  tag:'span',
16908     //                                  cls: 'fc-event-time',
16909     //                                  html : cells.length > 1 ? '' : ev.time
16910     //                                },
16911                                     {
16912                                       tag:'span',
16913                                       cls: 'fc-event-title',
16914                                       html : String.format('{0}', ev.title)
16915                                     }
16916
16917
16918                                 ]
16919                             },
16920                             {
16921                                 cls: 'ui-resizable-handle ui-resizable-e',
16922                                 html : '&nbsp;&nbsp;&nbsp'
16923                             }
16924
16925                         ]
16926                     };
16927
16928                     if (i == 0) {
16929                         cfg.cls += ' fc-event-start';
16930                     }
16931                     if ((i+1) == rows.length) {
16932                         cfg.cls += ' fc-event-end';
16933                     }
16934
16935                     var ctr = _this.el.select('.fc-event-container',true).first();
16936                     var cg = ctr.createChild(cfg);
16937
16938                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16939                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16940
16941                     var r = (c.more.length) ? 1 : 0;
16942                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16943                     cg.setWidth(ebox.right - sbox.x -2);
16944
16945                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16946                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16947                     cg.on('click', _this.onEventClick, _this, ev);
16948
16949                     ev.els.push(cg);
16950                     
16951                 }
16952                 
16953             }
16954             
16955             
16956             if(c.more.length){
16957                 var  cfg = {
16958                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16959                     style : 'position: absolute',
16960                     unselectable : "on",
16961                     cn : [
16962                         {
16963                             cls: 'fc-event-inner',
16964                             cn : [
16965                                 {
16966                                   tag:'span',
16967                                   cls: 'fc-event-title',
16968                                   html : 'More'
16969                                 }
16970
16971
16972                             ]
16973                         },
16974                         {
16975                             cls: 'ui-resizable-handle ui-resizable-e',
16976                             html : '&nbsp;&nbsp;&nbsp'
16977                         }
16978
16979                     ]
16980                 };
16981
16982                 var ctr = _this.el.select('.fc-event-container',true).first();
16983                 var cg = ctr.createChild(cfg);
16984
16985                 var sbox = c.select('.fc-day-content',true).first().getBox();
16986                 var ebox = c.select('.fc-day-content',true).first().getBox();
16987                 //Roo.log(cg);
16988                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16989                 cg.setWidth(ebox.right - sbox.x -2);
16990
16991                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16992                 
16993             }
16994             
16995         });
16996         
16997         
16998         
16999     },
17000     
17001     onEventEnter: function (e, el,event,d) {
17002         this.fireEvent('evententer', this, el, event);
17003     },
17004     
17005     onEventLeave: function (e, el,event,d) {
17006         this.fireEvent('eventleave', this, el, event);
17007     },
17008     
17009     onEventClick: function (e, el,event,d) {
17010         this.fireEvent('eventclick', this, el, event);
17011     },
17012     
17013     onMonthChange: function () {
17014         this.store.load();
17015     },
17016     
17017     onMoreEventClick: function(e, el, more)
17018     {
17019         var _this = this;
17020         
17021         this.calpopover.placement = 'right';
17022         this.calpopover.setTitle('More');
17023         
17024         this.calpopover.setContent('');
17025         
17026         var ctr = this.calpopover.el.select('.popover-content', true).first();
17027         
17028         Roo.each(more, function(m){
17029             var cfg = {
17030                 cls : 'fc-event-hori fc-event-draggable',
17031                 html : m.title
17032             };
17033             var cg = ctr.createChild(cfg);
17034             
17035             cg.on('click', _this.onEventClick, _this, m);
17036         });
17037         
17038         this.calpopover.show(el);
17039         
17040         
17041     },
17042     
17043     onLoad: function () 
17044     {   
17045         this.calevents = [];
17046         var cal = this;
17047         
17048         if(this.store.getCount() > 0){
17049             this.store.data.each(function(d){
17050                cal.addItem({
17051                     id : d.data.id,
17052                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17053                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17054                     time : d.data.start_time,
17055                     title : d.data.title,
17056                     description : d.data.description,
17057                     venue : d.data.venue
17058                 });
17059             });
17060         }
17061         
17062         this.renderEvents();
17063         
17064         if(this.calevents.length && this.loadMask){
17065             this.maskEl.hide();
17066         }
17067     },
17068     
17069     onBeforeLoad: function()
17070     {
17071         this.clearEvents();
17072         if(this.loadMask){
17073             this.maskEl.show();
17074         }
17075     }
17076 });
17077
17078  
17079  /*
17080  * - LGPL
17081  *
17082  * element
17083  * 
17084  */
17085
17086 /**
17087  * @class Roo.bootstrap.Popover
17088  * @extends Roo.bootstrap.Component
17089  * Bootstrap Popover class
17090  * @cfg {String} html contents of the popover   (or false to use children..)
17091  * @cfg {String} title of popover (or false to hide)
17092  * @cfg {String} placement how it is placed
17093  * @cfg {String} trigger click || hover (or false to trigger manually)
17094  * @cfg {String} over what (parent or false to trigger manually.)
17095  * @cfg {Number} delay - delay before showing
17096  
17097  * @constructor
17098  * Create a new Popover
17099  * @param {Object} config The config object
17100  */
17101
17102 Roo.bootstrap.Popover = function(config){
17103     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17104     
17105     this.addEvents({
17106         // raw events
17107          /**
17108          * @event show
17109          * After the popover show
17110          * 
17111          * @param {Roo.bootstrap.Popover} this
17112          */
17113         "show" : true,
17114         /**
17115          * @event hide
17116          * After the popover hide
17117          * 
17118          * @param {Roo.bootstrap.Popover} this
17119          */
17120         "hide" : true
17121     });
17122 };
17123
17124 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17125     
17126     title: 'Fill in a title',
17127     html: false,
17128     
17129     placement : 'right',
17130     trigger : 'hover', // hover
17131     
17132     delay : 0,
17133     
17134     over: 'parent',
17135     
17136     can_build_overlaid : false,
17137     
17138     getChildContainer : function()
17139     {
17140         return this.el.select('.popover-content',true).first();
17141     },
17142     
17143     getAutoCreate : function(){
17144          
17145         var cfg = {
17146            cls : 'popover roo-dynamic',
17147            style: 'display:block',
17148            cn : [
17149                 {
17150                     cls : 'arrow'
17151                 },
17152                 {
17153                     cls : 'popover-inner',
17154                     cn : [
17155                         {
17156                             tag: 'h3',
17157                             cls: 'popover-title',
17158                             html : this.title
17159                         },
17160                         {
17161                             cls : 'popover-content',
17162                             html : this.html
17163                         }
17164                     ]
17165                     
17166                 }
17167            ]
17168         };
17169         
17170         return cfg;
17171     },
17172     setTitle: function(str)
17173     {
17174         this.title = str;
17175         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17176     },
17177     setContent: function(str)
17178     {
17179         this.html = str;
17180         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17181     },
17182     // as it get's added to the bottom of the page.
17183     onRender : function(ct, position)
17184     {
17185         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17186         if(!this.el){
17187             var cfg = Roo.apply({},  this.getAutoCreate());
17188             cfg.id = Roo.id();
17189             
17190             if (this.cls) {
17191                 cfg.cls += ' ' + this.cls;
17192             }
17193             if (this.style) {
17194                 cfg.style = this.style;
17195             }
17196             //Roo.log("adding to ");
17197             this.el = Roo.get(document.body).createChild(cfg, position);
17198 //            Roo.log(this.el);
17199         }
17200         this.initEvents();
17201     },
17202     
17203     initEvents : function()
17204     {
17205         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17206         this.el.enableDisplayMode('block');
17207         this.el.hide();
17208         if (this.over === false) {
17209             return; 
17210         }
17211         if (this.triggers === false) {
17212             return;
17213         }
17214         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17215         var triggers = this.trigger ? this.trigger.split(' ') : [];
17216         Roo.each(triggers, function(trigger) {
17217         
17218             if (trigger == 'click') {
17219                 on_el.on('click', this.toggle, this);
17220             } else if (trigger != 'manual') {
17221                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17222                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17223       
17224                 on_el.on(eventIn  ,this.enter, this);
17225                 on_el.on(eventOut, this.leave, this);
17226             }
17227         }, this);
17228         
17229     },
17230     
17231     
17232     // private
17233     timeout : null,
17234     hoverState : null,
17235     
17236     toggle : function () {
17237         this.hoverState == 'in' ? this.leave() : this.enter();
17238     },
17239     
17240     enter : function () {
17241         
17242         clearTimeout(this.timeout);
17243     
17244         this.hoverState = 'in';
17245     
17246         if (!this.delay || !this.delay.show) {
17247             this.show();
17248             return;
17249         }
17250         var _t = this;
17251         this.timeout = setTimeout(function () {
17252             if (_t.hoverState == 'in') {
17253                 _t.show();
17254             }
17255         }, this.delay.show)
17256     },
17257     
17258     leave : function() {
17259         clearTimeout(this.timeout);
17260     
17261         this.hoverState = 'out';
17262     
17263         if (!this.delay || !this.delay.hide) {
17264             this.hide();
17265             return;
17266         }
17267         var _t = this;
17268         this.timeout = setTimeout(function () {
17269             if (_t.hoverState == 'out') {
17270                 _t.hide();
17271             }
17272         }, this.delay.hide)
17273     },
17274     
17275     show : function (on_el)
17276     {
17277         if (!on_el) {
17278             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17279         }
17280         
17281         // set content.
17282         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17283         if (this.html !== false) {
17284             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17285         }
17286         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17287         if (!this.title.length) {
17288             this.el.select('.popover-title',true).hide();
17289         }
17290         
17291         var placement = typeof this.placement == 'function' ?
17292             this.placement.call(this, this.el, on_el) :
17293             this.placement;
17294             
17295         var autoToken = /\s?auto?\s?/i;
17296         var autoPlace = autoToken.test(placement);
17297         if (autoPlace) {
17298             placement = placement.replace(autoToken, '') || 'top';
17299         }
17300         
17301         //this.el.detach()
17302         //this.el.setXY([0,0]);
17303         this.el.show();
17304         this.el.dom.style.display='block';
17305         this.el.addClass(placement);
17306         
17307         //this.el.appendTo(on_el);
17308         
17309         var p = this.getPosition();
17310         var box = this.el.getBox();
17311         
17312         if (autoPlace) {
17313             // fixme..
17314         }
17315         var align = Roo.bootstrap.Popover.alignment[placement];
17316         this.el.alignTo(on_el, align[0],align[1]);
17317         //var arrow = this.el.select('.arrow',true).first();
17318         //arrow.set(align[2], 
17319         
17320         this.el.addClass('in');
17321         
17322         
17323         if (this.el.hasClass('fade')) {
17324             // fade it?
17325         }
17326         
17327         this.hoverState = 'in';
17328         
17329         this.fireEvent('show', this);
17330         
17331     },
17332     hide : function()
17333     {
17334         this.el.setXY([0,0]);
17335         this.el.removeClass('in');
17336         this.el.hide();
17337         this.hoverState = null;
17338         
17339         this.fireEvent('hide', this);
17340     }
17341     
17342 });
17343
17344 Roo.bootstrap.Popover.alignment = {
17345     'left' : ['r-l', [-10,0], 'right'],
17346     'right' : ['l-r', [10,0], 'left'],
17347     'bottom' : ['t-b', [0,10], 'top'],
17348     'top' : [ 'b-t', [0,-10], 'bottom']
17349 };
17350
17351  /*
17352  * - LGPL
17353  *
17354  * Progress
17355  * 
17356  */
17357
17358 /**
17359  * @class Roo.bootstrap.Progress
17360  * @extends Roo.bootstrap.Component
17361  * Bootstrap Progress class
17362  * @cfg {Boolean} striped striped of the progress bar
17363  * @cfg {Boolean} active animated of the progress bar
17364  * 
17365  * 
17366  * @constructor
17367  * Create a new Progress
17368  * @param {Object} config The config object
17369  */
17370
17371 Roo.bootstrap.Progress = function(config){
17372     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17373 };
17374
17375 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17376     
17377     striped : false,
17378     active: false,
17379     
17380     getAutoCreate : function(){
17381         var cfg = {
17382             tag: 'div',
17383             cls: 'progress'
17384         };
17385         
17386         
17387         if(this.striped){
17388             cfg.cls += ' progress-striped';
17389         }
17390       
17391         if(this.active){
17392             cfg.cls += ' active';
17393         }
17394         
17395         
17396         return cfg;
17397     }
17398    
17399 });
17400
17401  
17402
17403  /*
17404  * - LGPL
17405  *
17406  * ProgressBar
17407  * 
17408  */
17409
17410 /**
17411  * @class Roo.bootstrap.ProgressBar
17412  * @extends Roo.bootstrap.Component
17413  * Bootstrap ProgressBar class
17414  * @cfg {Number} aria_valuenow aria-value now
17415  * @cfg {Number} aria_valuemin aria-value min
17416  * @cfg {Number} aria_valuemax aria-value max
17417  * @cfg {String} label label for the progress bar
17418  * @cfg {String} panel (success | info | warning | danger )
17419  * @cfg {String} role role of the progress bar
17420  * @cfg {String} sr_only text
17421  * 
17422  * 
17423  * @constructor
17424  * Create a new ProgressBar
17425  * @param {Object} config The config object
17426  */
17427
17428 Roo.bootstrap.ProgressBar = function(config){
17429     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17430 };
17431
17432 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17433     
17434     aria_valuenow : 0,
17435     aria_valuemin : 0,
17436     aria_valuemax : 100,
17437     label : false,
17438     panel : false,
17439     role : false,
17440     sr_only: false,
17441     
17442     getAutoCreate : function()
17443     {
17444         
17445         var cfg = {
17446             tag: 'div',
17447             cls: 'progress-bar',
17448             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17449         };
17450         
17451         if(this.sr_only){
17452             cfg.cn = {
17453                 tag: 'span',
17454                 cls: 'sr-only',
17455                 html: this.sr_only
17456             }
17457         }
17458         
17459         if(this.role){
17460             cfg.role = this.role;
17461         }
17462         
17463         if(this.aria_valuenow){
17464             cfg['aria-valuenow'] = this.aria_valuenow;
17465         }
17466         
17467         if(this.aria_valuemin){
17468             cfg['aria-valuemin'] = this.aria_valuemin;
17469         }
17470         
17471         if(this.aria_valuemax){
17472             cfg['aria-valuemax'] = this.aria_valuemax;
17473         }
17474         
17475         if(this.label && !this.sr_only){
17476             cfg.html = this.label;
17477         }
17478         
17479         if(this.panel){
17480             cfg.cls += ' progress-bar-' + this.panel;
17481         }
17482         
17483         return cfg;
17484     },
17485     
17486     update : function(aria_valuenow)
17487     {
17488         this.aria_valuenow = aria_valuenow;
17489         
17490         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17491     }
17492    
17493 });
17494
17495  
17496
17497  /*
17498  * - LGPL
17499  *
17500  * column
17501  * 
17502  */
17503
17504 /**
17505  * @class Roo.bootstrap.TabGroup
17506  * @extends Roo.bootstrap.Column
17507  * Bootstrap Column class
17508  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17509  * @cfg {Boolean} carousel true to make the group behave like a carousel
17510  * @cfg {Boolean} bullets show bullets for the panels
17511  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17512  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17513  * @cfg {Boolean} showarrow (true|false) show arrow default true
17514  * 
17515  * @constructor
17516  * Create a new TabGroup
17517  * @param {Object} config The config object
17518  */
17519
17520 Roo.bootstrap.TabGroup = function(config){
17521     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17522     if (!this.navId) {
17523         this.navId = Roo.id();
17524     }
17525     this.tabs = [];
17526     Roo.bootstrap.TabGroup.register(this);
17527     
17528 };
17529
17530 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17531     
17532     carousel : false,
17533     transition : false,
17534     bullets : 0,
17535     timer : 0,
17536     autoslide : false,
17537     slideFn : false,
17538     slideOnTouch : false,
17539     showarrow : true,
17540     
17541     getAutoCreate : function()
17542     {
17543         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17544         
17545         cfg.cls += ' tab-content';
17546         
17547         if (this.carousel) {
17548             cfg.cls += ' carousel slide';
17549             
17550             cfg.cn = [{
17551                cls : 'carousel-inner',
17552                cn : []
17553             }];
17554         
17555             if(this.bullets  && !Roo.isTouch){
17556                 
17557                 var bullets = {
17558                     cls : 'carousel-bullets',
17559                     cn : []
17560                 };
17561                
17562                 if(this.bullets_cls){
17563                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17564                 }
17565                 
17566                 bullets.cn.push({
17567                     cls : 'clear'
17568                 });
17569                 
17570                 cfg.cn[0].cn.push(bullets);
17571             }
17572             
17573             if(this.showarrow){
17574                 cfg.cn[0].cn.push({
17575                     tag : 'div',
17576                     class : 'carousel-arrow',
17577                     cn : [
17578                         {
17579                             tag : 'div',
17580                             class : 'carousel-prev',
17581                             cn : [
17582                                 {
17583                                     tag : 'i',
17584                                     class : 'fa fa-chevron-left'
17585                                 }
17586                             ]
17587                         },
17588                         {
17589                             tag : 'div',
17590                             class : 'carousel-next',
17591                             cn : [
17592                                 {
17593                                     tag : 'i',
17594                                     class : 'fa fa-chevron-right'
17595                                 }
17596                             ]
17597                         }
17598                     ]
17599                 });
17600             }
17601             
17602         }
17603         
17604         return cfg;
17605     },
17606     
17607     initEvents:  function()
17608     {
17609 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17610 //            this.el.on("touchstart", this.onTouchStart, this);
17611 //        }
17612         
17613         if(this.autoslide){
17614             var _this = this;
17615             
17616             this.slideFn = window.setInterval(function() {
17617                 _this.showPanelNext();
17618             }, this.timer);
17619         }
17620         
17621         if(this.showarrow){
17622             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17623             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17624         }
17625         
17626         
17627     },
17628     
17629 //    onTouchStart : function(e, el, o)
17630 //    {
17631 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17632 //            return;
17633 //        }
17634 //        
17635 //        this.showPanelNext();
17636 //    },
17637     
17638     
17639     getChildContainer : function()
17640     {
17641         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17642     },
17643     
17644     /**
17645     * register a Navigation item
17646     * @param {Roo.bootstrap.NavItem} the navitem to add
17647     */
17648     register : function(item)
17649     {
17650         this.tabs.push( item);
17651         item.navId = this.navId; // not really needed..
17652         this.addBullet();
17653     
17654     },
17655     
17656     getActivePanel : function()
17657     {
17658         var r = false;
17659         Roo.each(this.tabs, function(t) {
17660             if (t.active) {
17661                 r = t;
17662                 return false;
17663             }
17664             return null;
17665         });
17666         return r;
17667         
17668     },
17669     getPanelByName : function(n)
17670     {
17671         var r = false;
17672         Roo.each(this.tabs, function(t) {
17673             if (t.tabId == n) {
17674                 r = t;
17675                 return false;
17676             }
17677             return null;
17678         });
17679         return r;
17680     },
17681     indexOfPanel : function(p)
17682     {
17683         var r = false;
17684         Roo.each(this.tabs, function(t,i) {
17685             if (t.tabId == p.tabId) {
17686                 r = i;
17687                 return false;
17688             }
17689             return null;
17690         });
17691         return r;
17692     },
17693     /**
17694      * show a specific panel
17695      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17696      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17697      */
17698     showPanel : function (pan)
17699     {
17700         if(this.transition || typeof(pan) == 'undefined'){
17701             Roo.log("waiting for the transitionend");
17702             return;
17703         }
17704         
17705         if (typeof(pan) == 'number') {
17706             pan = this.tabs[pan];
17707         }
17708         
17709         if (typeof(pan) == 'string') {
17710             pan = this.getPanelByName(pan);
17711         }
17712         
17713         var cur = this.getActivePanel();
17714         
17715         if(!pan || !cur){
17716             Roo.log('pan or acitve pan is undefined');
17717             return false;
17718         }
17719         
17720         if (pan.tabId == this.getActivePanel().tabId) {
17721             return true;
17722         }
17723         
17724         if (false === cur.fireEvent('beforedeactivate')) {
17725             return false;
17726         }
17727         
17728         if(this.bullets > 0 && !Roo.isTouch){
17729             this.setActiveBullet(this.indexOfPanel(pan));
17730         }
17731         
17732         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17733             
17734             this.transition = true;
17735             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17736             var lr = dir == 'next' ? 'left' : 'right';
17737             pan.el.addClass(dir); // or prev
17738             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17739             cur.el.addClass(lr); // or right
17740             pan.el.addClass(lr);
17741             
17742             var _this = this;
17743             cur.el.on('transitionend', function() {
17744                 Roo.log("trans end?");
17745                 
17746                 pan.el.removeClass([lr,dir]);
17747                 pan.setActive(true);
17748                 
17749                 cur.el.removeClass([lr]);
17750                 cur.setActive(false);
17751                 
17752                 _this.transition = false;
17753                 
17754             }, this, { single:  true } );
17755             
17756             return true;
17757         }
17758         
17759         cur.setActive(false);
17760         pan.setActive(true);
17761         
17762         return true;
17763         
17764     },
17765     showPanelNext : function()
17766     {
17767         var i = this.indexOfPanel(this.getActivePanel());
17768         
17769         if (i >= this.tabs.length - 1 && !this.autoslide) {
17770             return;
17771         }
17772         
17773         if (i >= this.tabs.length - 1 && this.autoslide) {
17774             i = -1;
17775         }
17776         
17777         this.showPanel(this.tabs[i+1]);
17778     },
17779     
17780     showPanelPrev : function()
17781     {
17782         var i = this.indexOfPanel(this.getActivePanel());
17783         
17784         if (i  < 1 && !this.autoslide) {
17785             return;
17786         }
17787         
17788         if (i < 1 && this.autoslide) {
17789             i = this.tabs.length;
17790         }
17791         
17792         this.showPanel(this.tabs[i-1]);
17793     },
17794     
17795     
17796     addBullet: function()
17797     {
17798         if(!this.bullets || Roo.isTouch){
17799             return;
17800         }
17801         var ctr = this.el.select('.carousel-bullets',true).first();
17802         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17803         var bullet = ctr.createChild({
17804             cls : 'bullet bullet-' + i
17805         },ctr.dom.lastChild);
17806         
17807         
17808         var _this = this;
17809         
17810         bullet.on('click', (function(e, el, o, ii, t){
17811
17812             e.preventDefault();
17813
17814             this.showPanel(ii);
17815
17816             if(this.autoslide && this.slideFn){
17817                 clearInterval(this.slideFn);
17818                 this.slideFn = window.setInterval(function() {
17819                     _this.showPanelNext();
17820                 }, this.timer);
17821             }
17822
17823         }).createDelegate(this, [i, bullet], true));
17824                 
17825         
17826     },
17827      
17828     setActiveBullet : function(i)
17829     {
17830         if(Roo.isTouch){
17831             return;
17832         }
17833         
17834         Roo.each(this.el.select('.bullet', true).elements, function(el){
17835             el.removeClass('selected');
17836         });
17837
17838         var bullet = this.el.select('.bullet-' + i, true).first();
17839         
17840         if(!bullet){
17841             return;
17842         }
17843         
17844         bullet.addClass('selected');
17845     }
17846     
17847     
17848   
17849 });
17850
17851  
17852
17853  
17854  
17855 Roo.apply(Roo.bootstrap.TabGroup, {
17856     
17857     groups: {},
17858      /**
17859     * register a Navigation Group
17860     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17861     */
17862     register : function(navgrp)
17863     {
17864         this.groups[navgrp.navId] = navgrp;
17865         
17866     },
17867     /**
17868     * fetch a Navigation Group based on the navigation ID
17869     * if one does not exist , it will get created.
17870     * @param {string} the navgroup to add
17871     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17872     */
17873     get: function(navId) {
17874         if (typeof(this.groups[navId]) == 'undefined') {
17875             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17876         }
17877         return this.groups[navId] ;
17878     }
17879     
17880     
17881     
17882 });
17883
17884  /*
17885  * - LGPL
17886  *
17887  * TabPanel
17888  * 
17889  */
17890
17891 /**
17892  * @class Roo.bootstrap.TabPanel
17893  * @extends Roo.bootstrap.Component
17894  * Bootstrap TabPanel class
17895  * @cfg {Boolean} active panel active
17896  * @cfg {String} html panel content
17897  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17898  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17899  * @cfg {String} href click to link..
17900  * 
17901  * 
17902  * @constructor
17903  * Create a new TabPanel
17904  * @param {Object} config The config object
17905  */
17906
17907 Roo.bootstrap.TabPanel = function(config){
17908     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17909     this.addEvents({
17910         /**
17911              * @event changed
17912              * Fires when the active status changes
17913              * @param {Roo.bootstrap.TabPanel} this
17914              * @param {Boolean} state the new state
17915             
17916          */
17917         'changed': true,
17918         /**
17919              * @event beforedeactivate
17920              * Fires before a tab is de-activated - can be used to do validation on a form.
17921              * @param {Roo.bootstrap.TabPanel} this
17922              * @return {Boolean} false if there is an error
17923             
17924          */
17925         'beforedeactivate': true
17926      });
17927     
17928     this.tabId = this.tabId || Roo.id();
17929   
17930 };
17931
17932 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17933     
17934     active: false,
17935     html: false,
17936     tabId: false,
17937     navId : false,
17938     href : '',
17939     
17940     getAutoCreate : function(){
17941         var cfg = {
17942             tag: 'div',
17943             // item is needed for carousel - not sure if it has any effect otherwise
17944             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17945             html: this.html || ''
17946         };
17947         
17948         if(this.active){
17949             cfg.cls += ' active';
17950         }
17951         
17952         if(this.tabId){
17953             cfg.tabId = this.tabId;
17954         }
17955         
17956         
17957         return cfg;
17958     },
17959     
17960     initEvents:  function()
17961     {
17962         var p = this.parent();
17963         
17964         this.navId = this.navId || p.navId;
17965         
17966         if (typeof(this.navId) != 'undefined') {
17967             // not really needed.. but just in case.. parent should be a NavGroup.
17968             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17969             
17970             tg.register(this);
17971             
17972             var i = tg.tabs.length - 1;
17973             
17974             if(this.active && tg.bullets > 0 && i < tg.bullets){
17975                 tg.setActiveBullet(i);
17976             }
17977         }
17978         
17979         this.el.on('click', this.onClick, this);
17980         
17981         if(Roo.isTouch){
17982             this.el.on("touchstart", this.onTouchStart, this);
17983             this.el.on("touchmove", this.onTouchMove, this);
17984             this.el.on("touchend", this.onTouchEnd, this);
17985         }
17986         
17987     },
17988     
17989     onRender : function(ct, position)
17990     {
17991         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17992     },
17993     
17994     setActive : function(state)
17995     {
17996         Roo.log("panel - set active " + this.tabId + "=" + state);
17997         
17998         this.active = state;
17999         if (!state) {
18000             this.el.removeClass('active');
18001             
18002         } else  if (!this.el.hasClass('active')) {
18003             this.el.addClass('active');
18004         }
18005         
18006         this.fireEvent('changed', this, state);
18007     },
18008     
18009     onClick : function(e)
18010     {
18011         e.preventDefault();
18012         
18013         if(!this.href.length){
18014             return;
18015         }
18016         
18017         window.location.href = this.href;
18018     },
18019     
18020     startX : 0,
18021     startY : 0,
18022     endX : 0,
18023     endY : 0,
18024     swiping : false,
18025     
18026     onTouchStart : function(e)
18027     {
18028         this.swiping = false;
18029         
18030         this.startX = e.browserEvent.touches[0].clientX;
18031         this.startY = e.browserEvent.touches[0].clientY;
18032     },
18033     
18034     onTouchMove : function(e)
18035     {
18036         this.swiping = true;
18037         
18038         this.endX = e.browserEvent.touches[0].clientX;
18039         this.endY = e.browserEvent.touches[0].clientY;
18040     },
18041     
18042     onTouchEnd : function(e)
18043     {
18044         if(!this.swiping){
18045             this.onClick(e);
18046             return;
18047         }
18048         
18049         var tabGroup = this.parent();
18050         
18051         if(this.endX > this.startX){ // swiping right
18052             tabGroup.showPanelPrev();
18053             return;
18054         }
18055         
18056         if(this.startX > this.endX){ // swiping left
18057             tabGroup.showPanelNext();
18058             return;
18059         }
18060     }
18061     
18062     
18063 });
18064  
18065
18066  
18067
18068  /*
18069  * - LGPL
18070  *
18071  * DateField
18072  * 
18073  */
18074
18075 /**
18076  * @class Roo.bootstrap.DateField
18077  * @extends Roo.bootstrap.Input
18078  * Bootstrap DateField class
18079  * @cfg {Number} weekStart default 0
18080  * @cfg {String} viewMode default empty, (months|years)
18081  * @cfg {String} minViewMode default empty, (months|years)
18082  * @cfg {Number} startDate default -Infinity
18083  * @cfg {Number} endDate default Infinity
18084  * @cfg {Boolean} todayHighlight default false
18085  * @cfg {Boolean} todayBtn default false
18086  * @cfg {Boolean} calendarWeeks default false
18087  * @cfg {Object} daysOfWeekDisabled default empty
18088  * @cfg {Boolean} singleMode default false (true | false)
18089  * 
18090  * @cfg {Boolean} keyboardNavigation default true
18091  * @cfg {String} language default en
18092  * 
18093  * @constructor
18094  * Create a new DateField
18095  * @param {Object} config The config object
18096  */
18097
18098 Roo.bootstrap.DateField = function(config){
18099     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18100      this.addEvents({
18101             /**
18102              * @event show
18103              * Fires when this field show.
18104              * @param {Roo.bootstrap.DateField} this
18105              * @param {Mixed} date The date value
18106              */
18107             show : true,
18108             /**
18109              * @event show
18110              * Fires when this field hide.
18111              * @param {Roo.bootstrap.DateField} this
18112              * @param {Mixed} date The date value
18113              */
18114             hide : true,
18115             /**
18116              * @event select
18117              * Fires when select a date.
18118              * @param {Roo.bootstrap.DateField} this
18119              * @param {Mixed} date The date value
18120              */
18121             select : true,
18122             /**
18123              * @event beforeselect
18124              * Fires when before select a date.
18125              * @param {Roo.bootstrap.DateField} this
18126              * @param {Mixed} date The date value
18127              */
18128             beforeselect : true
18129         });
18130 };
18131
18132 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18133     
18134     /**
18135      * @cfg {String} format
18136      * The default date format string which can be overriden for localization support.  The format must be
18137      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18138      */
18139     format : "m/d/y",
18140     /**
18141      * @cfg {String} altFormats
18142      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18143      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18144      */
18145     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18146     
18147     weekStart : 0,
18148     
18149     viewMode : '',
18150     
18151     minViewMode : '',
18152     
18153     todayHighlight : false,
18154     
18155     todayBtn: false,
18156     
18157     language: 'en',
18158     
18159     keyboardNavigation: true,
18160     
18161     calendarWeeks: false,
18162     
18163     startDate: -Infinity,
18164     
18165     endDate: Infinity,
18166     
18167     daysOfWeekDisabled: [],
18168     
18169     _events: [],
18170     
18171     singleMode : false,
18172     
18173     UTCDate: function()
18174     {
18175         return new Date(Date.UTC.apply(Date, arguments));
18176     },
18177     
18178     UTCToday: function()
18179     {
18180         var today = new Date();
18181         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18182     },
18183     
18184     getDate: function() {
18185             var d = this.getUTCDate();
18186             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18187     },
18188     
18189     getUTCDate: function() {
18190             return this.date;
18191     },
18192     
18193     setDate: function(d) {
18194             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18195     },
18196     
18197     setUTCDate: function(d) {
18198             this.date = d;
18199             this.setValue(this.formatDate(this.date));
18200     },
18201         
18202     onRender: function(ct, position)
18203     {
18204         
18205         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18206         
18207         this.language = this.language || 'en';
18208         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18209         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18210         
18211         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18212         this.format = this.format || 'm/d/y';
18213         this.isInline = false;
18214         this.isInput = true;
18215         this.component = this.el.select('.add-on', true).first() || false;
18216         this.component = (this.component && this.component.length === 0) ? false : this.component;
18217         this.hasInput = this.component && this.inputEl().length;
18218         
18219         if (typeof(this.minViewMode === 'string')) {
18220             switch (this.minViewMode) {
18221                 case 'months':
18222                     this.minViewMode = 1;
18223                     break;
18224                 case 'years':
18225                     this.minViewMode = 2;
18226                     break;
18227                 default:
18228                     this.minViewMode = 0;
18229                     break;
18230             }
18231         }
18232         
18233         if (typeof(this.viewMode === 'string')) {
18234             switch (this.viewMode) {
18235                 case 'months':
18236                     this.viewMode = 1;
18237                     break;
18238                 case 'years':
18239                     this.viewMode = 2;
18240                     break;
18241                 default:
18242                     this.viewMode = 0;
18243                     break;
18244             }
18245         }
18246                 
18247         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18248         
18249 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18250         
18251         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18252         
18253         this.picker().on('mousedown', this.onMousedown, this);
18254         this.picker().on('click', this.onClick, this);
18255         
18256         this.picker().addClass('datepicker-dropdown');
18257         
18258         this.startViewMode = this.viewMode;
18259         
18260         if(this.singleMode){
18261             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18262                 v.setVisibilityMode(Roo.Element.DISPLAY);
18263                 v.hide();
18264             });
18265             
18266             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18267                 v.setStyle('width', '189px');
18268             });
18269         }
18270         
18271         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18272             if(!this.calendarWeeks){
18273                 v.remove();
18274                 return;
18275             }
18276             
18277             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18278             v.attr('colspan', function(i, val){
18279                 return parseInt(val) + 1;
18280             });
18281         });
18282                         
18283         
18284         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18285         
18286         this.setStartDate(this.startDate);
18287         this.setEndDate(this.endDate);
18288         
18289         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18290         
18291         this.fillDow();
18292         this.fillMonths();
18293         this.update();
18294         this.showMode();
18295         
18296         if(this.isInline) {
18297             this.show();
18298         }
18299     },
18300     
18301     picker : function()
18302     {
18303         return this.pickerEl;
18304 //        return this.el.select('.datepicker', true).first();
18305     },
18306     
18307     fillDow: function()
18308     {
18309         var dowCnt = this.weekStart;
18310         
18311         var dow = {
18312             tag: 'tr',
18313             cn: [
18314                 
18315             ]
18316         };
18317         
18318         if(this.calendarWeeks){
18319             dow.cn.push({
18320                 tag: 'th',
18321                 cls: 'cw',
18322                 html: '&nbsp;'
18323             })
18324         }
18325         
18326         while (dowCnt < this.weekStart + 7) {
18327             dow.cn.push({
18328                 tag: 'th',
18329                 cls: 'dow',
18330                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18331             });
18332         }
18333         
18334         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18335     },
18336     
18337     fillMonths: function()
18338     {    
18339         var i = 0;
18340         var months = this.picker().select('>.datepicker-months td', true).first();
18341         
18342         months.dom.innerHTML = '';
18343         
18344         while (i < 12) {
18345             var month = {
18346                 tag: 'span',
18347                 cls: 'month',
18348                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18349             };
18350             
18351             months.createChild(month);
18352         }
18353         
18354     },
18355     
18356     update: function()
18357     {
18358         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;
18359         
18360         if (this.date < this.startDate) {
18361             this.viewDate = new Date(this.startDate);
18362         } else if (this.date > this.endDate) {
18363             this.viewDate = new Date(this.endDate);
18364         } else {
18365             this.viewDate = new Date(this.date);
18366         }
18367         
18368         this.fill();
18369     },
18370     
18371     fill: function() 
18372     {
18373         var d = new Date(this.viewDate),
18374                 year = d.getUTCFullYear(),
18375                 month = d.getUTCMonth(),
18376                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18377                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18378                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18379                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18380                 currentDate = this.date && this.date.valueOf(),
18381                 today = this.UTCToday();
18382         
18383         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18384         
18385 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18386         
18387 //        this.picker.select('>tfoot th.today').
18388 //                                              .text(dates[this.language].today)
18389 //                                              .toggle(this.todayBtn !== false);
18390     
18391         this.updateNavArrows();
18392         this.fillMonths();
18393                                                 
18394         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18395         
18396         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18397          
18398         prevMonth.setUTCDate(day);
18399         
18400         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18401         
18402         var nextMonth = new Date(prevMonth);
18403         
18404         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18405         
18406         nextMonth = nextMonth.valueOf();
18407         
18408         var fillMonths = false;
18409         
18410         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18411         
18412         while(prevMonth.valueOf() < nextMonth) {
18413             var clsName = '';
18414             
18415             if (prevMonth.getUTCDay() === this.weekStart) {
18416                 if(fillMonths){
18417                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18418                 }
18419                     
18420                 fillMonths = {
18421                     tag: 'tr',
18422                     cn: []
18423                 };
18424                 
18425                 if(this.calendarWeeks){
18426                     // ISO 8601: First week contains first thursday.
18427                     // ISO also states week starts on Monday, but we can be more abstract here.
18428                     var
18429                     // Start of current week: based on weekstart/current date
18430                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18431                     // Thursday of this week
18432                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18433                     // First Thursday of year, year from thursday
18434                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18435                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18436                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18437                     
18438                     fillMonths.cn.push({
18439                         tag: 'td',
18440                         cls: 'cw',
18441                         html: calWeek
18442                     });
18443                 }
18444             }
18445             
18446             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18447                 clsName += ' old';
18448             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18449                 clsName += ' new';
18450             }
18451             if (this.todayHighlight &&
18452                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18453                 prevMonth.getUTCMonth() == today.getMonth() &&
18454                 prevMonth.getUTCDate() == today.getDate()) {
18455                 clsName += ' today';
18456             }
18457             
18458             if (currentDate && prevMonth.valueOf() === currentDate) {
18459                 clsName += ' active';
18460             }
18461             
18462             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18463                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18464                     clsName += ' disabled';
18465             }
18466             
18467             fillMonths.cn.push({
18468                 tag: 'td',
18469                 cls: 'day ' + clsName,
18470                 html: prevMonth.getDate()
18471             });
18472             
18473             prevMonth.setDate(prevMonth.getDate()+1);
18474         }
18475           
18476         var currentYear = this.date && this.date.getUTCFullYear();
18477         var currentMonth = this.date && this.date.getUTCMonth();
18478         
18479         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18480         
18481         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18482             v.removeClass('active');
18483             
18484             if(currentYear === year && k === currentMonth){
18485                 v.addClass('active');
18486             }
18487             
18488             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18489                 v.addClass('disabled');
18490             }
18491             
18492         });
18493         
18494         
18495         year = parseInt(year/10, 10) * 10;
18496         
18497         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18498         
18499         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18500         
18501         year -= 1;
18502         for (var i = -1; i < 11; i++) {
18503             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18504                 tag: 'span',
18505                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18506                 html: year
18507             });
18508             
18509             year += 1;
18510         }
18511     },
18512     
18513     showMode: function(dir) 
18514     {
18515         if (dir) {
18516             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18517         }
18518         
18519         Roo.each(this.picker().select('>div',true).elements, function(v){
18520             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18521             v.hide();
18522         });
18523         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18524     },
18525     
18526     place: function()
18527     {
18528         if(this.isInline) {
18529             return;
18530         }
18531         
18532         this.picker().removeClass(['bottom', 'top']);
18533         
18534         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18535             /*
18536              * place to the top of element!
18537              *
18538              */
18539             
18540             this.picker().addClass('top');
18541             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18542             
18543             return;
18544         }
18545         
18546         this.picker().addClass('bottom');
18547         
18548         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18549     },
18550     
18551     parseDate : function(value)
18552     {
18553         if(!value || value instanceof Date){
18554             return value;
18555         }
18556         var v = Date.parseDate(value, this.format);
18557         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18558             v = Date.parseDate(value, 'Y-m-d');
18559         }
18560         if(!v && this.altFormats){
18561             if(!this.altFormatsArray){
18562                 this.altFormatsArray = this.altFormats.split("|");
18563             }
18564             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18565                 v = Date.parseDate(value, this.altFormatsArray[i]);
18566             }
18567         }
18568         return v;
18569     },
18570     
18571     formatDate : function(date, fmt)
18572     {   
18573         return (!date || !(date instanceof Date)) ?
18574         date : date.dateFormat(fmt || this.format);
18575     },
18576     
18577     onFocus : function()
18578     {
18579         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18580         this.show();
18581     },
18582     
18583     onBlur : function()
18584     {
18585         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18586         
18587         var d = this.inputEl().getValue();
18588         
18589         this.setValue(d);
18590                 
18591         this.hide();
18592     },
18593     
18594     show : function()
18595     {
18596         this.picker().show();
18597         this.update();
18598         this.place();
18599         
18600         this.fireEvent('show', this, this.date);
18601     },
18602     
18603     hide : function()
18604     {
18605         if(this.isInline) {
18606             return;
18607         }
18608         this.picker().hide();
18609         this.viewMode = this.startViewMode;
18610         this.showMode();
18611         
18612         this.fireEvent('hide', this, this.date);
18613         
18614     },
18615     
18616     onMousedown: function(e)
18617     {
18618         e.stopPropagation();
18619         e.preventDefault();
18620     },
18621     
18622     keyup: function(e)
18623     {
18624         Roo.bootstrap.DateField.superclass.keyup.call(this);
18625         this.update();
18626     },
18627
18628     setValue: function(v)
18629     {
18630         if(this.fireEvent('beforeselect', this, v) !== false){
18631             var d = new Date(this.parseDate(v) ).clearTime();
18632         
18633             if(isNaN(d.getTime())){
18634                 this.date = this.viewDate = '';
18635                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18636                 return;
18637             }
18638
18639             v = this.formatDate(d);
18640
18641             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18642
18643             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18644
18645             this.update();
18646
18647             this.fireEvent('select', this, this.date);
18648         }
18649     },
18650     
18651     getValue: function()
18652     {
18653         return this.formatDate(this.date);
18654     },
18655     
18656     fireKey: function(e)
18657     {
18658         if (!this.picker().isVisible()){
18659             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18660                 this.show();
18661             }
18662             return;
18663         }
18664         
18665         var dateChanged = false,
18666         dir, day, month,
18667         newDate, newViewDate;
18668         
18669         switch(e.keyCode){
18670             case 27: // escape
18671                 this.hide();
18672                 e.preventDefault();
18673                 break;
18674             case 37: // left
18675             case 39: // right
18676                 if (!this.keyboardNavigation) {
18677                     break;
18678                 }
18679                 dir = e.keyCode == 37 ? -1 : 1;
18680                 
18681                 if (e.ctrlKey){
18682                     newDate = this.moveYear(this.date, dir);
18683                     newViewDate = this.moveYear(this.viewDate, dir);
18684                 } else if (e.shiftKey){
18685                     newDate = this.moveMonth(this.date, dir);
18686                     newViewDate = this.moveMonth(this.viewDate, dir);
18687                 } else {
18688                     newDate = new Date(this.date);
18689                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18690                     newViewDate = new Date(this.viewDate);
18691                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18692                 }
18693                 if (this.dateWithinRange(newDate)){
18694                     this.date = newDate;
18695                     this.viewDate = newViewDate;
18696                     this.setValue(this.formatDate(this.date));
18697 //                    this.update();
18698                     e.preventDefault();
18699                     dateChanged = true;
18700                 }
18701                 break;
18702             case 38: // up
18703             case 40: // down
18704                 if (!this.keyboardNavigation) {
18705                     break;
18706                 }
18707                 dir = e.keyCode == 38 ? -1 : 1;
18708                 if (e.ctrlKey){
18709                     newDate = this.moveYear(this.date, dir);
18710                     newViewDate = this.moveYear(this.viewDate, dir);
18711                 } else if (e.shiftKey){
18712                     newDate = this.moveMonth(this.date, dir);
18713                     newViewDate = this.moveMonth(this.viewDate, dir);
18714                 } else {
18715                     newDate = new Date(this.date);
18716                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18717                     newViewDate = new Date(this.viewDate);
18718                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18719                 }
18720                 if (this.dateWithinRange(newDate)){
18721                     this.date = newDate;
18722                     this.viewDate = newViewDate;
18723                     this.setValue(this.formatDate(this.date));
18724 //                    this.update();
18725                     e.preventDefault();
18726                     dateChanged = true;
18727                 }
18728                 break;
18729             case 13: // enter
18730                 this.setValue(this.formatDate(this.date));
18731                 this.hide();
18732                 e.preventDefault();
18733                 break;
18734             case 9: // tab
18735                 this.setValue(this.formatDate(this.date));
18736                 this.hide();
18737                 break;
18738             case 16: // shift
18739             case 17: // ctrl
18740             case 18: // alt
18741                 break;
18742             default :
18743                 this.hide();
18744                 
18745         }
18746     },
18747     
18748     
18749     onClick: function(e) 
18750     {
18751         e.stopPropagation();
18752         e.preventDefault();
18753         
18754         var target = e.getTarget();
18755         
18756         if(target.nodeName.toLowerCase() === 'i'){
18757             target = Roo.get(target).dom.parentNode;
18758         }
18759         
18760         var nodeName = target.nodeName;
18761         var className = target.className;
18762         var html = target.innerHTML;
18763         //Roo.log(nodeName);
18764         
18765         switch(nodeName.toLowerCase()) {
18766             case 'th':
18767                 switch(className) {
18768                     case 'switch':
18769                         this.showMode(1);
18770                         break;
18771                     case 'prev':
18772                     case 'next':
18773                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18774                         switch(this.viewMode){
18775                                 case 0:
18776                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18777                                         break;
18778                                 case 1:
18779                                 case 2:
18780                                         this.viewDate = this.moveYear(this.viewDate, dir);
18781                                         break;
18782                         }
18783                         this.fill();
18784                         break;
18785                     case 'today':
18786                         var date = new Date();
18787                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18788 //                        this.fill()
18789                         this.setValue(this.formatDate(this.date));
18790                         
18791                         this.hide();
18792                         break;
18793                 }
18794                 break;
18795             case 'span':
18796                 if (className.indexOf('disabled') < 0) {
18797                     this.viewDate.setUTCDate(1);
18798                     if (className.indexOf('month') > -1) {
18799                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18800                     } else {
18801                         var year = parseInt(html, 10) || 0;
18802                         this.viewDate.setUTCFullYear(year);
18803                         
18804                     }
18805                     
18806                     if(this.singleMode){
18807                         this.setValue(this.formatDate(this.viewDate));
18808                         this.hide();
18809                         return;
18810                     }
18811                     
18812                     this.showMode(-1);
18813                     this.fill();
18814                 }
18815                 break;
18816                 
18817             case 'td':
18818                 //Roo.log(className);
18819                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18820                     var day = parseInt(html, 10) || 1;
18821                     var year = this.viewDate.getUTCFullYear(),
18822                         month = this.viewDate.getUTCMonth();
18823
18824                     if (className.indexOf('old') > -1) {
18825                         if(month === 0 ){
18826                             month = 11;
18827                             year -= 1;
18828                         }else{
18829                             month -= 1;
18830                         }
18831                     } else if (className.indexOf('new') > -1) {
18832                         if (month == 11) {
18833                             month = 0;
18834                             year += 1;
18835                         } else {
18836                             month += 1;
18837                         }
18838                     }
18839                     //Roo.log([year,month,day]);
18840                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18841                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18842 //                    this.fill();
18843                     //Roo.log(this.formatDate(this.date));
18844                     this.setValue(this.formatDate(this.date));
18845                     this.hide();
18846                 }
18847                 break;
18848         }
18849     },
18850     
18851     setStartDate: function(startDate)
18852     {
18853         this.startDate = startDate || -Infinity;
18854         if (this.startDate !== -Infinity) {
18855             this.startDate = this.parseDate(this.startDate);
18856         }
18857         this.update();
18858         this.updateNavArrows();
18859     },
18860
18861     setEndDate: function(endDate)
18862     {
18863         this.endDate = endDate || Infinity;
18864         if (this.endDate !== Infinity) {
18865             this.endDate = this.parseDate(this.endDate);
18866         }
18867         this.update();
18868         this.updateNavArrows();
18869     },
18870     
18871     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18872     {
18873         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18874         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18875             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18876         }
18877         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18878             return parseInt(d, 10);
18879         });
18880         this.update();
18881         this.updateNavArrows();
18882     },
18883     
18884     updateNavArrows: function() 
18885     {
18886         if(this.singleMode){
18887             return;
18888         }
18889         
18890         var d = new Date(this.viewDate),
18891         year = d.getUTCFullYear(),
18892         month = d.getUTCMonth();
18893         
18894         Roo.each(this.picker().select('.prev', true).elements, function(v){
18895             v.show();
18896             switch (this.viewMode) {
18897                 case 0:
18898
18899                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18900                         v.hide();
18901                     }
18902                     break;
18903                 case 1:
18904                 case 2:
18905                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18906                         v.hide();
18907                     }
18908                     break;
18909             }
18910         });
18911         
18912         Roo.each(this.picker().select('.next', true).elements, function(v){
18913             v.show();
18914             switch (this.viewMode) {
18915                 case 0:
18916
18917                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18918                         v.hide();
18919                     }
18920                     break;
18921                 case 1:
18922                 case 2:
18923                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18924                         v.hide();
18925                     }
18926                     break;
18927             }
18928         })
18929     },
18930     
18931     moveMonth: function(date, dir)
18932     {
18933         if (!dir) {
18934             return date;
18935         }
18936         var new_date = new Date(date.valueOf()),
18937         day = new_date.getUTCDate(),
18938         month = new_date.getUTCMonth(),
18939         mag = Math.abs(dir),
18940         new_month, test;
18941         dir = dir > 0 ? 1 : -1;
18942         if (mag == 1){
18943             test = dir == -1
18944             // If going back one month, make sure month is not current month
18945             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18946             ? function(){
18947                 return new_date.getUTCMonth() == month;
18948             }
18949             // If going forward one month, make sure month is as expected
18950             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18951             : function(){
18952                 return new_date.getUTCMonth() != new_month;
18953             };
18954             new_month = month + dir;
18955             new_date.setUTCMonth(new_month);
18956             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18957             if (new_month < 0 || new_month > 11) {
18958                 new_month = (new_month + 12) % 12;
18959             }
18960         } else {
18961             // For magnitudes >1, move one month at a time...
18962             for (var i=0; i<mag; i++) {
18963                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18964                 new_date = this.moveMonth(new_date, dir);
18965             }
18966             // ...then reset the day, keeping it in the new month
18967             new_month = new_date.getUTCMonth();
18968             new_date.setUTCDate(day);
18969             test = function(){
18970                 return new_month != new_date.getUTCMonth();
18971             };
18972         }
18973         // Common date-resetting loop -- if date is beyond end of month, make it
18974         // end of month
18975         while (test()){
18976             new_date.setUTCDate(--day);
18977             new_date.setUTCMonth(new_month);
18978         }
18979         return new_date;
18980     },
18981
18982     moveYear: function(date, dir)
18983     {
18984         return this.moveMonth(date, dir*12);
18985     },
18986
18987     dateWithinRange: function(date)
18988     {
18989         return date >= this.startDate && date <= this.endDate;
18990     },
18991
18992     
18993     remove: function() 
18994     {
18995         this.picker().remove();
18996     },
18997     
18998     validateValue : function(value)
18999     {
19000         if(value.length < 1)  {
19001             if(this.allowBlank){
19002                 return true;
19003             }
19004             return false;
19005         }
19006         
19007         if(value.length < this.minLength){
19008             return false;
19009         }
19010         if(value.length > this.maxLength){
19011             return false;
19012         }
19013         if(this.vtype){
19014             var vt = Roo.form.VTypes;
19015             if(!vt[this.vtype](value, this)){
19016                 return false;
19017             }
19018         }
19019         if(typeof this.validator == "function"){
19020             var msg = this.validator(value);
19021             if(msg !== true){
19022                 return false;
19023             }
19024         }
19025         
19026         if(this.regex && !this.regex.test(value)){
19027             return false;
19028         }
19029         
19030         if(typeof(this.parseDate(value)) == 'undefined'){
19031             return false;
19032         }
19033         
19034         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19035             return false;
19036         }      
19037         
19038         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19039             return false;
19040         } 
19041         
19042         
19043         return true;
19044     }
19045    
19046 });
19047
19048 Roo.apply(Roo.bootstrap.DateField,  {
19049     
19050     head : {
19051         tag: 'thead',
19052         cn: [
19053         {
19054             tag: 'tr',
19055             cn: [
19056             {
19057                 tag: 'th',
19058                 cls: 'prev',
19059                 html: '<i class="fa fa-arrow-left"/>'
19060             },
19061             {
19062                 tag: 'th',
19063                 cls: 'switch',
19064                 colspan: '5'
19065             },
19066             {
19067                 tag: 'th',
19068                 cls: 'next',
19069                 html: '<i class="fa fa-arrow-right"/>'
19070             }
19071
19072             ]
19073         }
19074         ]
19075     },
19076     
19077     content : {
19078         tag: 'tbody',
19079         cn: [
19080         {
19081             tag: 'tr',
19082             cn: [
19083             {
19084                 tag: 'td',
19085                 colspan: '7'
19086             }
19087             ]
19088         }
19089         ]
19090     },
19091     
19092     footer : {
19093         tag: 'tfoot',
19094         cn: [
19095         {
19096             tag: 'tr',
19097             cn: [
19098             {
19099                 tag: 'th',
19100                 colspan: '7',
19101                 cls: 'today'
19102             }
19103                     
19104             ]
19105         }
19106         ]
19107     },
19108     
19109     dates:{
19110         en: {
19111             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19112             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19113             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19114             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19115             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19116             today: "Today"
19117         }
19118     },
19119     
19120     modes: [
19121     {
19122         clsName: 'days',
19123         navFnc: 'Month',
19124         navStep: 1
19125     },
19126     {
19127         clsName: 'months',
19128         navFnc: 'FullYear',
19129         navStep: 1
19130     },
19131     {
19132         clsName: 'years',
19133         navFnc: 'FullYear',
19134         navStep: 10
19135     }]
19136 });
19137
19138 Roo.apply(Roo.bootstrap.DateField,  {
19139   
19140     template : {
19141         tag: 'div',
19142         cls: 'datepicker dropdown-menu roo-dynamic',
19143         cn: [
19144         {
19145             tag: 'div',
19146             cls: 'datepicker-days',
19147             cn: [
19148             {
19149                 tag: 'table',
19150                 cls: 'table-condensed',
19151                 cn:[
19152                 Roo.bootstrap.DateField.head,
19153                 {
19154                     tag: 'tbody'
19155                 },
19156                 Roo.bootstrap.DateField.footer
19157                 ]
19158             }
19159             ]
19160         },
19161         {
19162             tag: 'div',
19163             cls: 'datepicker-months',
19164             cn: [
19165             {
19166                 tag: 'table',
19167                 cls: 'table-condensed',
19168                 cn:[
19169                 Roo.bootstrap.DateField.head,
19170                 Roo.bootstrap.DateField.content,
19171                 Roo.bootstrap.DateField.footer
19172                 ]
19173             }
19174             ]
19175         },
19176         {
19177             tag: 'div',
19178             cls: 'datepicker-years',
19179             cn: [
19180             {
19181                 tag: 'table',
19182                 cls: 'table-condensed',
19183                 cn:[
19184                 Roo.bootstrap.DateField.head,
19185                 Roo.bootstrap.DateField.content,
19186                 Roo.bootstrap.DateField.footer
19187                 ]
19188             }
19189             ]
19190         }
19191         ]
19192     }
19193 });
19194
19195  
19196
19197  /*
19198  * - LGPL
19199  *
19200  * TimeField
19201  * 
19202  */
19203
19204 /**
19205  * @class Roo.bootstrap.TimeField
19206  * @extends Roo.bootstrap.Input
19207  * Bootstrap DateField class
19208  * 
19209  * 
19210  * @constructor
19211  * Create a new TimeField
19212  * @param {Object} config The config object
19213  */
19214
19215 Roo.bootstrap.TimeField = function(config){
19216     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19217     this.addEvents({
19218             /**
19219              * @event show
19220              * Fires when this field show.
19221              * @param {Roo.bootstrap.DateField} thisthis
19222              * @param {Mixed} date The date value
19223              */
19224             show : true,
19225             /**
19226              * @event show
19227              * Fires when this field hide.
19228              * @param {Roo.bootstrap.DateField} this
19229              * @param {Mixed} date The date value
19230              */
19231             hide : true,
19232             /**
19233              * @event select
19234              * Fires when select a date.
19235              * @param {Roo.bootstrap.DateField} this
19236              * @param {Mixed} date The date value
19237              */
19238             select : true
19239         });
19240 };
19241
19242 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19243     
19244     /**
19245      * @cfg {String} format
19246      * The default time format string which can be overriden for localization support.  The format must be
19247      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19248      */
19249     format : "H:i",
19250        
19251     onRender: function(ct, position)
19252     {
19253         
19254         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19255                 
19256         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19257         
19258         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19259         
19260         this.pop = this.picker().select('>.datepicker-time',true).first();
19261         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19262         
19263         this.picker().on('mousedown', this.onMousedown, this);
19264         this.picker().on('click', this.onClick, this);
19265         
19266         this.picker().addClass('datepicker-dropdown');
19267     
19268         this.fillTime();
19269         this.update();
19270             
19271         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19272         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19273         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19274         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19275         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19276         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19277
19278     },
19279     
19280     fireKey: function(e){
19281         if (!this.picker().isVisible()){
19282             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19283                 this.show();
19284             }
19285             return;
19286         }
19287
19288         e.preventDefault();
19289         
19290         switch(e.keyCode){
19291             case 27: // escape
19292                 this.hide();
19293                 break;
19294             case 37: // left
19295             case 39: // right
19296                 this.onTogglePeriod();
19297                 break;
19298             case 38: // up
19299                 this.onIncrementMinutes();
19300                 break;
19301             case 40: // down
19302                 this.onDecrementMinutes();
19303                 break;
19304             case 13: // enter
19305             case 9: // tab
19306                 this.setTime();
19307                 break;
19308         }
19309     },
19310     
19311     onClick: function(e) {
19312         e.stopPropagation();
19313         e.preventDefault();
19314     },
19315     
19316     picker : function()
19317     {
19318         return this.el.select('.datepicker', true).first();
19319     },
19320     
19321     fillTime: function()
19322     {    
19323         var time = this.pop.select('tbody', true).first();
19324         
19325         time.dom.innerHTML = '';
19326         
19327         time.createChild({
19328             tag: 'tr',
19329             cn: [
19330                 {
19331                     tag: 'td',
19332                     cn: [
19333                         {
19334                             tag: 'a',
19335                             href: '#',
19336                             cls: 'btn',
19337                             cn: [
19338                                 {
19339                                     tag: 'span',
19340                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19341                                 }
19342                             ]
19343                         } 
19344                     ]
19345                 },
19346                 {
19347                     tag: 'td',
19348                     cls: 'separator'
19349                 },
19350                 {
19351                     tag: 'td',
19352                     cn: [
19353                         {
19354                             tag: 'a',
19355                             href: '#',
19356                             cls: 'btn',
19357                             cn: [
19358                                 {
19359                                     tag: 'span',
19360                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19361                                 }
19362                             ]
19363                         }
19364                     ]
19365                 },
19366                 {
19367                     tag: 'td',
19368                     cls: 'separator'
19369                 }
19370             ]
19371         });
19372         
19373         time.createChild({
19374             tag: 'tr',
19375             cn: [
19376                 {
19377                     tag: 'td',
19378                     cn: [
19379                         {
19380                             tag: 'span',
19381                             cls: 'timepicker-hour',
19382                             html: '00'
19383                         }  
19384                     ]
19385                 },
19386                 {
19387                     tag: 'td',
19388                     cls: 'separator',
19389                     html: ':'
19390                 },
19391                 {
19392                     tag: 'td',
19393                     cn: [
19394                         {
19395                             tag: 'span',
19396                             cls: 'timepicker-minute',
19397                             html: '00'
19398                         }  
19399                     ]
19400                 },
19401                 {
19402                     tag: 'td',
19403                     cls: 'separator'
19404                 },
19405                 {
19406                     tag: 'td',
19407                     cn: [
19408                         {
19409                             tag: 'button',
19410                             type: 'button',
19411                             cls: 'btn btn-primary period',
19412                             html: 'AM'
19413                             
19414                         }
19415                     ]
19416                 }
19417             ]
19418         });
19419         
19420         time.createChild({
19421             tag: 'tr',
19422             cn: [
19423                 {
19424                     tag: 'td',
19425                     cn: [
19426                         {
19427                             tag: 'a',
19428                             href: '#',
19429                             cls: 'btn',
19430                             cn: [
19431                                 {
19432                                     tag: 'span',
19433                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19434                                 }
19435                             ]
19436                         }
19437                     ]
19438                 },
19439                 {
19440                     tag: 'td',
19441                     cls: 'separator'
19442                 },
19443                 {
19444                     tag: 'td',
19445                     cn: [
19446                         {
19447                             tag: 'a',
19448                             href: '#',
19449                             cls: 'btn',
19450                             cn: [
19451                                 {
19452                                     tag: 'span',
19453                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19454                                 }
19455                             ]
19456                         }
19457                     ]
19458                 },
19459                 {
19460                     tag: 'td',
19461                     cls: 'separator'
19462                 }
19463             ]
19464         });
19465         
19466     },
19467     
19468     update: function()
19469     {
19470         
19471         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19472         
19473         this.fill();
19474     },
19475     
19476     fill: function() 
19477     {
19478         var hours = this.time.getHours();
19479         var minutes = this.time.getMinutes();
19480         var period = 'AM';
19481         
19482         if(hours > 11){
19483             period = 'PM';
19484         }
19485         
19486         if(hours == 0){
19487             hours = 12;
19488         }
19489         
19490         
19491         if(hours > 12){
19492             hours = hours - 12;
19493         }
19494         
19495         if(hours < 10){
19496             hours = '0' + hours;
19497         }
19498         
19499         if(minutes < 10){
19500             minutes = '0' + minutes;
19501         }
19502         
19503         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19504         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19505         this.pop.select('button', true).first().dom.innerHTML = period;
19506         
19507     },
19508     
19509     place: function()
19510     {   
19511         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19512         
19513         var cls = ['bottom'];
19514         
19515         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19516             cls.pop();
19517             cls.push('top');
19518         }
19519         
19520         cls.push('right');
19521         
19522         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19523             cls.pop();
19524             cls.push('left');
19525         }
19526         
19527         this.picker().addClass(cls.join('-'));
19528         
19529         var _this = this;
19530         
19531         Roo.each(cls, function(c){
19532             if(c == 'bottom'){
19533                 _this.picker().setTop(_this.inputEl().getHeight());
19534                 return;
19535             }
19536             if(c == 'top'){
19537                 _this.picker().setTop(0 - _this.picker().getHeight());
19538                 return;
19539             }
19540             
19541             if(c == 'left'){
19542                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19543                 return;
19544             }
19545             if(c == 'right'){
19546                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19547                 return;
19548             }
19549         });
19550         
19551     },
19552   
19553     onFocus : function()
19554     {
19555         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19556         this.show();
19557     },
19558     
19559     onBlur : function()
19560     {
19561         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19562         this.hide();
19563     },
19564     
19565     show : function()
19566     {
19567         this.picker().show();
19568         this.pop.show();
19569         this.update();
19570         this.place();
19571         
19572         this.fireEvent('show', this, this.date);
19573     },
19574     
19575     hide : function()
19576     {
19577         this.picker().hide();
19578         this.pop.hide();
19579         
19580         this.fireEvent('hide', this, this.date);
19581     },
19582     
19583     setTime : function()
19584     {
19585         this.hide();
19586         this.setValue(this.time.format(this.format));
19587         
19588         this.fireEvent('select', this, this.date);
19589         
19590         
19591     },
19592     
19593     onMousedown: function(e){
19594         e.stopPropagation();
19595         e.preventDefault();
19596     },
19597     
19598     onIncrementHours: function()
19599     {
19600         Roo.log('onIncrementHours');
19601         this.time = this.time.add(Date.HOUR, 1);
19602         this.update();
19603         
19604     },
19605     
19606     onDecrementHours: function()
19607     {
19608         Roo.log('onDecrementHours');
19609         this.time = this.time.add(Date.HOUR, -1);
19610         this.update();
19611     },
19612     
19613     onIncrementMinutes: function()
19614     {
19615         Roo.log('onIncrementMinutes');
19616         this.time = this.time.add(Date.MINUTE, 1);
19617         this.update();
19618     },
19619     
19620     onDecrementMinutes: function()
19621     {
19622         Roo.log('onDecrementMinutes');
19623         this.time = this.time.add(Date.MINUTE, -1);
19624         this.update();
19625     },
19626     
19627     onTogglePeriod: function()
19628     {
19629         Roo.log('onTogglePeriod');
19630         this.time = this.time.add(Date.HOUR, 12);
19631         this.update();
19632     }
19633     
19634    
19635 });
19636
19637 Roo.apply(Roo.bootstrap.TimeField,  {
19638     
19639     content : {
19640         tag: 'tbody',
19641         cn: [
19642             {
19643                 tag: 'tr',
19644                 cn: [
19645                 {
19646                     tag: 'td',
19647                     colspan: '7'
19648                 }
19649                 ]
19650             }
19651         ]
19652     },
19653     
19654     footer : {
19655         tag: 'tfoot',
19656         cn: [
19657             {
19658                 tag: 'tr',
19659                 cn: [
19660                 {
19661                     tag: 'th',
19662                     colspan: '7',
19663                     cls: '',
19664                     cn: [
19665                         {
19666                             tag: 'button',
19667                             cls: 'btn btn-info ok',
19668                             html: 'OK'
19669                         }
19670                     ]
19671                 }
19672
19673                 ]
19674             }
19675         ]
19676     }
19677 });
19678
19679 Roo.apply(Roo.bootstrap.TimeField,  {
19680   
19681     template : {
19682         tag: 'div',
19683         cls: 'datepicker dropdown-menu',
19684         cn: [
19685             {
19686                 tag: 'div',
19687                 cls: 'datepicker-time',
19688                 cn: [
19689                 {
19690                     tag: 'table',
19691                     cls: 'table-condensed',
19692                     cn:[
19693                     Roo.bootstrap.TimeField.content,
19694                     Roo.bootstrap.TimeField.footer
19695                     ]
19696                 }
19697                 ]
19698             }
19699         ]
19700     }
19701 });
19702
19703  
19704
19705  /*
19706  * - LGPL
19707  *
19708  * MonthField
19709  * 
19710  */
19711
19712 /**
19713  * @class Roo.bootstrap.MonthField
19714  * @extends Roo.bootstrap.Input
19715  * Bootstrap MonthField class
19716  * 
19717  * @cfg {String} language default en
19718  * 
19719  * @constructor
19720  * Create a new MonthField
19721  * @param {Object} config The config object
19722  */
19723
19724 Roo.bootstrap.MonthField = function(config){
19725     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19726     
19727     this.addEvents({
19728         /**
19729          * @event show
19730          * Fires when this field show.
19731          * @param {Roo.bootstrap.MonthField} this
19732          * @param {Mixed} date The date value
19733          */
19734         show : true,
19735         /**
19736          * @event show
19737          * Fires when this field hide.
19738          * @param {Roo.bootstrap.MonthField} this
19739          * @param {Mixed} date The date value
19740          */
19741         hide : true,
19742         /**
19743          * @event select
19744          * Fires when select a date.
19745          * @param {Roo.bootstrap.MonthField} this
19746          * @param {String} oldvalue The old value
19747          * @param {String} newvalue The new value
19748          */
19749         select : true
19750     });
19751 };
19752
19753 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19754     
19755     onRender: function(ct, position)
19756     {
19757         
19758         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19759         
19760         this.language = this.language || 'en';
19761         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19762         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19763         
19764         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19765         this.isInline = false;
19766         this.isInput = true;
19767         this.component = this.el.select('.add-on', true).first() || false;
19768         this.component = (this.component && this.component.length === 0) ? false : this.component;
19769         this.hasInput = this.component && this.inputEL().length;
19770         
19771         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19772         
19773         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19774         
19775         this.picker().on('mousedown', this.onMousedown, this);
19776         this.picker().on('click', this.onClick, this);
19777         
19778         this.picker().addClass('datepicker-dropdown');
19779         
19780         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19781             v.setStyle('width', '189px');
19782         });
19783         
19784         this.fillMonths();
19785         
19786         this.update();
19787         
19788         if(this.isInline) {
19789             this.show();
19790         }
19791         
19792     },
19793     
19794     setValue: function(v, suppressEvent)
19795     {   
19796         var o = this.getValue();
19797         
19798         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19799         
19800         this.update();
19801
19802         if(suppressEvent !== true){
19803             this.fireEvent('select', this, o, v);
19804         }
19805         
19806     },
19807     
19808     getValue: function()
19809     {
19810         return this.value;
19811     },
19812     
19813     onClick: function(e) 
19814     {
19815         e.stopPropagation();
19816         e.preventDefault();
19817         
19818         var target = e.getTarget();
19819         
19820         if(target.nodeName.toLowerCase() === 'i'){
19821             target = Roo.get(target).dom.parentNode;
19822         }
19823         
19824         var nodeName = target.nodeName;
19825         var className = target.className;
19826         var html = target.innerHTML;
19827         
19828         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19829             return;
19830         }
19831         
19832         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19833         
19834         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19835         
19836         this.hide();
19837                         
19838     },
19839     
19840     picker : function()
19841     {
19842         return this.pickerEl;
19843     },
19844     
19845     fillMonths: function()
19846     {    
19847         var i = 0;
19848         var months = this.picker().select('>.datepicker-months td', true).first();
19849         
19850         months.dom.innerHTML = '';
19851         
19852         while (i < 12) {
19853             var month = {
19854                 tag: 'span',
19855                 cls: 'month',
19856                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19857             };
19858             
19859             months.createChild(month);
19860         }
19861         
19862     },
19863     
19864     update: function()
19865     {
19866         var _this = this;
19867         
19868         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19869             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19870         }
19871         
19872         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19873             e.removeClass('active');
19874             
19875             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19876                 e.addClass('active');
19877             }
19878         })
19879     },
19880     
19881     place: function()
19882     {
19883         if(this.isInline) {
19884             return;
19885         }
19886         
19887         this.picker().removeClass(['bottom', 'top']);
19888         
19889         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19890             /*
19891              * place to the top of element!
19892              *
19893              */
19894             
19895             this.picker().addClass('top');
19896             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19897             
19898             return;
19899         }
19900         
19901         this.picker().addClass('bottom');
19902         
19903         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19904     },
19905     
19906     onFocus : function()
19907     {
19908         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19909         this.show();
19910     },
19911     
19912     onBlur : function()
19913     {
19914         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19915         
19916         var d = this.inputEl().getValue();
19917         
19918         this.setValue(d);
19919                 
19920         this.hide();
19921     },
19922     
19923     show : function()
19924     {
19925         this.picker().show();
19926         this.picker().select('>.datepicker-months', true).first().show();
19927         this.update();
19928         this.place();
19929         
19930         this.fireEvent('show', this, this.date);
19931     },
19932     
19933     hide : function()
19934     {
19935         if(this.isInline) {
19936             return;
19937         }
19938         this.picker().hide();
19939         this.fireEvent('hide', this, this.date);
19940         
19941     },
19942     
19943     onMousedown: function(e)
19944     {
19945         e.stopPropagation();
19946         e.preventDefault();
19947     },
19948     
19949     keyup: function(e)
19950     {
19951         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19952         this.update();
19953     },
19954
19955     fireKey: function(e)
19956     {
19957         if (!this.picker().isVisible()){
19958             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19959                 this.show();
19960             }
19961             return;
19962         }
19963         
19964         var dir;
19965         
19966         switch(e.keyCode){
19967             case 27: // escape
19968                 this.hide();
19969                 e.preventDefault();
19970                 break;
19971             case 37: // left
19972             case 39: // right
19973                 dir = e.keyCode == 37 ? -1 : 1;
19974                 
19975                 this.vIndex = this.vIndex + dir;
19976                 
19977                 if(this.vIndex < 0){
19978                     this.vIndex = 0;
19979                 }
19980                 
19981                 if(this.vIndex > 11){
19982                     this.vIndex = 11;
19983                 }
19984                 
19985                 if(isNaN(this.vIndex)){
19986                     this.vIndex = 0;
19987                 }
19988                 
19989                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19990                 
19991                 break;
19992             case 38: // up
19993             case 40: // down
19994                 
19995                 dir = e.keyCode == 38 ? -1 : 1;
19996                 
19997                 this.vIndex = this.vIndex + dir * 4;
19998                 
19999                 if(this.vIndex < 0){
20000                     this.vIndex = 0;
20001                 }
20002                 
20003                 if(this.vIndex > 11){
20004                     this.vIndex = 11;
20005                 }
20006                 
20007                 if(isNaN(this.vIndex)){
20008                     this.vIndex = 0;
20009                 }
20010                 
20011                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20012                 break;
20013                 
20014             case 13: // enter
20015                 
20016                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20017                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20018                 }
20019                 
20020                 this.hide();
20021                 e.preventDefault();
20022                 break;
20023             case 9: // tab
20024                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20025                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20026                 }
20027                 this.hide();
20028                 break;
20029             case 16: // shift
20030             case 17: // ctrl
20031             case 18: // alt
20032                 break;
20033             default :
20034                 this.hide();
20035                 
20036         }
20037     },
20038     
20039     remove: function() 
20040     {
20041         this.picker().remove();
20042     }
20043    
20044 });
20045
20046 Roo.apply(Roo.bootstrap.MonthField,  {
20047     
20048     content : {
20049         tag: 'tbody',
20050         cn: [
20051         {
20052             tag: 'tr',
20053             cn: [
20054             {
20055                 tag: 'td',
20056                 colspan: '7'
20057             }
20058             ]
20059         }
20060         ]
20061     },
20062     
20063     dates:{
20064         en: {
20065             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20066             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20067         }
20068     }
20069 });
20070
20071 Roo.apply(Roo.bootstrap.MonthField,  {
20072   
20073     template : {
20074         tag: 'div',
20075         cls: 'datepicker dropdown-menu roo-dynamic',
20076         cn: [
20077             {
20078                 tag: 'div',
20079                 cls: 'datepicker-months',
20080                 cn: [
20081                 {
20082                     tag: 'table',
20083                     cls: 'table-condensed',
20084                     cn:[
20085                         Roo.bootstrap.DateField.content
20086                     ]
20087                 }
20088                 ]
20089             }
20090         ]
20091     }
20092 });
20093
20094  
20095
20096  
20097  /*
20098  * - LGPL
20099  *
20100  * CheckBox
20101  * 
20102  */
20103
20104 /**
20105  * @class Roo.bootstrap.CheckBox
20106  * @extends Roo.bootstrap.Input
20107  * Bootstrap CheckBox class
20108  * 
20109  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20110  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20111  * @cfg {String} boxLabel The text that appears beside the checkbox
20112  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20113  * @cfg {Boolean} checked initnal the element
20114  * @cfg {Boolean} inline inline the element (default false)
20115  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20116  * 
20117  * @constructor
20118  * Create a new CheckBox
20119  * @param {Object} config The config object
20120  */
20121
20122 Roo.bootstrap.CheckBox = function(config){
20123     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20124    
20125     this.addEvents({
20126         /**
20127         * @event check
20128         * Fires when the element is checked or unchecked.
20129         * @param {Roo.bootstrap.CheckBox} this This input
20130         * @param {Boolean} checked The new checked value
20131         */
20132        check : true
20133     });
20134     
20135 };
20136
20137 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20138   
20139     inputType: 'checkbox',
20140     inputValue: 1,
20141     valueOff: 0,
20142     boxLabel: false,
20143     checked: false,
20144     weight : false,
20145     inline: false,
20146     
20147     getAutoCreate : function()
20148     {
20149         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20150         
20151         var id = Roo.id();
20152         
20153         var cfg = {};
20154         
20155         cfg.cls = 'form-group ' + this.inputType; //input-group
20156         
20157         if(this.inline){
20158             cfg.cls += ' ' + this.inputType + '-inline';
20159         }
20160         
20161         var input =  {
20162             tag: 'input',
20163             id : id,
20164             type : this.inputType,
20165             value : this.inputValue,
20166             cls : 'roo-' + this.inputType, //'form-box',
20167             placeholder : this.placeholder || ''
20168             
20169         };
20170         
20171         if(this.inputType != 'radio'){
20172             var hidden =  {
20173                 tag: 'input',
20174                 type : 'hidden',
20175                 cls : 'roo-hidden-value',
20176                 value : this.checked ? this.valueOff : this.inputValue
20177             };
20178         }
20179         
20180             
20181         if (this.weight) { // Validity check?
20182             cfg.cls += " " + this.inputType + "-" + this.weight;
20183         }
20184         
20185         if (this.disabled) {
20186             input.disabled=true;
20187         }
20188         
20189         if(this.checked){
20190             input.checked = this.checked;
20191             
20192         }
20193         
20194         
20195         if (this.name) {
20196             
20197             input.name = this.name;
20198             
20199             if(this.inputType != 'radio'){
20200                 hidden.name = this.name;
20201                 input.name = '_hidden_' + this.name;
20202             }
20203         }
20204         
20205         if (this.size) {
20206             input.cls += ' input-' + this.size;
20207         }
20208         
20209         var settings=this;
20210         
20211         ['xs','sm','md','lg'].map(function(size){
20212             if (settings[size]) {
20213                 cfg.cls += ' col-' + size + '-' + settings[size];
20214             }
20215         });
20216         
20217         var inputblock = input;
20218          
20219         if (this.before || this.after) {
20220             
20221             inputblock = {
20222                 cls : 'input-group',
20223                 cn :  [] 
20224             };
20225             
20226             if (this.before) {
20227                 inputblock.cn.push({
20228                     tag :'span',
20229                     cls : 'input-group-addon',
20230                     html : this.before
20231                 });
20232             }
20233             
20234             inputblock.cn.push(input);
20235             
20236             if(this.inputType != 'radio'){
20237                 inputblock.cn.push(hidden);
20238             }
20239             
20240             if (this.after) {
20241                 inputblock.cn.push({
20242                     tag :'span',
20243                     cls : 'input-group-addon',
20244                     html : this.after
20245                 });
20246             }
20247             
20248         }
20249         
20250         if (align ==='left' && this.fieldLabel.length) {
20251 //                Roo.log("left and has label");
20252             cfg.cn = [
20253                 {
20254                     tag: 'label',
20255                     'for' :  id,
20256                     cls : 'control-label',
20257                     html : this.fieldLabel
20258
20259                 },
20260                 {
20261                     cls : "", 
20262                     cn: [
20263                         inputblock
20264                     ]
20265                 }
20266             ];
20267             
20268             if(this.labelWidth > 12){
20269                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20270             }
20271             
20272             if(this.labelWidth < 13 && this.labelmd == 0){
20273                 this.labelmd = this.labelWidth;
20274             }
20275             
20276             if(this.labellg > 0){
20277                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20278                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20279             }
20280             
20281             if(this.labelmd > 0){
20282                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20283                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20284             }
20285             
20286             if(this.labelsm > 0){
20287                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20288                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20289             }
20290             
20291             if(this.labelxs > 0){
20292                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20293                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20294             }
20295             
20296         } else if ( this.fieldLabel.length) {
20297 //                Roo.log(" label");
20298                 cfg.cn = [
20299                    
20300                     {
20301                         tag: this.boxLabel ? 'span' : 'label',
20302                         'for': id,
20303                         cls: 'control-label box-input-label',
20304                         //cls : 'input-group-addon',
20305                         html : this.fieldLabel
20306                         
20307                     },
20308                     
20309                     inputblock
20310                     
20311                 ];
20312
20313         } else {
20314             
20315 //                Roo.log(" no label && no align");
20316                 cfg.cn = [  inputblock ] ;
20317                 
20318                 
20319         }
20320         
20321         if(this.boxLabel){
20322              var boxLabelCfg = {
20323                 tag: 'label',
20324                 //'for': id, // box label is handled by onclick - so no for...
20325                 cls: 'box-label',
20326                 html: this.boxLabel
20327             };
20328             
20329             if(this.tooltip){
20330                 boxLabelCfg.tooltip = this.tooltip;
20331             }
20332              
20333             cfg.cn.push(boxLabelCfg);
20334         }
20335         
20336         if(this.inputType != 'radio'){
20337             cfg.cn.push(hidden);
20338         }
20339         
20340         return cfg;
20341         
20342     },
20343     
20344     /**
20345      * return the real input element.
20346      */
20347     inputEl: function ()
20348     {
20349         return this.el.select('input.roo-' + this.inputType,true).first();
20350     },
20351     hiddenEl: function ()
20352     {
20353         return this.el.select('input.roo-hidden-value',true).first();
20354     },
20355     
20356     labelEl: function()
20357     {
20358         return this.el.select('label.control-label',true).first();
20359     },
20360     /* depricated... */
20361     
20362     label: function()
20363     {
20364         return this.labelEl();
20365     },
20366     
20367     boxLabelEl: function()
20368     {
20369         return this.el.select('label.box-label',true).first();
20370     },
20371     
20372     initEvents : function()
20373     {
20374 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20375         
20376         this.inputEl().on('click', this.onClick,  this);
20377         
20378         if (this.boxLabel) { 
20379             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20380         }
20381         
20382         this.startValue = this.getValue();
20383         
20384         if(this.groupId){
20385             Roo.bootstrap.CheckBox.register(this);
20386         }
20387     },
20388     
20389     onClick : function()
20390     {   
20391         this.setChecked(!this.checked);
20392     },
20393     
20394     setChecked : function(state,suppressEvent)
20395     {
20396         this.startValue = this.getValue();
20397
20398         if(this.inputType == 'radio'){
20399             
20400             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20401                 e.dom.checked = false;
20402             });
20403             
20404             this.inputEl().dom.checked = true;
20405             
20406             this.inputEl().dom.value = this.inputValue;
20407             
20408             if(suppressEvent !== true){
20409                 this.fireEvent('check', this, true);
20410             }
20411             
20412             this.validate();
20413             
20414             return;
20415         }
20416         
20417         this.checked = state;
20418         
20419         this.inputEl().dom.checked = state;
20420         
20421         
20422         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20423         
20424         if(suppressEvent !== true){
20425             this.fireEvent('check', this, state);
20426         }
20427         
20428         this.validate();
20429     },
20430     
20431     getValue : function()
20432     {
20433         if(this.inputType == 'radio'){
20434             return this.getGroupValue();
20435         }
20436         
20437         return this.hiddenEl().dom.value;
20438         
20439     },
20440     
20441     getGroupValue : function()
20442     {
20443         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20444             return '';
20445         }
20446         
20447         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20448     },
20449     
20450     setValue : function(v,suppressEvent)
20451     {
20452         if(this.inputType == 'radio'){
20453             this.setGroupValue(v, suppressEvent);
20454             return;
20455         }
20456         
20457         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20458         
20459         this.validate();
20460     },
20461     
20462     setGroupValue : function(v, suppressEvent)
20463     {
20464         this.startValue = this.getValue();
20465         
20466         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20467             e.dom.checked = false;
20468             
20469             if(e.dom.value == v){
20470                 e.dom.checked = true;
20471             }
20472         });
20473         
20474         if(suppressEvent !== true){
20475             this.fireEvent('check', this, true);
20476         }
20477
20478         this.validate();
20479         
20480         return;
20481     },
20482     
20483     validate : function()
20484     {
20485         if(
20486                 this.disabled || 
20487                 (this.inputType == 'radio' && this.validateRadio()) ||
20488                 (this.inputType == 'checkbox' && this.validateCheckbox())
20489         ){
20490             this.markValid();
20491             return true;
20492         }
20493         
20494         this.markInvalid();
20495         return false;
20496     },
20497     
20498     validateRadio : function()
20499     {
20500         if(this.allowBlank){
20501             return true;
20502         }
20503         
20504         var valid = false;
20505         
20506         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20507             if(!e.dom.checked){
20508                 return;
20509             }
20510             
20511             valid = true;
20512             
20513             return false;
20514         });
20515         
20516         return valid;
20517     },
20518     
20519     validateCheckbox : function()
20520     {
20521         if(!this.groupId){
20522             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20523             //return (this.getValue() == this.inputValue) ? true : false;
20524         }
20525         
20526         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20527         
20528         if(!group){
20529             return false;
20530         }
20531         
20532         var r = false;
20533         
20534         for(var i in group){
20535             if(r){
20536                 break;
20537             }
20538             
20539             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20540         }
20541         
20542         return r;
20543     },
20544     
20545     /**
20546      * Mark this field as valid
20547      */
20548     markValid : function()
20549     {
20550         var _this = this;
20551         
20552         this.fireEvent('valid', this);
20553         
20554         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20555         
20556         if(this.groupId){
20557             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20558         }
20559         
20560         if(label){
20561             label.markValid();
20562         }
20563
20564         if(this.inputType == 'radio'){
20565             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20566                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20567                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20568             });
20569             
20570             return;
20571         }
20572
20573         if(!this.groupId){
20574             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20575             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20576             return;
20577         }
20578         
20579         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20580         
20581         if(!group){
20582             return;
20583         }
20584         
20585         for(var i in group){
20586             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20587             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20588         }
20589     },
20590     
20591      /**
20592      * Mark this field as invalid
20593      * @param {String} msg The validation message
20594      */
20595     markInvalid : function(msg)
20596     {
20597         if(this.allowBlank){
20598             return;
20599         }
20600         
20601         var _this = this;
20602         
20603         this.fireEvent('invalid', this, msg);
20604         
20605         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20606         
20607         if(this.groupId){
20608             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20609         }
20610         
20611         if(label){
20612             label.markInvalid();
20613         }
20614             
20615         if(this.inputType == 'radio'){
20616             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20617                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20618                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20619             });
20620             
20621             return;
20622         }
20623         
20624         if(!this.groupId){
20625             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20626             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20627             return;
20628         }
20629         
20630         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20631         
20632         if(!group){
20633             return;
20634         }
20635         
20636         for(var i in group){
20637             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20638             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20639         }
20640         
20641     },
20642     
20643     clearInvalid : function()
20644     {
20645         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20646         
20647         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20648         
20649         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20650         
20651         if (label) {
20652             label.iconEl.removeClass(label.validClass);
20653             label.iconEl.removeClass(label.invalidClass);
20654         }
20655     },
20656     
20657     disable : function()
20658     {
20659         if(this.inputType != 'radio'){
20660             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20661             return;
20662         }
20663         
20664         var _this = this;
20665         
20666         if(this.rendered){
20667             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20668                 _this.getActionEl().addClass(this.disabledClass);
20669                 e.dom.disabled = true;
20670             });
20671         }
20672         
20673         this.disabled = true;
20674         this.fireEvent("disable", this);
20675         return this;
20676     },
20677
20678     enable : function()
20679     {
20680         if(this.inputType != 'radio'){
20681             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20682             return;
20683         }
20684         
20685         var _this = this;
20686         
20687         if(this.rendered){
20688             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20689                 _this.getActionEl().removeClass(this.disabledClass);
20690                 e.dom.disabled = false;
20691             });
20692         }
20693         
20694         this.disabled = false;
20695         this.fireEvent("enable", this);
20696         return this;
20697     }
20698
20699 });
20700
20701 Roo.apply(Roo.bootstrap.CheckBox, {
20702     
20703     groups: {},
20704     
20705      /**
20706     * register a CheckBox Group
20707     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20708     */
20709     register : function(checkbox)
20710     {
20711         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20712             this.groups[checkbox.groupId] = {};
20713         }
20714         
20715         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20716             return;
20717         }
20718         
20719         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20720         
20721     },
20722     /**
20723     * fetch a CheckBox Group based on the group ID
20724     * @param {string} the group ID
20725     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20726     */
20727     get: function(groupId) {
20728         if (typeof(this.groups[groupId]) == 'undefined') {
20729             return false;
20730         }
20731         
20732         return this.groups[groupId] ;
20733     }
20734     
20735     
20736 });
20737 /*
20738  * - LGPL
20739  *
20740  * RadioItem
20741  * 
20742  */
20743
20744 /**
20745  * @class Roo.bootstrap.Radio
20746  * @extends Roo.bootstrap.Component
20747  * Bootstrap Radio class
20748  * @cfg {String} boxLabel - the label associated
20749  * @cfg {String} value - the value of radio
20750  * 
20751  * @constructor
20752  * Create a new Radio
20753  * @param {Object} config The config object
20754  */
20755 Roo.bootstrap.Radio = function(config){
20756     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20757     
20758 };
20759
20760 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20761     
20762     boxLabel : '',
20763     
20764     value : '',
20765     
20766     getAutoCreate : function()
20767     {
20768         var cfg = {
20769             tag : 'div',
20770             cls : 'form-group radio',
20771             cn : [
20772                 {
20773                     tag : 'label',
20774                     cls : 'box-label',
20775                     html : this.boxLabel
20776                 }
20777             ]
20778         };
20779         
20780         return cfg;
20781     },
20782     
20783     initEvents : function() 
20784     {
20785         this.parent().register(this);
20786         
20787         this.el.on('click', this.onClick, this);
20788         
20789     },
20790     
20791     onClick : function()
20792     {
20793         this.setChecked(true);
20794     },
20795     
20796     setChecked : function(state, suppressEvent)
20797     {
20798         this.parent().setValue(this.value, suppressEvent);
20799         
20800     },
20801     
20802     setBoxLabel : function(v)
20803     {
20804         this.boxLabel = v;
20805         
20806         if(this.rendered){
20807             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20808         }
20809     }
20810     
20811 });
20812  
20813
20814  /*
20815  * - LGPL
20816  *
20817  * Input
20818  * 
20819  */
20820
20821 /**
20822  * @class Roo.bootstrap.SecurePass
20823  * @extends Roo.bootstrap.Input
20824  * Bootstrap SecurePass class
20825  *
20826  * 
20827  * @constructor
20828  * Create a new SecurePass
20829  * @param {Object} config The config object
20830  */
20831  
20832 Roo.bootstrap.SecurePass = function (config) {
20833     // these go here, so the translation tool can replace them..
20834     this.errors = {
20835         PwdEmpty: "Please type a password, and then retype it to confirm.",
20836         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20837         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20838         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20839         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20840         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20841         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20842         TooWeak: "Your password is Too Weak."
20843     },
20844     this.meterLabel = "Password strength:";
20845     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20846     this.meterClass = [
20847         "roo-password-meter-tooweak", 
20848         "roo-password-meter-weak", 
20849         "roo-password-meter-medium", 
20850         "roo-password-meter-strong", 
20851         "roo-password-meter-grey"
20852     ];
20853     
20854     this.errors = {};
20855     
20856     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20857 }
20858
20859 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20860     /**
20861      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20862      * {
20863      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20864      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20865      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20866      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20867      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20868      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20869      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20870      * })
20871      */
20872     // private
20873     
20874     meterWidth: 300,
20875     errorMsg :'',    
20876     errors: false,
20877     imageRoot: '/',
20878     /**
20879      * @cfg {String/Object} Label for the strength meter (defaults to
20880      * 'Password strength:')
20881      */
20882     // private
20883     meterLabel: '',
20884     /**
20885      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20886      * ['Weak', 'Medium', 'Strong'])
20887      */
20888     // private    
20889     pwdStrengths: false,    
20890     // private
20891     strength: 0,
20892     // private
20893     _lastPwd: null,
20894     // private
20895     kCapitalLetter: 0,
20896     kSmallLetter: 1,
20897     kDigit: 2,
20898     kPunctuation: 3,
20899     
20900     insecure: false,
20901     // private
20902     initEvents: function ()
20903     {
20904         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20905
20906         if (this.el.is('input[type=password]') && Roo.isSafari) {
20907             this.el.on('keydown', this.SafariOnKeyDown, this);
20908         }
20909
20910         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20911     },
20912     // private
20913     onRender: function (ct, position)
20914     {
20915         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20916         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20917         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20918
20919         this.trigger.createChild({
20920                    cn: [
20921                     {
20922                     //id: 'PwdMeter',
20923                     tag: 'div',
20924                     cls: 'roo-password-meter-grey col-xs-12',
20925                     style: {
20926                         //width: 0,
20927                         //width: this.meterWidth + 'px'                                                
20928                         }
20929                     },
20930                     {                            
20931                          cls: 'roo-password-meter-text'                          
20932                     }
20933                 ]            
20934         });
20935
20936          
20937         if (this.hideTrigger) {
20938             this.trigger.setDisplayed(false);
20939         }
20940         this.setSize(this.width || '', this.height || '');
20941     },
20942     // private
20943     onDestroy: function ()
20944     {
20945         if (this.trigger) {
20946             this.trigger.removeAllListeners();
20947             this.trigger.remove();
20948         }
20949         if (this.wrap) {
20950             this.wrap.remove();
20951         }
20952         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20953     },
20954     // private
20955     checkStrength: function ()
20956     {
20957         var pwd = this.inputEl().getValue();
20958         if (pwd == this._lastPwd) {
20959             return;
20960         }
20961
20962         var strength;
20963         if (this.ClientSideStrongPassword(pwd)) {
20964             strength = 3;
20965         } else if (this.ClientSideMediumPassword(pwd)) {
20966             strength = 2;
20967         } else if (this.ClientSideWeakPassword(pwd)) {
20968             strength = 1;
20969         } else {
20970             strength = 0;
20971         }
20972         
20973         Roo.log('strength1: ' + strength);
20974         
20975         //var pm = this.trigger.child('div/div/div').dom;
20976         var pm = this.trigger.child('div/div');
20977         pm.removeClass(this.meterClass);
20978         pm.addClass(this.meterClass[strength]);
20979                 
20980         
20981         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20982                 
20983         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20984         
20985         this._lastPwd = pwd;
20986     },
20987     reset: function ()
20988     {
20989         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20990         
20991         this._lastPwd = '';
20992         
20993         var pm = this.trigger.child('div/div');
20994         pm.removeClass(this.meterClass);
20995         pm.addClass('roo-password-meter-grey');        
20996         
20997         
20998         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20999         
21000         pt.innerHTML = '';
21001         this.inputEl().dom.type='password';
21002     },
21003     // private
21004     validateValue: function (value)
21005     {
21006         
21007         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21008             return false;
21009         }
21010         if (value.length == 0) {
21011             if (this.allowBlank) {
21012                 this.clearInvalid();
21013                 return true;
21014             }
21015
21016             this.markInvalid(this.errors.PwdEmpty);
21017             this.errorMsg = this.errors.PwdEmpty;
21018             return false;
21019         }
21020         
21021         if(this.insecure){
21022             return true;
21023         }
21024         
21025         if ('[\x21-\x7e]*'.match(value)) {
21026             this.markInvalid(this.errors.PwdBadChar);
21027             this.errorMsg = this.errors.PwdBadChar;
21028             return false;
21029         }
21030         if (value.length < 6) {
21031             this.markInvalid(this.errors.PwdShort);
21032             this.errorMsg = this.errors.PwdShort;
21033             return false;
21034         }
21035         if (value.length > 16) {
21036             this.markInvalid(this.errors.PwdLong);
21037             this.errorMsg = this.errors.PwdLong;
21038             return false;
21039         }
21040         var strength;
21041         if (this.ClientSideStrongPassword(value)) {
21042             strength = 3;
21043         } else if (this.ClientSideMediumPassword(value)) {
21044             strength = 2;
21045         } else if (this.ClientSideWeakPassword(value)) {
21046             strength = 1;
21047         } else {
21048             strength = 0;
21049         }
21050
21051         
21052         if (strength < 2) {
21053             //this.markInvalid(this.errors.TooWeak);
21054             this.errorMsg = this.errors.TooWeak;
21055             //return false;
21056         }
21057         
21058         
21059         console.log('strength2: ' + strength);
21060         
21061         //var pm = this.trigger.child('div/div/div').dom;
21062         
21063         var pm = this.trigger.child('div/div');
21064         pm.removeClass(this.meterClass);
21065         pm.addClass(this.meterClass[strength]);
21066                 
21067         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21068                 
21069         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21070         
21071         this.errorMsg = ''; 
21072         return true;
21073     },
21074     // private
21075     CharacterSetChecks: function (type)
21076     {
21077         this.type = type;
21078         this.fResult = false;
21079     },
21080     // private
21081     isctype: function (character, type)
21082     {
21083         switch (type) {  
21084             case this.kCapitalLetter:
21085                 if (character >= 'A' && character <= 'Z') {
21086                     return true;
21087                 }
21088                 break;
21089             
21090             case this.kSmallLetter:
21091                 if (character >= 'a' && character <= 'z') {
21092                     return true;
21093                 }
21094                 break;
21095             
21096             case this.kDigit:
21097                 if (character >= '0' && character <= '9') {
21098                     return true;
21099                 }
21100                 break;
21101             
21102             case this.kPunctuation:
21103                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21104                     return true;
21105                 }
21106                 break;
21107             
21108             default:
21109                 return false;
21110         }
21111
21112     },
21113     // private
21114     IsLongEnough: function (pwd, size)
21115     {
21116         return !(pwd == null || isNaN(size) || pwd.length < size);
21117     },
21118     // private
21119     SpansEnoughCharacterSets: function (word, nb)
21120     {
21121         if (!this.IsLongEnough(word, nb))
21122         {
21123             return false;
21124         }
21125
21126         var characterSetChecks = new Array(
21127             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21128             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21129         );
21130         
21131         for (var index = 0; index < word.length; ++index) {
21132             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21133                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21134                     characterSetChecks[nCharSet].fResult = true;
21135                     break;
21136                 }
21137             }
21138         }
21139
21140         var nCharSets = 0;
21141         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21142             if (characterSetChecks[nCharSet].fResult) {
21143                 ++nCharSets;
21144             }
21145         }
21146
21147         if (nCharSets < nb) {
21148             return false;
21149         }
21150         return true;
21151     },
21152     // private
21153     ClientSideStrongPassword: function (pwd)
21154     {
21155         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21156     },
21157     // private
21158     ClientSideMediumPassword: function (pwd)
21159     {
21160         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21161     },
21162     // private
21163     ClientSideWeakPassword: function (pwd)
21164     {
21165         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21166     }
21167           
21168 })//<script type="text/javascript">
21169
21170 /*
21171  * Based  Ext JS Library 1.1.1
21172  * Copyright(c) 2006-2007, Ext JS, LLC.
21173  * LGPL
21174  *
21175  */
21176  
21177 /**
21178  * @class Roo.HtmlEditorCore
21179  * @extends Roo.Component
21180  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21181  *
21182  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21183  */
21184
21185 Roo.HtmlEditorCore = function(config){
21186     
21187     
21188     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21189     
21190     
21191     this.addEvents({
21192         /**
21193          * @event initialize
21194          * Fires when the editor is fully initialized (including the iframe)
21195          * @param {Roo.HtmlEditorCore} this
21196          */
21197         initialize: true,
21198         /**
21199          * @event activate
21200          * Fires when the editor is first receives the focus. Any insertion must wait
21201          * until after this event.
21202          * @param {Roo.HtmlEditorCore} this
21203          */
21204         activate: true,
21205          /**
21206          * @event beforesync
21207          * Fires before the textarea is updated with content from the editor iframe. Return false
21208          * to cancel the sync.
21209          * @param {Roo.HtmlEditorCore} this
21210          * @param {String} html
21211          */
21212         beforesync: true,
21213          /**
21214          * @event beforepush
21215          * Fires before the iframe editor is updated with content from the textarea. Return false
21216          * to cancel the push.
21217          * @param {Roo.HtmlEditorCore} this
21218          * @param {String} html
21219          */
21220         beforepush: true,
21221          /**
21222          * @event sync
21223          * Fires when the textarea is updated with content from the editor iframe.
21224          * @param {Roo.HtmlEditorCore} this
21225          * @param {String} html
21226          */
21227         sync: true,
21228          /**
21229          * @event push
21230          * Fires when the iframe editor is updated with content from the textarea.
21231          * @param {Roo.HtmlEditorCore} this
21232          * @param {String} html
21233          */
21234         push: true,
21235         
21236         /**
21237          * @event editorevent
21238          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21239          * @param {Roo.HtmlEditorCore} this
21240          */
21241         editorevent: true
21242         
21243     });
21244     
21245     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21246     
21247     // defaults : white / black...
21248     this.applyBlacklists();
21249     
21250     
21251     
21252 };
21253
21254
21255 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21256
21257
21258      /**
21259      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21260      */
21261     
21262     owner : false,
21263     
21264      /**
21265      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21266      *                        Roo.resizable.
21267      */
21268     resizable : false,
21269      /**
21270      * @cfg {Number} height (in pixels)
21271      */   
21272     height: 300,
21273    /**
21274      * @cfg {Number} width (in pixels)
21275      */   
21276     width: 500,
21277     
21278     /**
21279      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21280      * 
21281      */
21282     stylesheets: false,
21283     
21284     // id of frame..
21285     frameId: false,
21286     
21287     // private properties
21288     validationEvent : false,
21289     deferHeight: true,
21290     initialized : false,
21291     activated : false,
21292     sourceEditMode : false,
21293     onFocus : Roo.emptyFn,
21294     iframePad:3,
21295     hideMode:'offsets',
21296     
21297     clearUp: true,
21298     
21299     // blacklist + whitelisted elements..
21300     black: false,
21301     white: false,
21302      
21303     bodyCls : '',
21304
21305     /**
21306      * Protected method that will not generally be called directly. It
21307      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21308      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21309      */
21310     getDocMarkup : function(){
21311         // body styles..
21312         var st = '';
21313         
21314         // inherit styels from page...?? 
21315         if (this.stylesheets === false) {
21316             
21317             Roo.get(document.head).select('style').each(function(node) {
21318                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21319             });
21320             
21321             Roo.get(document.head).select('link').each(function(node) { 
21322                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21323             });
21324             
21325         } else if (!this.stylesheets.length) {
21326                 // simple..
21327                 st = '<style type="text/css">' +
21328                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21329                    '</style>';
21330         } else { 
21331             st = '<style type="text/css">' +
21332                     this.stylesheets +
21333                 '</style>';
21334         }
21335         
21336         st +=  '<style type="text/css">' +
21337             'IMG { cursor: pointer } ' +
21338         '</style>';
21339
21340         var cls = 'roo-htmleditor-body';
21341         
21342         if(this.bodyCls.length){
21343             cls += ' ' + this.bodyCls;
21344         }
21345         
21346         return '<html><head>' + st  +
21347             //<style type="text/css">' +
21348             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21349             //'</style>' +
21350             ' </head><body class="' +  cls + '"></body></html>';
21351     },
21352
21353     // private
21354     onRender : function(ct, position)
21355     {
21356         var _t = this;
21357         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21358         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21359         
21360         
21361         this.el.dom.style.border = '0 none';
21362         this.el.dom.setAttribute('tabIndex', -1);
21363         this.el.addClass('x-hidden hide');
21364         
21365         
21366         
21367         if(Roo.isIE){ // fix IE 1px bogus margin
21368             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21369         }
21370        
21371         
21372         this.frameId = Roo.id();
21373         
21374          
21375         
21376         var iframe = this.owner.wrap.createChild({
21377             tag: 'iframe',
21378             cls: 'form-control', // bootstrap..
21379             id: this.frameId,
21380             name: this.frameId,
21381             frameBorder : 'no',
21382             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21383         }, this.el
21384         );
21385         
21386         
21387         this.iframe = iframe.dom;
21388
21389          this.assignDocWin();
21390         
21391         this.doc.designMode = 'on';
21392        
21393         this.doc.open();
21394         this.doc.write(this.getDocMarkup());
21395         this.doc.close();
21396
21397         
21398         var task = { // must defer to wait for browser to be ready
21399             run : function(){
21400                 //console.log("run task?" + this.doc.readyState);
21401                 this.assignDocWin();
21402                 if(this.doc.body || this.doc.readyState == 'complete'){
21403                     try {
21404                         this.doc.designMode="on";
21405                     } catch (e) {
21406                         return;
21407                     }
21408                     Roo.TaskMgr.stop(task);
21409                     this.initEditor.defer(10, this);
21410                 }
21411             },
21412             interval : 10,
21413             duration: 10000,
21414             scope: this
21415         };
21416         Roo.TaskMgr.start(task);
21417
21418     },
21419
21420     // private
21421     onResize : function(w, h)
21422     {
21423          Roo.log('resize: ' +w + ',' + h );
21424         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21425         if(!this.iframe){
21426             return;
21427         }
21428         if(typeof w == 'number'){
21429             
21430             this.iframe.style.width = w + 'px';
21431         }
21432         if(typeof h == 'number'){
21433             
21434             this.iframe.style.height = h + 'px';
21435             if(this.doc){
21436                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21437             }
21438         }
21439         
21440     },
21441
21442     /**
21443      * Toggles the editor between standard and source edit mode.
21444      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21445      */
21446     toggleSourceEdit : function(sourceEditMode){
21447         
21448         this.sourceEditMode = sourceEditMode === true;
21449         
21450         if(this.sourceEditMode){
21451  
21452             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21453             
21454         }else{
21455             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21456             //this.iframe.className = '';
21457             this.deferFocus();
21458         }
21459         //this.setSize(this.owner.wrap.getSize());
21460         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21461     },
21462
21463     
21464   
21465
21466     /**
21467      * Protected method that will not generally be called directly. If you need/want
21468      * custom HTML cleanup, this is the method you should override.
21469      * @param {String} html The HTML to be cleaned
21470      * return {String} The cleaned HTML
21471      */
21472     cleanHtml : function(html){
21473         html = String(html);
21474         if(html.length > 5){
21475             if(Roo.isSafari){ // strip safari nonsense
21476                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21477             }
21478         }
21479         if(html == '&nbsp;'){
21480             html = '';
21481         }
21482         return html;
21483     },
21484
21485     /**
21486      * HTML Editor -> Textarea
21487      * Protected method that will not generally be called directly. Syncs the contents
21488      * of the editor iframe with the textarea.
21489      */
21490     syncValue : function(){
21491         if(this.initialized){
21492             var bd = (this.doc.body || this.doc.documentElement);
21493             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21494             var html = bd.innerHTML;
21495             if(Roo.isSafari){
21496                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21497                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21498                 if(m && m[1]){
21499                     html = '<div style="'+m[0]+'">' + html + '</div>';
21500                 }
21501             }
21502             html = this.cleanHtml(html);
21503             // fix up the special chars.. normaly like back quotes in word...
21504             // however we do not want to do this with chinese..
21505             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21506                 var cc = b.charCodeAt();
21507                 if (
21508                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21509                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21510                     (cc >= 0xf900 && cc < 0xfb00 )
21511                 ) {
21512                         return b;
21513                 }
21514                 return "&#"+cc+";" 
21515             });
21516             if(this.owner.fireEvent('beforesync', this, html) !== false){
21517                 this.el.dom.value = html;
21518                 this.owner.fireEvent('sync', this, html);
21519             }
21520         }
21521     },
21522
21523     /**
21524      * Protected method that will not generally be called directly. Pushes the value of the textarea
21525      * into the iframe editor.
21526      */
21527     pushValue : function(){
21528         if(this.initialized){
21529             var v = this.el.dom.value.trim();
21530             
21531 //            if(v.length < 1){
21532 //                v = '&#160;';
21533 //            }
21534             
21535             if(this.owner.fireEvent('beforepush', this, v) !== false){
21536                 var d = (this.doc.body || this.doc.documentElement);
21537                 d.innerHTML = v;
21538                 this.cleanUpPaste();
21539                 this.el.dom.value = d.innerHTML;
21540                 this.owner.fireEvent('push', this, v);
21541             }
21542         }
21543     },
21544
21545     // private
21546     deferFocus : function(){
21547         this.focus.defer(10, this);
21548     },
21549
21550     // doc'ed in Field
21551     focus : function(){
21552         if(this.win && !this.sourceEditMode){
21553             this.win.focus();
21554         }else{
21555             this.el.focus();
21556         }
21557     },
21558     
21559     assignDocWin: function()
21560     {
21561         var iframe = this.iframe;
21562         
21563          if(Roo.isIE){
21564             this.doc = iframe.contentWindow.document;
21565             this.win = iframe.contentWindow;
21566         } else {
21567 //            if (!Roo.get(this.frameId)) {
21568 //                return;
21569 //            }
21570 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21571 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21572             
21573             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21574                 return;
21575             }
21576             
21577             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21578             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21579         }
21580     },
21581     
21582     // private
21583     initEditor : function(){
21584         //console.log("INIT EDITOR");
21585         this.assignDocWin();
21586         
21587         
21588         
21589         this.doc.designMode="on";
21590         this.doc.open();
21591         this.doc.write(this.getDocMarkup());
21592         this.doc.close();
21593         
21594         var dbody = (this.doc.body || this.doc.documentElement);
21595         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21596         // this copies styles from the containing element into thsi one..
21597         // not sure why we need all of this..
21598         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21599         
21600         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21601         //ss['background-attachment'] = 'fixed'; // w3c
21602         dbody.bgProperties = 'fixed'; // ie
21603         //Roo.DomHelper.applyStyles(dbody, ss);
21604         Roo.EventManager.on(this.doc, {
21605             //'mousedown': this.onEditorEvent,
21606             'mouseup': this.onEditorEvent,
21607             'dblclick': this.onEditorEvent,
21608             'click': this.onEditorEvent,
21609             'keyup': this.onEditorEvent,
21610             buffer:100,
21611             scope: this
21612         });
21613         if(Roo.isGecko){
21614             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21615         }
21616         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21617             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21618         }
21619         this.initialized = true;
21620
21621         this.owner.fireEvent('initialize', this);
21622         this.pushValue();
21623     },
21624
21625     // private
21626     onDestroy : function(){
21627         
21628         
21629         
21630         if(this.rendered){
21631             
21632             //for (var i =0; i < this.toolbars.length;i++) {
21633             //    // fixme - ask toolbars for heights?
21634             //    this.toolbars[i].onDestroy();
21635            // }
21636             
21637             //this.wrap.dom.innerHTML = '';
21638             //this.wrap.remove();
21639         }
21640     },
21641
21642     // private
21643     onFirstFocus : function(){
21644         
21645         this.assignDocWin();
21646         
21647         
21648         this.activated = true;
21649          
21650     
21651         if(Roo.isGecko){ // prevent silly gecko errors
21652             this.win.focus();
21653             var s = this.win.getSelection();
21654             if(!s.focusNode || s.focusNode.nodeType != 3){
21655                 var r = s.getRangeAt(0);
21656                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21657                 r.collapse(true);
21658                 this.deferFocus();
21659             }
21660             try{
21661                 this.execCmd('useCSS', true);
21662                 this.execCmd('styleWithCSS', false);
21663             }catch(e){}
21664         }
21665         this.owner.fireEvent('activate', this);
21666     },
21667
21668     // private
21669     adjustFont: function(btn){
21670         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21671         //if(Roo.isSafari){ // safari
21672         //    adjust *= 2;
21673        // }
21674         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21675         if(Roo.isSafari){ // safari
21676             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21677             v =  (v < 10) ? 10 : v;
21678             v =  (v > 48) ? 48 : v;
21679             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21680             
21681         }
21682         
21683         
21684         v = Math.max(1, v+adjust);
21685         
21686         this.execCmd('FontSize', v  );
21687     },
21688
21689     onEditorEvent : function(e)
21690     {
21691         this.owner.fireEvent('editorevent', this, e);
21692       //  this.updateToolbar();
21693         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21694     },
21695
21696     insertTag : function(tg)
21697     {
21698         // could be a bit smarter... -> wrap the current selected tRoo..
21699         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21700             
21701             range = this.createRange(this.getSelection());
21702             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21703             wrappingNode.appendChild(range.extractContents());
21704             range.insertNode(wrappingNode);
21705
21706             return;
21707             
21708             
21709             
21710         }
21711         this.execCmd("formatblock",   tg);
21712         
21713     },
21714     
21715     insertText : function(txt)
21716     {
21717         
21718         
21719         var range = this.createRange();
21720         range.deleteContents();
21721                //alert(Sender.getAttribute('label'));
21722                
21723         range.insertNode(this.doc.createTextNode(txt));
21724     } ,
21725     
21726      
21727
21728     /**
21729      * Executes a Midas editor command on the editor document and performs necessary focus and
21730      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21731      * @param {String} cmd The Midas command
21732      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21733      */
21734     relayCmd : function(cmd, value){
21735         this.win.focus();
21736         this.execCmd(cmd, value);
21737         this.owner.fireEvent('editorevent', this);
21738         //this.updateToolbar();
21739         this.owner.deferFocus();
21740     },
21741
21742     /**
21743      * Executes a Midas editor command directly on the editor document.
21744      * For visual commands, you should use {@link #relayCmd} instead.
21745      * <b>This should only be called after the editor is initialized.</b>
21746      * @param {String} cmd The Midas command
21747      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21748      */
21749     execCmd : function(cmd, value){
21750         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21751         this.syncValue();
21752     },
21753  
21754  
21755    
21756     /**
21757      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21758      * to insert tRoo.
21759      * @param {String} text | dom node.. 
21760      */
21761     insertAtCursor : function(text)
21762     {
21763         
21764         if(!this.activated){
21765             return;
21766         }
21767         /*
21768         if(Roo.isIE){
21769             this.win.focus();
21770             var r = this.doc.selection.createRange();
21771             if(r){
21772                 r.collapse(true);
21773                 r.pasteHTML(text);
21774                 this.syncValue();
21775                 this.deferFocus();
21776             
21777             }
21778             return;
21779         }
21780         */
21781         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21782             this.win.focus();
21783             
21784             
21785             // from jquery ui (MIT licenced)
21786             var range, node;
21787             var win = this.win;
21788             
21789             if (win.getSelection && win.getSelection().getRangeAt) {
21790                 range = win.getSelection().getRangeAt(0);
21791                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21792                 range.insertNode(node);
21793             } else if (win.document.selection && win.document.selection.createRange) {
21794                 // no firefox support
21795                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21796                 win.document.selection.createRange().pasteHTML(txt);
21797             } else {
21798                 // no firefox support
21799                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21800                 this.execCmd('InsertHTML', txt);
21801             } 
21802             
21803             this.syncValue();
21804             
21805             this.deferFocus();
21806         }
21807     },
21808  // private
21809     mozKeyPress : function(e){
21810         if(e.ctrlKey){
21811             var c = e.getCharCode(), cmd;
21812           
21813             if(c > 0){
21814                 c = String.fromCharCode(c).toLowerCase();
21815                 switch(c){
21816                     case 'b':
21817                         cmd = 'bold';
21818                         break;
21819                     case 'i':
21820                         cmd = 'italic';
21821                         break;
21822                     
21823                     case 'u':
21824                         cmd = 'underline';
21825                         break;
21826                     
21827                     case 'v':
21828                         this.cleanUpPaste.defer(100, this);
21829                         return;
21830                         
21831                 }
21832                 if(cmd){
21833                     this.win.focus();
21834                     this.execCmd(cmd);
21835                     this.deferFocus();
21836                     e.preventDefault();
21837                 }
21838                 
21839             }
21840         }
21841     },
21842
21843     // private
21844     fixKeys : function(){ // load time branching for fastest keydown performance
21845         if(Roo.isIE){
21846             return function(e){
21847                 var k = e.getKey(), r;
21848                 if(k == e.TAB){
21849                     e.stopEvent();
21850                     r = this.doc.selection.createRange();
21851                     if(r){
21852                         r.collapse(true);
21853                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21854                         this.deferFocus();
21855                     }
21856                     return;
21857                 }
21858                 
21859                 if(k == e.ENTER){
21860                     r = this.doc.selection.createRange();
21861                     if(r){
21862                         var target = r.parentElement();
21863                         if(!target || target.tagName.toLowerCase() != 'li'){
21864                             e.stopEvent();
21865                             r.pasteHTML('<br />');
21866                             r.collapse(false);
21867                             r.select();
21868                         }
21869                     }
21870                 }
21871                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21872                     this.cleanUpPaste.defer(100, this);
21873                     return;
21874                 }
21875                 
21876                 
21877             };
21878         }else if(Roo.isOpera){
21879             return function(e){
21880                 var k = e.getKey();
21881                 if(k == e.TAB){
21882                     e.stopEvent();
21883                     this.win.focus();
21884                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21885                     this.deferFocus();
21886                 }
21887                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21888                     this.cleanUpPaste.defer(100, this);
21889                     return;
21890                 }
21891                 
21892             };
21893         }else if(Roo.isSafari){
21894             return function(e){
21895                 var k = e.getKey();
21896                 
21897                 if(k == e.TAB){
21898                     e.stopEvent();
21899                     this.execCmd('InsertText','\t');
21900                     this.deferFocus();
21901                     return;
21902                 }
21903                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21904                     this.cleanUpPaste.defer(100, this);
21905                     return;
21906                 }
21907                 
21908              };
21909         }
21910     }(),
21911     
21912     getAllAncestors: function()
21913     {
21914         var p = this.getSelectedNode();
21915         var a = [];
21916         if (!p) {
21917             a.push(p); // push blank onto stack..
21918             p = this.getParentElement();
21919         }
21920         
21921         
21922         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21923             a.push(p);
21924             p = p.parentNode;
21925         }
21926         a.push(this.doc.body);
21927         return a;
21928     },
21929     lastSel : false,
21930     lastSelNode : false,
21931     
21932     
21933     getSelection : function() 
21934     {
21935         this.assignDocWin();
21936         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21937     },
21938     
21939     getSelectedNode: function() 
21940     {
21941         // this may only work on Gecko!!!
21942         
21943         // should we cache this!!!!
21944         
21945         
21946         
21947          
21948         var range = this.createRange(this.getSelection()).cloneRange();
21949         
21950         if (Roo.isIE) {
21951             var parent = range.parentElement();
21952             while (true) {
21953                 var testRange = range.duplicate();
21954                 testRange.moveToElementText(parent);
21955                 if (testRange.inRange(range)) {
21956                     break;
21957                 }
21958                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21959                     break;
21960                 }
21961                 parent = parent.parentElement;
21962             }
21963             return parent;
21964         }
21965         
21966         // is ancestor a text element.
21967         var ac =  range.commonAncestorContainer;
21968         if (ac.nodeType == 3) {
21969             ac = ac.parentNode;
21970         }
21971         
21972         var ar = ac.childNodes;
21973          
21974         var nodes = [];
21975         var other_nodes = [];
21976         var has_other_nodes = false;
21977         for (var i=0;i<ar.length;i++) {
21978             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21979                 continue;
21980             }
21981             // fullly contained node.
21982             
21983             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21984                 nodes.push(ar[i]);
21985                 continue;
21986             }
21987             
21988             // probably selected..
21989             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21990                 other_nodes.push(ar[i]);
21991                 continue;
21992             }
21993             // outer..
21994             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21995                 continue;
21996             }
21997             
21998             
21999             has_other_nodes = true;
22000         }
22001         if (!nodes.length && other_nodes.length) {
22002             nodes= other_nodes;
22003         }
22004         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22005             return false;
22006         }
22007         
22008         return nodes[0];
22009     },
22010     createRange: function(sel)
22011     {
22012         // this has strange effects when using with 
22013         // top toolbar - not sure if it's a great idea.
22014         //this.editor.contentWindow.focus();
22015         if (typeof sel != "undefined") {
22016             try {
22017                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22018             } catch(e) {
22019                 return this.doc.createRange();
22020             }
22021         } else {
22022             return this.doc.createRange();
22023         }
22024     },
22025     getParentElement: function()
22026     {
22027         
22028         this.assignDocWin();
22029         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22030         
22031         var range = this.createRange(sel);
22032          
22033         try {
22034             var p = range.commonAncestorContainer;
22035             while (p.nodeType == 3) { // text node
22036                 p = p.parentNode;
22037             }
22038             return p;
22039         } catch (e) {
22040             return null;
22041         }
22042     
22043     },
22044     /***
22045      *
22046      * Range intersection.. the hard stuff...
22047      *  '-1' = before
22048      *  '0' = hits..
22049      *  '1' = after.
22050      *         [ -- selected range --- ]
22051      *   [fail]                        [fail]
22052      *
22053      *    basically..
22054      *      if end is before start or  hits it. fail.
22055      *      if start is after end or hits it fail.
22056      *
22057      *   if either hits (but other is outside. - then it's not 
22058      *   
22059      *    
22060      **/
22061     
22062     
22063     // @see http://www.thismuchiknow.co.uk/?p=64.
22064     rangeIntersectsNode : function(range, node)
22065     {
22066         var nodeRange = node.ownerDocument.createRange();
22067         try {
22068             nodeRange.selectNode(node);
22069         } catch (e) {
22070             nodeRange.selectNodeContents(node);
22071         }
22072     
22073         var rangeStartRange = range.cloneRange();
22074         rangeStartRange.collapse(true);
22075     
22076         var rangeEndRange = range.cloneRange();
22077         rangeEndRange.collapse(false);
22078     
22079         var nodeStartRange = nodeRange.cloneRange();
22080         nodeStartRange.collapse(true);
22081     
22082         var nodeEndRange = nodeRange.cloneRange();
22083         nodeEndRange.collapse(false);
22084     
22085         return rangeStartRange.compareBoundaryPoints(
22086                  Range.START_TO_START, nodeEndRange) == -1 &&
22087                rangeEndRange.compareBoundaryPoints(
22088                  Range.START_TO_START, nodeStartRange) == 1;
22089         
22090          
22091     },
22092     rangeCompareNode : function(range, node)
22093     {
22094         var nodeRange = node.ownerDocument.createRange();
22095         try {
22096             nodeRange.selectNode(node);
22097         } catch (e) {
22098             nodeRange.selectNodeContents(node);
22099         }
22100         
22101         
22102         range.collapse(true);
22103     
22104         nodeRange.collapse(true);
22105      
22106         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22107         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22108          
22109         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22110         
22111         var nodeIsBefore   =  ss == 1;
22112         var nodeIsAfter    = ee == -1;
22113         
22114         if (nodeIsBefore && nodeIsAfter) {
22115             return 0; // outer
22116         }
22117         if (!nodeIsBefore && nodeIsAfter) {
22118             return 1; //right trailed.
22119         }
22120         
22121         if (nodeIsBefore && !nodeIsAfter) {
22122             return 2;  // left trailed.
22123         }
22124         // fully contined.
22125         return 3;
22126     },
22127
22128     // private? - in a new class?
22129     cleanUpPaste :  function()
22130     {
22131         // cleans up the whole document..
22132         Roo.log('cleanuppaste');
22133         
22134         this.cleanUpChildren(this.doc.body);
22135         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22136         if (clean != this.doc.body.innerHTML) {
22137             this.doc.body.innerHTML = clean;
22138         }
22139         
22140     },
22141     
22142     cleanWordChars : function(input) {// change the chars to hex code
22143         var he = Roo.HtmlEditorCore;
22144         
22145         var output = input;
22146         Roo.each(he.swapCodes, function(sw) { 
22147             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22148             
22149             output = output.replace(swapper, sw[1]);
22150         });
22151         
22152         return output;
22153     },
22154     
22155     
22156     cleanUpChildren : function (n)
22157     {
22158         if (!n.childNodes.length) {
22159             return;
22160         }
22161         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22162            this.cleanUpChild(n.childNodes[i]);
22163         }
22164     },
22165     
22166     
22167         
22168     
22169     cleanUpChild : function (node)
22170     {
22171         var ed = this;
22172         //console.log(node);
22173         if (node.nodeName == "#text") {
22174             // clean up silly Windows -- stuff?
22175             return; 
22176         }
22177         if (node.nodeName == "#comment") {
22178             node.parentNode.removeChild(node);
22179             // clean up silly Windows -- stuff?
22180             return; 
22181         }
22182         var lcname = node.tagName.toLowerCase();
22183         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22184         // whitelist of tags..
22185         
22186         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22187             // remove node.
22188             node.parentNode.removeChild(node);
22189             return;
22190             
22191         }
22192         
22193         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22194         
22195         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22196         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22197         
22198         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22199         //    remove_keep_children = true;
22200         //}
22201         
22202         if (remove_keep_children) {
22203             this.cleanUpChildren(node);
22204             // inserts everything just before this node...
22205             while (node.childNodes.length) {
22206                 var cn = node.childNodes[0];
22207                 node.removeChild(cn);
22208                 node.parentNode.insertBefore(cn, node);
22209             }
22210             node.parentNode.removeChild(node);
22211             return;
22212         }
22213         
22214         if (!node.attributes || !node.attributes.length) {
22215             this.cleanUpChildren(node);
22216             return;
22217         }
22218         
22219         function cleanAttr(n,v)
22220         {
22221             
22222             if (v.match(/^\./) || v.match(/^\//)) {
22223                 return;
22224             }
22225             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22226                 return;
22227             }
22228             if (v.match(/^#/)) {
22229                 return;
22230             }
22231 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22232             node.removeAttribute(n);
22233             
22234         }
22235         
22236         var cwhite = this.cwhite;
22237         var cblack = this.cblack;
22238             
22239         function cleanStyle(n,v)
22240         {
22241             if (v.match(/expression/)) { //XSS?? should we even bother..
22242                 node.removeAttribute(n);
22243                 return;
22244             }
22245             
22246             var parts = v.split(/;/);
22247             var clean = [];
22248             
22249             Roo.each(parts, function(p) {
22250                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22251                 if (!p.length) {
22252                     return true;
22253                 }
22254                 var l = p.split(':').shift().replace(/\s+/g,'');
22255                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22256                 
22257                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22258 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22259                     //node.removeAttribute(n);
22260                     return true;
22261                 }
22262                 //Roo.log()
22263                 // only allow 'c whitelisted system attributes'
22264                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22265 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22266                     //node.removeAttribute(n);
22267                     return true;
22268                 }
22269                 
22270                 
22271                  
22272                 
22273                 clean.push(p);
22274                 return true;
22275             });
22276             if (clean.length) { 
22277                 node.setAttribute(n, clean.join(';'));
22278             } else {
22279                 node.removeAttribute(n);
22280             }
22281             
22282         }
22283         
22284         
22285         for (var i = node.attributes.length-1; i > -1 ; i--) {
22286             var a = node.attributes[i];
22287             //console.log(a);
22288             
22289             if (a.name.toLowerCase().substr(0,2)=='on')  {
22290                 node.removeAttribute(a.name);
22291                 continue;
22292             }
22293             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22294                 node.removeAttribute(a.name);
22295                 continue;
22296             }
22297             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22298                 cleanAttr(a.name,a.value); // fixme..
22299                 continue;
22300             }
22301             if (a.name == 'style') {
22302                 cleanStyle(a.name,a.value);
22303                 continue;
22304             }
22305             /// clean up MS crap..
22306             // tecnically this should be a list of valid class'es..
22307             
22308             
22309             if (a.name == 'class') {
22310                 if (a.value.match(/^Mso/)) {
22311                     node.className = '';
22312                 }
22313                 
22314                 if (a.value.match(/^body$/)) {
22315                     node.className = '';
22316                 }
22317                 continue;
22318             }
22319             
22320             // style cleanup!?
22321             // class cleanup?
22322             
22323         }
22324         
22325         
22326         this.cleanUpChildren(node);
22327         
22328         
22329     },
22330     
22331     /**
22332      * Clean up MS wordisms...
22333      */
22334     cleanWord : function(node)
22335     {
22336         
22337         
22338         if (!node) {
22339             this.cleanWord(this.doc.body);
22340             return;
22341         }
22342         if (node.nodeName == "#text") {
22343             // clean up silly Windows -- stuff?
22344             return; 
22345         }
22346         if (node.nodeName == "#comment") {
22347             node.parentNode.removeChild(node);
22348             // clean up silly Windows -- stuff?
22349             return; 
22350         }
22351         
22352         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22353             node.parentNode.removeChild(node);
22354             return;
22355         }
22356         
22357         // remove - but keep children..
22358         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22359             while (node.childNodes.length) {
22360                 var cn = node.childNodes[0];
22361                 node.removeChild(cn);
22362                 node.parentNode.insertBefore(cn, node);
22363             }
22364             node.parentNode.removeChild(node);
22365             this.iterateChildren(node, this.cleanWord);
22366             return;
22367         }
22368         // clean styles
22369         if (node.className.length) {
22370             
22371             var cn = node.className.split(/\W+/);
22372             var cna = [];
22373             Roo.each(cn, function(cls) {
22374                 if (cls.match(/Mso[a-zA-Z]+/)) {
22375                     return;
22376                 }
22377                 cna.push(cls);
22378             });
22379             node.className = cna.length ? cna.join(' ') : '';
22380             if (!cna.length) {
22381                 node.removeAttribute("class");
22382             }
22383         }
22384         
22385         if (node.hasAttribute("lang")) {
22386             node.removeAttribute("lang");
22387         }
22388         
22389         if (node.hasAttribute("style")) {
22390             
22391             var styles = node.getAttribute("style").split(";");
22392             var nstyle = [];
22393             Roo.each(styles, function(s) {
22394                 if (!s.match(/:/)) {
22395                     return;
22396                 }
22397                 var kv = s.split(":");
22398                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22399                     return;
22400                 }
22401                 // what ever is left... we allow.
22402                 nstyle.push(s);
22403             });
22404             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22405             if (!nstyle.length) {
22406                 node.removeAttribute('style');
22407             }
22408         }
22409         this.iterateChildren(node, this.cleanWord);
22410         
22411         
22412         
22413     },
22414     /**
22415      * iterateChildren of a Node, calling fn each time, using this as the scole..
22416      * @param {DomNode} node node to iterate children of.
22417      * @param {Function} fn method of this class to call on each item.
22418      */
22419     iterateChildren : function(node, fn)
22420     {
22421         if (!node.childNodes.length) {
22422                 return;
22423         }
22424         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22425            fn.call(this, node.childNodes[i])
22426         }
22427     },
22428     
22429     
22430     /**
22431      * cleanTableWidths.
22432      *
22433      * Quite often pasting from word etc.. results in tables with column and widths.
22434      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22435      *
22436      */
22437     cleanTableWidths : function(node)
22438     {
22439          
22440          
22441         if (!node) {
22442             this.cleanTableWidths(this.doc.body);
22443             return;
22444         }
22445         
22446         // ignore list...
22447         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22448             return; 
22449         }
22450         Roo.log(node.tagName);
22451         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22452             this.iterateChildren(node, this.cleanTableWidths);
22453             return;
22454         }
22455         if (node.hasAttribute('width')) {
22456             node.removeAttribute('width');
22457         }
22458         
22459          
22460         if (node.hasAttribute("style")) {
22461             // pretty basic...
22462             
22463             var styles = node.getAttribute("style").split(";");
22464             var nstyle = [];
22465             Roo.each(styles, function(s) {
22466                 if (!s.match(/:/)) {
22467                     return;
22468                 }
22469                 var kv = s.split(":");
22470                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22471                     return;
22472                 }
22473                 // what ever is left... we allow.
22474                 nstyle.push(s);
22475             });
22476             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22477             if (!nstyle.length) {
22478                 node.removeAttribute('style');
22479             }
22480         }
22481         
22482         this.iterateChildren(node, this.cleanTableWidths);
22483         
22484         
22485     },
22486     
22487     
22488     
22489     
22490     domToHTML : function(currentElement, depth, nopadtext) {
22491         
22492         depth = depth || 0;
22493         nopadtext = nopadtext || false;
22494     
22495         if (!currentElement) {
22496             return this.domToHTML(this.doc.body);
22497         }
22498         
22499         //Roo.log(currentElement);
22500         var j;
22501         var allText = false;
22502         var nodeName = currentElement.nodeName;
22503         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22504         
22505         if  (nodeName == '#text') {
22506             
22507             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22508         }
22509         
22510         
22511         var ret = '';
22512         if (nodeName != 'BODY') {
22513              
22514             var i = 0;
22515             // Prints the node tagName, such as <A>, <IMG>, etc
22516             if (tagName) {
22517                 var attr = [];
22518                 for(i = 0; i < currentElement.attributes.length;i++) {
22519                     // quoting?
22520                     var aname = currentElement.attributes.item(i).name;
22521                     if (!currentElement.attributes.item(i).value.length) {
22522                         continue;
22523                     }
22524                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22525                 }
22526                 
22527                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22528             } 
22529             else {
22530                 
22531                 // eack
22532             }
22533         } else {
22534             tagName = false;
22535         }
22536         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22537             return ret;
22538         }
22539         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22540             nopadtext = true;
22541         }
22542         
22543         
22544         // Traverse the tree
22545         i = 0;
22546         var currentElementChild = currentElement.childNodes.item(i);
22547         var allText = true;
22548         var innerHTML  = '';
22549         lastnode = '';
22550         while (currentElementChild) {
22551             // Formatting code (indent the tree so it looks nice on the screen)
22552             var nopad = nopadtext;
22553             if (lastnode == 'SPAN') {
22554                 nopad  = true;
22555             }
22556             // text
22557             if  (currentElementChild.nodeName == '#text') {
22558                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22559                 toadd = nopadtext ? toadd : toadd.trim();
22560                 if (!nopad && toadd.length > 80) {
22561                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22562                 }
22563                 innerHTML  += toadd;
22564                 
22565                 i++;
22566                 currentElementChild = currentElement.childNodes.item(i);
22567                 lastNode = '';
22568                 continue;
22569             }
22570             allText = false;
22571             
22572             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22573                 
22574             // Recursively traverse the tree structure of the child node
22575             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22576             lastnode = currentElementChild.nodeName;
22577             i++;
22578             currentElementChild=currentElement.childNodes.item(i);
22579         }
22580         
22581         ret += innerHTML;
22582         
22583         if (!allText) {
22584                 // The remaining code is mostly for formatting the tree
22585             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22586         }
22587         
22588         
22589         if (tagName) {
22590             ret+= "</"+tagName+">";
22591         }
22592         return ret;
22593         
22594     },
22595         
22596     applyBlacklists : function()
22597     {
22598         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22599         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22600         
22601         this.white = [];
22602         this.black = [];
22603         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22604             if (b.indexOf(tag) > -1) {
22605                 return;
22606             }
22607             this.white.push(tag);
22608             
22609         }, this);
22610         
22611         Roo.each(w, function(tag) {
22612             if (b.indexOf(tag) > -1) {
22613                 return;
22614             }
22615             if (this.white.indexOf(tag) > -1) {
22616                 return;
22617             }
22618             this.white.push(tag);
22619             
22620         }, this);
22621         
22622         
22623         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22624             if (w.indexOf(tag) > -1) {
22625                 return;
22626             }
22627             this.black.push(tag);
22628             
22629         }, this);
22630         
22631         Roo.each(b, function(tag) {
22632             if (w.indexOf(tag) > -1) {
22633                 return;
22634             }
22635             if (this.black.indexOf(tag) > -1) {
22636                 return;
22637             }
22638             this.black.push(tag);
22639             
22640         }, this);
22641         
22642         
22643         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22644         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22645         
22646         this.cwhite = [];
22647         this.cblack = [];
22648         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22649             if (b.indexOf(tag) > -1) {
22650                 return;
22651             }
22652             this.cwhite.push(tag);
22653             
22654         }, this);
22655         
22656         Roo.each(w, function(tag) {
22657             if (b.indexOf(tag) > -1) {
22658                 return;
22659             }
22660             if (this.cwhite.indexOf(tag) > -1) {
22661                 return;
22662             }
22663             this.cwhite.push(tag);
22664             
22665         }, this);
22666         
22667         
22668         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22669             if (w.indexOf(tag) > -1) {
22670                 return;
22671             }
22672             this.cblack.push(tag);
22673             
22674         }, this);
22675         
22676         Roo.each(b, function(tag) {
22677             if (w.indexOf(tag) > -1) {
22678                 return;
22679             }
22680             if (this.cblack.indexOf(tag) > -1) {
22681                 return;
22682             }
22683             this.cblack.push(tag);
22684             
22685         }, this);
22686     },
22687     
22688     setStylesheets : function(stylesheets)
22689     {
22690         if(typeof(stylesheets) == 'string'){
22691             Roo.get(this.iframe.contentDocument.head).createChild({
22692                 tag : 'link',
22693                 rel : 'stylesheet',
22694                 type : 'text/css',
22695                 href : stylesheets
22696             });
22697             
22698             return;
22699         }
22700         var _this = this;
22701      
22702         Roo.each(stylesheets, function(s) {
22703             if(!s.length){
22704                 return;
22705             }
22706             
22707             Roo.get(_this.iframe.contentDocument.head).createChild({
22708                 tag : 'link',
22709                 rel : 'stylesheet',
22710                 type : 'text/css',
22711                 href : s
22712             });
22713         });
22714
22715         
22716     },
22717     
22718     removeStylesheets : function()
22719     {
22720         var _this = this;
22721         
22722         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22723             s.remove();
22724         });
22725     },
22726     
22727     setStyle : function(style)
22728     {
22729         Roo.get(this.iframe.contentDocument.head).createChild({
22730             tag : 'style',
22731             type : 'text/css',
22732             html : style
22733         });
22734
22735         return;
22736     }
22737     
22738     // hide stuff that is not compatible
22739     /**
22740      * @event blur
22741      * @hide
22742      */
22743     /**
22744      * @event change
22745      * @hide
22746      */
22747     /**
22748      * @event focus
22749      * @hide
22750      */
22751     /**
22752      * @event specialkey
22753      * @hide
22754      */
22755     /**
22756      * @cfg {String} fieldClass @hide
22757      */
22758     /**
22759      * @cfg {String} focusClass @hide
22760      */
22761     /**
22762      * @cfg {String} autoCreate @hide
22763      */
22764     /**
22765      * @cfg {String} inputType @hide
22766      */
22767     /**
22768      * @cfg {String} invalidClass @hide
22769      */
22770     /**
22771      * @cfg {String} invalidText @hide
22772      */
22773     /**
22774      * @cfg {String} msgFx @hide
22775      */
22776     /**
22777      * @cfg {String} validateOnBlur @hide
22778      */
22779 });
22780
22781 Roo.HtmlEditorCore.white = [
22782         'area', 'br', 'img', 'input', 'hr', 'wbr',
22783         
22784        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22785        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22786        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22787        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22788        'table',   'ul',         'xmp', 
22789        
22790        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22791       'thead',   'tr', 
22792      
22793       'dir', 'menu', 'ol', 'ul', 'dl',
22794        
22795       'embed',  'object'
22796 ];
22797
22798
22799 Roo.HtmlEditorCore.black = [
22800     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22801         'applet', // 
22802         'base',   'basefont', 'bgsound', 'blink',  'body', 
22803         'frame',  'frameset', 'head',    'html',   'ilayer', 
22804         'iframe', 'layer',  'link',     'meta',    'object',   
22805         'script', 'style' ,'title',  'xml' // clean later..
22806 ];
22807 Roo.HtmlEditorCore.clean = [
22808     'script', 'style', 'title', 'xml'
22809 ];
22810 Roo.HtmlEditorCore.remove = [
22811     'font'
22812 ];
22813 // attributes..
22814
22815 Roo.HtmlEditorCore.ablack = [
22816     'on'
22817 ];
22818     
22819 Roo.HtmlEditorCore.aclean = [ 
22820     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22821 ];
22822
22823 // protocols..
22824 Roo.HtmlEditorCore.pwhite= [
22825         'http',  'https',  'mailto'
22826 ];
22827
22828 // white listed style attributes.
22829 Roo.HtmlEditorCore.cwhite= [
22830       //  'text-align', /// default is to allow most things..
22831       
22832          
22833 //        'font-size'//??
22834 ];
22835
22836 // black listed style attributes.
22837 Roo.HtmlEditorCore.cblack= [
22838       //  'font-size' -- this can be set by the project 
22839 ];
22840
22841
22842 Roo.HtmlEditorCore.swapCodes   =[ 
22843     [    8211, "--" ], 
22844     [    8212, "--" ], 
22845     [    8216,  "'" ],  
22846     [    8217, "'" ],  
22847     [    8220, '"' ],  
22848     [    8221, '"' ],  
22849     [    8226, "*" ],  
22850     [    8230, "..." ]
22851 ]; 
22852
22853     /*
22854  * - LGPL
22855  *
22856  * HtmlEditor
22857  * 
22858  */
22859
22860 /**
22861  * @class Roo.bootstrap.HtmlEditor
22862  * @extends Roo.bootstrap.TextArea
22863  * Bootstrap HtmlEditor class
22864
22865  * @constructor
22866  * Create a new HtmlEditor
22867  * @param {Object} config The config object
22868  */
22869
22870 Roo.bootstrap.HtmlEditor = function(config){
22871     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22872     if (!this.toolbars) {
22873         this.toolbars = [];
22874     }
22875     
22876     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22877     this.addEvents({
22878             /**
22879              * @event initialize
22880              * Fires when the editor is fully initialized (including the iframe)
22881              * @param {HtmlEditor} this
22882              */
22883             initialize: true,
22884             /**
22885              * @event activate
22886              * Fires when the editor is first receives the focus. Any insertion must wait
22887              * until after this event.
22888              * @param {HtmlEditor} this
22889              */
22890             activate: true,
22891              /**
22892              * @event beforesync
22893              * Fires before the textarea is updated with content from the editor iframe. Return false
22894              * to cancel the sync.
22895              * @param {HtmlEditor} this
22896              * @param {String} html
22897              */
22898             beforesync: true,
22899              /**
22900              * @event beforepush
22901              * Fires before the iframe editor is updated with content from the textarea. Return false
22902              * to cancel the push.
22903              * @param {HtmlEditor} this
22904              * @param {String} html
22905              */
22906             beforepush: true,
22907              /**
22908              * @event sync
22909              * Fires when the textarea is updated with content from the editor iframe.
22910              * @param {HtmlEditor} this
22911              * @param {String} html
22912              */
22913             sync: true,
22914              /**
22915              * @event push
22916              * Fires when the iframe editor is updated with content from the textarea.
22917              * @param {HtmlEditor} this
22918              * @param {String} html
22919              */
22920             push: true,
22921              /**
22922              * @event editmodechange
22923              * Fires when the editor switches edit modes
22924              * @param {HtmlEditor} this
22925              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22926              */
22927             editmodechange: true,
22928             /**
22929              * @event editorevent
22930              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22931              * @param {HtmlEditor} this
22932              */
22933             editorevent: true,
22934             /**
22935              * @event firstfocus
22936              * Fires when on first focus - needed by toolbars..
22937              * @param {HtmlEditor} this
22938              */
22939             firstfocus: true,
22940             /**
22941              * @event autosave
22942              * Auto save the htmlEditor value as a file into Events
22943              * @param {HtmlEditor} this
22944              */
22945             autosave: true,
22946             /**
22947              * @event savedpreview
22948              * preview the saved version of htmlEditor
22949              * @param {HtmlEditor} this
22950              */
22951             savedpreview: true
22952         });
22953 };
22954
22955
22956 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22957     
22958     
22959       /**
22960      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22961      */
22962     toolbars : false,
22963     
22964      /**
22965     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22966     */
22967     btns : [],
22968    
22969      /**
22970      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22971      *                        Roo.resizable.
22972      */
22973     resizable : false,
22974      /**
22975      * @cfg {Number} height (in pixels)
22976      */   
22977     height: 300,
22978    /**
22979      * @cfg {Number} width (in pixels)
22980      */   
22981     width: false,
22982     
22983     /**
22984      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22985      * 
22986      */
22987     stylesheets: false,
22988     
22989     // id of frame..
22990     frameId: false,
22991     
22992     // private properties
22993     validationEvent : false,
22994     deferHeight: true,
22995     initialized : false,
22996     activated : false,
22997     
22998     onFocus : Roo.emptyFn,
22999     iframePad:3,
23000     hideMode:'offsets',
23001     
23002     tbContainer : false,
23003     
23004     bodyCls : '',
23005     
23006     toolbarContainer :function() {
23007         return this.wrap.select('.x-html-editor-tb',true).first();
23008     },
23009
23010     /**
23011      * Protected method that will not generally be called directly. It
23012      * is called when the editor creates its toolbar. Override this method if you need to
23013      * add custom toolbar buttons.
23014      * @param {HtmlEditor} editor
23015      */
23016     createToolbar : function(){
23017         Roo.log('renewing');
23018         Roo.log("create toolbars");
23019         
23020         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23021         this.toolbars[0].render(this.toolbarContainer());
23022         
23023         return;
23024         
23025 //        if (!editor.toolbars || !editor.toolbars.length) {
23026 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23027 //        }
23028 //        
23029 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23030 //            editor.toolbars[i] = Roo.factory(
23031 //                    typeof(editor.toolbars[i]) == 'string' ?
23032 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23033 //                Roo.bootstrap.HtmlEditor);
23034 //            editor.toolbars[i].init(editor);
23035 //        }
23036     },
23037
23038      
23039     // private
23040     onRender : function(ct, position)
23041     {
23042        // Roo.log("Call onRender: " + this.xtype);
23043         var _t = this;
23044         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23045       
23046         this.wrap = this.inputEl().wrap({
23047             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23048         });
23049         
23050         this.editorcore.onRender(ct, position);
23051          
23052         if (this.resizable) {
23053             this.resizeEl = new Roo.Resizable(this.wrap, {
23054                 pinned : true,
23055                 wrap: true,
23056                 dynamic : true,
23057                 minHeight : this.height,
23058                 height: this.height,
23059                 handles : this.resizable,
23060                 width: this.width,
23061                 listeners : {
23062                     resize : function(r, w, h) {
23063                         _t.onResize(w,h); // -something
23064                     }
23065                 }
23066             });
23067             
23068         }
23069         this.createToolbar(this);
23070        
23071         
23072         if(!this.width && this.resizable){
23073             this.setSize(this.wrap.getSize());
23074         }
23075         if (this.resizeEl) {
23076             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23077             // should trigger onReize..
23078         }
23079         
23080     },
23081
23082     // private
23083     onResize : function(w, h)
23084     {
23085         Roo.log('resize: ' +w + ',' + h );
23086         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23087         var ew = false;
23088         var eh = false;
23089         
23090         if(this.inputEl() ){
23091             if(typeof w == 'number'){
23092                 var aw = w - this.wrap.getFrameWidth('lr');
23093                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23094                 ew = aw;
23095             }
23096             if(typeof h == 'number'){
23097                  var tbh = -11;  // fixme it needs to tool bar size!
23098                 for (var i =0; i < this.toolbars.length;i++) {
23099                     // fixme - ask toolbars for heights?
23100                     tbh += this.toolbars[i].el.getHeight();
23101                     //if (this.toolbars[i].footer) {
23102                     //    tbh += this.toolbars[i].footer.el.getHeight();
23103                     //}
23104                 }
23105               
23106                 
23107                 
23108                 
23109                 
23110                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23111                 ah -= 5; // knock a few pixes off for look..
23112                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23113                 var eh = ah;
23114             }
23115         }
23116         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23117         this.editorcore.onResize(ew,eh);
23118         
23119     },
23120
23121     /**
23122      * Toggles the editor between standard and source edit mode.
23123      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23124      */
23125     toggleSourceEdit : function(sourceEditMode)
23126     {
23127         this.editorcore.toggleSourceEdit(sourceEditMode);
23128         
23129         if(this.editorcore.sourceEditMode){
23130             Roo.log('editor - showing textarea');
23131             
23132 //            Roo.log('in');
23133 //            Roo.log(this.syncValue());
23134             this.syncValue();
23135             this.inputEl().removeClass(['hide', 'x-hidden']);
23136             this.inputEl().dom.removeAttribute('tabIndex');
23137             this.inputEl().focus();
23138         }else{
23139             Roo.log('editor - hiding textarea');
23140 //            Roo.log('out')
23141 //            Roo.log(this.pushValue()); 
23142             this.pushValue();
23143             
23144             this.inputEl().addClass(['hide', 'x-hidden']);
23145             this.inputEl().dom.setAttribute('tabIndex', -1);
23146             //this.deferFocus();
23147         }
23148          
23149         if(this.resizable){
23150             this.setSize(this.wrap.getSize());
23151         }
23152         
23153         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23154     },
23155  
23156     // private (for BoxComponent)
23157     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23158
23159     // private (for BoxComponent)
23160     getResizeEl : function(){
23161         return this.wrap;
23162     },
23163
23164     // private (for BoxComponent)
23165     getPositionEl : function(){
23166         return this.wrap;
23167     },
23168
23169     // private
23170     initEvents : function(){
23171         this.originalValue = this.getValue();
23172     },
23173
23174 //    /**
23175 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23176 //     * @method
23177 //     */
23178 //    markInvalid : Roo.emptyFn,
23179 //    /**
23180 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23181 //     * @method
23182 //     */
23183 //    clearInvalid : Roo.emptyFn,
23184
23185     setValue : function(v){
23186         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23187         this.editorcore.pushValue();
23188     },
23189
23190      
23191     // private
23192     deferFocus : function(){
23193         this.focus.defer(10, this);
23194     },
23195
23196     // doc'ed in Field
23197     focus : function(){
23198         this.editorcore.focus();
23199         
23200     },
23201       
23202
23203     // private
23204     onDestroy : function(){
23205         
23206         
23207         
23208         if(this.rendered){
23209             
23210             for (var i =0; i < this.toolbars.length;i++) {
23211                 // fixme - ask toolbars for heights?
23212                 this.toolbars[i].onDestroy();
23213             }
23214             
23215             this.wrap.dom.innerHTML = '';
23216             this.wrap.remove();
23217         }
23218     },
23219
23220     // private
23221     onFirstFocus : function(){
23222         //Roo.log("onFirstFocus");
23223         this.editorcore.onFirstFocus();
23224          for (var i =0; i < this.toolbars.length;i++) {
23225             this.toolbars[i].onFirstFocus();
23226         }
23227         
23228     },
23229     
23230     // private
23231     syncValue : function()
23232     {   
23233         this.editorcore.syncValue();
23234     },
23235     
23236     pushValue : function()
23237     {   
23238         this.editorcore.pushValue();
23239     }
23240      
23241     
23242     // hide stuff that is not compatible
23243     /**
23244      * @event blur
23245      * @hide
23246      */
23247     /**
23248      * @event change
23249      * @hide
23250      */
23251     /**
23252      * @event focus
23253      * @hide
23254      */
23255     /**
23256      * @event specialkey
23257      * @hide
23258      */
23259     /**
23260      * @cfg {String} fieldClass @hide
23261      */
23262     /**
23263      * @cfg {String} focusClass @hide
23264      */
23265     /**
23266      * @cfg {String} autoCreate @hide
23267      */
23268     /**
23269      * @cfg {String} inputType @hide
23270      */
23271     /**
23272      * @cfg {String} invalidClass @hide
23273      */
23274     /**
23275      * @cfg {String} invalidText @hide
23276      */
23277     /**
23278      * @cfg {String} msgFx @hide
23279      */
23280     /**
23281      * @cfg {String} validateOnBlur @hide
23282      */
23283 });
23284  
23285     
23286    
23287    
23288    
23289       
23290 Roo.namespace('Roo.bootstrap.htmleditor');
23291 /**
23292  * @class Roo.bootstrap.HtmlEditorToolbar1
23293  * Basic Toolbar
23294  * 
23295  * Usage:
23296  *
23297  new Roo.bootstrap.HtmlEditor({
23298     ....
23299     toolbars : [
23300         new Roo.bootstrap.HtmlEditorToolbar1({
23301             disable : { fonts: 1 , format: 1, ..., ... , ...],
23302             btns : [ .... ]
23303         })
23304     }
23305      
23306  * 
23307  * @cfg {Object} disable List of elements to disable..
23308  * @cfg {Array} btns List of additional buttons.
23309  * 
23310  * 
23311  * NEEDS Extra CSS? 
23312  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23313  */
23314  
23315 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23316 {
23317     
23318     Roo.apply(this, config);
23319     
23320     // default disabled, based on 'good practice'..
23321     this.disable = this.disable || {};
23322     Roo.applyIf(this.disable, {
23323         fontSize : true,
23324         colors : true,
23325         specialElements : true
23326     });
23327     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23328     
23329     this.editor = config.editor;
23330     this.editorcore = config.editor.editorcore;
23331     
23332     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23333     
23334     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23335     // dont call parent... till later.
23336 }
23337 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23338      
23339     bar : true,
23340     
23341     editor : false,
23342     editorcore : false,
23343     
23344     
23345     formats : [
23346         "p" ,  
23347         "h1","h2","h3","h4","h5","h6", 
23348         "pre", "code", 
23349         "abbr", "acronym", "address", "cite", "samp", "var",
23350         'div','span'
23351     ],
23352     
23353     onRender : function(ct, position)
23354     {
23355        // Roo.log("Call onRender: " + this.xtype);
23356         
23357        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23358        Roo.log(this.el);
23359        this.el.dom.style.marginBottom = '0';
23360        var _this = this;
23361        var editorcore = this.editorcore;
23362        var editor= this.editor;
23363        
23364        var children = [];
23365        var btn = function(id,cmd , toggle, handler, html){
23366        
23367             var  event = toggle ? 'toggle' : 'click';
23368        
23369             var a = {
23370                 size : 'sm',
23371                 xtype: 'Button',
23372                 xns: Roo.bootstrap,
23373                 glyphicon : id,
23374                 cmd : id || cmd,
23375                 enableToggle:toggle !== false,
23376                 html : html || '',
23377                 pressed : toggle ? false : null,
23378                 listeners : {}
23379             };
23380             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23381                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23382             };
23383             children.push(a);
23384             return a;
23385        }
23386        
23387     //    var cb_box = function...
23388         
23389         var style = {
23390                 xtype: 'Button',
23391                 size : 'sm',
23392                 xns: Roo.bootstrap,
23393                 glyphicon : 'font',
23394                 //html : 'submit'
23395                 menu : {
23396                     xtype: 'Menu',
23397                     xns: Roo.bootstrap,
23398                     items:  []
23399                 }
23400         };
23401         Roo.each(this.formats, function(f) {
23402             style.menu.items.push({
23403                 xtype :'MenuItem',
23404                 xns: Roo.bootstrap,
23405                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23406                 tagname : f,
23407                 listeners : {
23408                     click : function()
23409                     {
23410                         editorcore.insertTag(this.tagname);
23411                         editor.focus();
23412                     }
23413                 }
23414                 
23415             });
23416         });
23417         children.push(style);   
23418         
23419         btn('bold',false,true);
23420         btn('italic',false,true);
23421         btn('align-left', 'justifyleft',true);
23422         btn('align-center', 'justifycenter',true);
23423         btn('align-right' , 'justifyright',true);
23424         btn('link', false, false, function(btn) {
23425             //Roo.log("create link?");
23426             var url = prompt(this.createLinkText, this.defaultLinkValue);
23427             if(url && url != 'http:/'+'/'){
23428                 this.editorcore.relayCmd('createlink', url);
23429             }
23430         }),
23431         btn('list','insertunorderedlist',true);
23432         btn('pencil', false,true, function(btn){
23433                 Roo.log(this);
23434                 this.toggleSourceEdit(btn.pressed);
23435         });
23436         
23437         if (this.editor.btns.length > 0) {
23438             for (var i = 0; i<this.editor.btns.length; i++) {
23439                 children.push(this.editor.btns[i]);
23440             }
23441         }
23442         
23443         /*
23444         var cog = {
23445                 xtype: 'Button',
23446                 size : 'sm',
23447                 xns: Roo.bootstrap,
23448                 glyphicon : 'cog',
23449                 //html : 'submit'
23450                 menu : {
23451                     xtype: 'Menu',
23452                     xns: Roo.bootstrap,
23453                     items:  []
23454                 }
23455         };
23456         
23457         cog.menu.items.push({
23458             xtype :'MenuItem',
23459             xns: Roo.bootstrap,
23460             html : Clean styles,
23461             tagname : f,
23462             listeners : {
23463                 click : function()
23464                 {
23465                     editorcore.insertTag(this.tagname);
23466                     editor.focus();
23467                 }
23468             }
23469             
23470         });
23471        */
23472         
23473          
23474        this.xtype = 'NavSimplebar';
23475         
23476         for(var i=0;i< children.length;i++) {
23477             
23478             this.buttons.add(this.addxtypeChild(children[i]));
23479             
23480         }
23481         
23482         editor.on('editorevent', this.updateToolbar, this);
23483     },
23484     onBtnClick : function(id)
23485     {
23486        this.editorcore.relayCmd(id);
23487        this.editorcore.focus();
23488     },
23489     
23490     /**
23491      * Protected method that will not generally be called directly. It triggers
23492      * a toolbar update by reading the markup state of the current selection in the editor.
23493      */
23494     updateToolbar: function(){
23495
23496         if(!this.editorcore.activated){
23497             this.editor.onFirstFocus(); // is this neeed?
23498             return;
23499         }
23500
23501         var btns = this.buttons; 
23502         var doc = this.editorcore.doc;
23503         btns.get('bold').setActive(doc.queryCommandState('bold'));
23504         btns.get('italic').setActive(doc.queryCommandState('italic'));
23505         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23506         
23507         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23508         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23509         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23510         
23511         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23512         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23513          /*
23514         
23515         var ans = this.editorcore.getAllAncestors();
23516         if (this.formatCombo) {
23517             
23518             
23519             var store = this.formatCombo.store;
23520             this.formatCombo.setValue("");
23521             for (var i =0; i < ans.length;i++) {
23522                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23523                     // select it..
23524                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23525                     break;
23526                 }
23527             }
23528         }
23529         
23530         
23531         
23532         // hides menus... - so this cant be on a menu...
23533         Roo.bootstrap.MenuMgr.hideAll();
23534         */
23535         Roo.bootstrap.MenuMgr.hideAll();
23536         //this.editorsyncValue();
23537     },
23538     onFirstFocus: function() {
23539         this.buttons.each(function(item){
23540            item.enable();
23541         });
23542     },
23543     toggleSourceEdit : function(sourceEditMode){
23544         
23545           
23546         if(sourceEditMode){
23547             Roo.log("disabling buttons");
23548            this.buttons.each( function(item){
23549                 if(item.cmd != 'pencil'){
23550                     item.disable();
23551                 }
23552             });
23553           
23554         }else{
23555             Roo.log("enabling buttons");
23556             if(this.editorcore.initialized){
23557                 this.buttons.each( function(item){
23558                     item.enable();
23559                 });
23560             }
23561             
23562         }
23563         Roo.log("calling toggole on editor");
23564         // tell the editor that it's been pressed..
23565         this.editor.toggleSourceEdit(sourceEditMode);
23566        
23567     }
23568 });
23569
23570
23571
23572
23573
23574 /**
23575  * @class Roo.bootstrap.Table.AbstractSelectionModel
23576  * @extends Roo.util.Observable
23577  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23578  * implemented by descendant classes.  This class should not be directly instantiated.
23579  * @constructor
23580  */
23581 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23582     this.locked = false;
23583     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23584 };
23585
23586
23587 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23588     /** @ignore Called by the grid automatically. Do not call directly. */
23589     init : function(grid){
23590         this.grid = grid;
23591         this.initEvents();
23592     },
23593
23594     /**
23595      * Locks the selections.
23596      */
23597     lock : function(){
23598         this.locked = true;
23599     },
23600
23601     /**
23602      * Unlocks the selections.
23603      */
23604     unlock : function(){
23605         this.locked = false;
23606     },
23607
23608     /**
23609      * Returns true if the selections are locked.
23610      * @return {Boolean}
23611      */
23612     isLocked : function(){
23613         return this.locked;
23614     }
23615 });
23616 /**
23617  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23618  * @class Roo.bootstrap.Table.RowSelectionModel
23619  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23620  * It supports multiple selections and keyboard selection/navigation. 
23621  * @constructor
23622  * @param {Object} config
23623  */
23624
23625 Roo.bootstrap.Table.RowSelectionModel = function(config){
23626     Roo.apply(this, config);
23627     this.selections = new Roo.util.MixedCollection(false, function(o){
23628         return o.id;
23629     });
23630
23631     this.last = false;
23632     this.lastActive = false;
23633
23634     this.addEvents({
23635         /**
23636              * @event selectionchange
23637              * Fires when the selection changes
23638              * @param {SelectionModel} this
23639              */
23640             "selectionchange" : true,
23641         /**
23642              * @event afterselectionchange
23643              * Fires after the selection changes (eg. by key press or clicking)
23644              * @param {SelectionModel} this
23645              */
23646             "afterselectionchange" : true,
23647         /**
23648              * @event beforerowselect
23649              * Fires when a row is selected being selected, return false to cancel.
23650              * @param {SelectionModel} this
23651              * @param {Number} rowIndex The selected index
23652              * @param {Boolean} keepExisting False if other selections will be cleared
23653              */
23654             "beforerowselect" : true,
23655         /**
23656              * @event rowselect
23657              * Fires when a row is selected.
23658              * @param {SelectionModel} this
23659              * @param {Number} rowIndex The selected index
23660              * @param {Roo.data.Record} r The record
23661              */
23662             "rowselect" : true,
23663         /**
23664              * @event rowdeselect
23665              * Fires when a row is deselected.
23666              * @param {SelectionModel} this
23667              * @param {Number} rowIndex The selected index
23668              */
23669         "rowdeselect" : true
23670     });
23671     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23672     this.locked = false;
23673  };
23674
23675 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23676     /**
23677      * @cfg {Boolean} singleSelect
23678      * True to allow selection of only one row at a time (defaults to false)
23679      */
23680     singleSelect : false,
23681
23682     // private
23683     initEvents : function()
23684     {
23685
23686         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23687         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23688         //}else{ // allow click to work like normal
23689          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23690         //}
23691         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23692         this.grid.on("rowclick", this.handleMouseDown, this);
23693         
23694         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23695             "up" : function(e){
23696                 if(!e.shiftKey){
23697                     this.selectPrevious(e.shiftKey);
23698                 }else if(this.last !== false && this.lastActive !== false){
23699                     var last = this.last;
23700                     this.selectRange(this.last,  this.lastActive-1);
23701                     this.grid.getView().focusRow(this.lastActive);
23702                     if(last !== false){
23703                         this.last = last;
23704                     }
23705                 }else{
23706                     this.selectFirstRow();
23707                 }
23708                 this.fireEvent("afterselectionchange", this);
23709             },
23710             "down" : function(e){
23711                 if(!e.shiftKey){
23712                     this.selectNext(e.shiftKey);
23713                 }else if(this.last !== false && this.lastActive !== false){
23714                     var last = this.last;
23715                     this.selectRange(this.last,  this.lastActive+1);
23716                     this.grid.getView().focusRow(this.lastActive);
23717                     if(last !== false){
23718                         this.last = last;
23719                     }
23720                 }else{
23721                     this.selectFirstRow();
23722                 }
23723                 this.fireEvent("afterselectionchange", this);
23724             },
23725             scope: this
23726         });
23727         this.grid.store.on('load', function(){
23728             this.selections.clear();
23729         },this);
23730         /*
23731         var view = this.grid.view;
23732         view.on("refresh", this.onRefresh, this);
23733         view.on("rowupdated", this.onRowUpdated, this);
23734         view.on("rowremoved", this.onRemove, this);
23735         */
23736     },
23737
23738     // private
23739     onRefresh : function()
23740     {
23741         var ds = this.grid.store, i, v = this.grid.view;
23742         var s = this.selections;
23743         s.each(function(r){
23744             if((i = ds.indexOfId(r.id)) != -1){
23745                 v.onRowSelect(i);
23746             }else{
23747                 s.remove(r);
23748             }
23749         });
23750     },
23751
23752     // private
23753     onRemove : function(v, index, r){
23754         this.selections.remove(r);
23755     },
23756
23757     // private
23758     onRowUpdated : function(v, index, r){
23759         if(this.isSelected(r)){
23760             v.onRowSelect(index);
23761         }
23762     },
23763
23764     /**
23765      * Select records.
23766      * @param {Array} records The records to select
23767      * @param {Boolean} keepExisting (optional) True to keep existing selections
23768      */
23769     selectRecords : function(records, keepExisting)
23770     {
23771         if(!keepExisting){
23772             this.clearSelections();
23773         }
23774             var ds = this.grid.store;
23775         for(var i = 0, len = records.length; i < len; i++){
23776             this.selectRow(ds.indexOf(records[i]), true);
23777         }
23778     },
23779
23780     /**
23781      * Gets the number of selected rows.
23782      * @return {Number}
23783      */
23784     getCount : function(){
23785         return this.selections.length;
23786     },
23787
23788     /**
23789      * Selects the first row in the grid.
23790      */
23791     selectFirstRow : function(){
23792         this.selectRow(0);
23793     },
23794
23795     /**
23796      * Select the last row.
23797      * @param {Boolean} keepExisting (optional) True to keep existing selections
23798      */
23799     selectLastRow : function(keepExisting){
23800         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23801         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23802     },
23803
23804     /**
23805      * Selects the row immediately following the last selected row.
23806      * @param {Boolean} keepExisting (optional) True to keep existing selections
23807      */
23808     selectNext : function(keepExisting)
23809     {
23810             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23811             this.selectRow(this.last+1, keepExisting);
23812             this.grid.getView().focusRow(this.last);
23813         }
23814     },
23815
23816     /**
23817      * Selects the row that precedes the last selected row.
23818      * @param {Boolean} keepExisting (optional) True to keep existing selections
23819      */
23820     selectPrevious : function(keepExisting){
23821         if(this.last){
23822             this.selectRow(this.last-1, keepExisting);
23823             this.grid.getView().focusRow(this.last);
23824         }
23825     },
23826
23827     /**
23828      * Returns the selected records
23829      * @return {Array} Array of selected records
23830      */
23831     getSelections : function(){
23832         return [].concat(this.selections.items);
23833     },
23834
23835     /**
23836      * Returns the first selected record.
23837      * @return {Record}
23838      */
23839     getSelected : function(){
23840         return this.selections.itemAt(0);
23841     },
23842
23843
23844     /**
23845      * Clears all selections.
23846      */
23847     clearSelections : function(fast)
23848     {
23849         if(this.locked) {
23850             return;
23851         }
23852         if(fast !== true){
23853                 var ds = this.grid.store;
23854             var s = this.selections;
23855             s.each(function(r){
23856                 this.deselectRow(ds.indexOfId(r.id));
23857             }, this);
23858             s.clear();
23859         }else{
23860             this.selections.clear();
23861         }
23862         this.last = false;
23863     },
23864
23865
23866     /**
23867      * Selects all rows.
23868      */
23869     selectAll : function(){
23870         if(this.locked) {
23871             return;
23872         }
23873         this.selections.clear();
23874         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23875             this.selectRow(i, true);
23876         }
23877     },
23878
23879     /**
23880      * Returns True if there is a selection.
23881      * @return {Boolean}
23882      */
23883     hasSelection : function(){
23884         return this.selections.length > 0;
23885     },
23886
23887     /**
23888      * Returns True if the specified row is selected.
23889      * @param {Number/Record} record The record or index of the record to check
23890      * @return {Boolean}
23891      */
23892     isSelected : function(index){
23893             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23894         return (r && this.selections.key(r.id) ? true : false);
23895     },
23896
23897     /**
23898      * Returns True if the specified record id is selected.
23899      * @param {String} id The id of record to check
23900      * @return {Boolean}
23901      */
23902     isIdSelected : function(id){
23903         return (this.selections.key(id) ? true : false);
23904     },
23905
23906
23907     // private
23908     handleMouseDBClick : function(e, t){
23909         
23910     },
23911     // private
23912     handleMouseDown : function(e, t)
23913     {
23914             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23915         if(this.isLocked() || rowIndex < 0 ){
23916             return;
23917         };
23918         if(e.shiftKey && this.last !== false){
23919             var last = this.last;
23920             this.selectRange(last, rowIndex, e.ctrlKey);
23921             this.last = last; // reset the last
23922             t.focus();
23923     
23924         }else{
23925             var isSelected = this.isSelected(rowIndex);
23926             //Roo.log("select row:" + rowIndex);
23927             if(isSelected){
23928                 this.deselectRow(rowIndex);
23929             } else {
23930                         this.selectRow(rowIndex, true);
23931             }
23932     
23933             /*
23934                 if(e.button !== 0 && isSelected){
23935                 alert('rowIndex 2: ' + rowIndex);
23936                     view.focusRow(rowIndex);
23937                 }else if(e.ctrlKey && isSelected){
23938                     this.deselectRow(rowIndex);
23939                 }else if(!isSelected){
23940                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23941                     view.focusRow(rowIndex);
23942                 }
23943             */
23944         }
23945         this.fireEvent("afterselectionchange", this);
23946     },
23947     // private
23948     handleDragableRowClick :  function(grid, rowIndex, e) 
23949     {
23950         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23951             this.selectRow(rowIndex, false);
23952             grid.view.focusRow(rowIndex);
23953              this.fireEvent("afterselectionchange", this);
23954         }
23955     },
23956     
23957     /**
23958      * Selects multiple rows.
23959      * @param {Array} rows Array of the indexes of the row to select
23960      * @param {Boolean} keepExisting (optional) True to keep existing selections
23961      */
23962     selectRows : function(rows, keepExisting){
23963         if(!keepExisting){
23964             this.clearSelections();
23965         }
23966         for(var i = 0, len = rows.length; i < len; i++){
23967             this.selectRow(rows[i], true);
23968         }
23969     },
23970
23971     /**
23972      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23973      * @param {Number} startRow The index of the first row in the range
23974      * @param {Number} endRow The index of the last row in the range
23975      * @param {Boolean} keepExisting (optional) True to retain existing selections
23976      */
23977     selectRange : function(startRow, endRow, keepExisting){
23978         if(this.locked) {
23979             return;
23980         }
23981         if(!keepExisting){
23982             this.clearSelections();
23983         }
23984         if(startRow <= endRow){
23985             for(var i = startRow; i <= endRow; i++){
23986                 this.selectRow(i, true);
23987             }
23988         }else{
23989             for(var i = startRow; i >= endRow; i--){
23990                 this.selectRow(i, true);
23991             }
23992         }
23993     },
23994
23995     /**
23996      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23997      * @param {Number} startRow The index of the first row in the range
23998      * @param {Number} endRow The index of the last row in the range
23999      */
24000     deselectRange : function(startRow, endRow, preventViewNotify){
24001         if(this.locked) {
24002             return;
24003         }
24004         for(var i = startRow; i <= endRow; i++){
24005             this.deselectRow(i, preventViewNotify);
24006         }
24007     },
24008
24009     /**
24010      * Selects a row.
24011      * @param {Number} row The index of the row to select
24012      * @param {Boolean} keepExisting (optional) True to keep existing selections
24013      */
24014     selectRow : function(index, keepExisting, preventViewNotify)
24015     {
24016             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24017             return;
24018         }
24019         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24020             if(!keepExisting || this.singleSelect){
24021                 this.clearSelections();
24022             }
24023             
24024             var r = this.grid.store.getAt(index);
24025             //console.log('selectRow - record id :' + r.id);
24026             
24027             this.selections.add(r);
24028             this.last = this.lastActive = index;
24029             if(!preventViewNotify){
24030                 var proxy = new Roo.Element(
24031                                 this.grid.getRowDom(index)
24032                 );
24033                 proxy.addClass('bg-info info');
24034             }
24035             this.fireEvent("rowselect", this, index, r);
24036             this.fireEvent("selectionchange", this);
24037         }
24038     },
24039
24040     /**
24041      * Deselects a row.
24042      * @param {Number} row The index of the row to deselect
24043      */
24044     deselectRow : function(index, preventViewNotify)
24045     {
24046         if(this.locked) {
24047             return;
24048         }
24049         if(this.last == index){
24050             this.last = false;
24051         }
24052         if(this.lastActive == index){
24053             this.lastActive = false;
24054         }
24055         
24056         var r = this.grid.store.getAt(index);
24057         if (!r) {
24058             return;
24059         }
24060         
24061         this.selections.remove(r);
24062         //.console.log('deselectRow - record id :' + r.id);
24063         if(!preventViewNotify){
24064         
24065             var proxy = new Roo.Element(
24066                 this.grid.getRowDom(index)
24067             );
24068             proxy.removeClass('bg-info info');
24069         }
24070         this.fireEvent("rowdeselect", this, index);
24071         this.fireEvent("selectionchange", this);
24072     },
24073
24074     // private
24075     restoreLast : function(){
24076         if(this._last){
24077             this.last = this._last;
24078         }
24079     },
24080
24081     // private
24082     acceptsNav : function(row, col, cm){
24083         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24084     },
24085
24086     // private
24087     onEditorKey : function(field, e){
24088         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24089         if(k == e.TAB){
24090             e.stopEvent();
24091             ed.completeEdit();
24092             if(e.shiftKey){
24093                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24094             }else{
24095                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24096             }
24097         }else if(k == e.ENTER && !e.ctrlKey){
24098             e.stopEvent();
24099             ed.completeEdit();
24100             if(e.shiftKey){
24101                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24102             }else{
24103                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24104             }
24105         }else if(k == e.ESC){
24106             ed.cancelEdit();
24107         }
24108         if(newCell){
24109             g.startEditing(newCell[0], newCell[1]);
24110         }
24111     }
24112 });
24113 /*
24114  * Based on:
24115  * Ext JS Library 1.1.1
24116  * Copyright(c) 2006-2007, Ext JS, LLC.
24117  *
24118  * Originally Released Under LGPL - original licence link has changed is not relivant.
24119  *
24120  * Fork - LGPL
24121  * <script type="text/javascript">
24122  */
24123  
24124 /**
24125  * @class Roo.bootstrap.PagingToolbar
24126  * @extends Roo.bootstrap.NavSimplebar
24127  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24128  * @constructor
24129  * Create a new PagingToolbar
24130  * @param {Object} config The config object
24131  * @param {Roo.data.Store} store
24132  */
24133 Roo.bootstrap.PagingToolbar = function(config)
24134 {
24135     // old args format still supported... - xtype is prefered..
24136         // created from xtype...
24137     
24138     this.ds = config.dataSource;
24139     
24140     if (config.store && !this.ds) {
24141         this.store= Roo.factory(config.store, Roo.data);
24142         this.ds = this.store;
24143         this.ds.xmodule = this.xmodule || false;
24144     }
24145     
24146     this.toolbarItems = [];
24147     if (config.items) {
24148         this.toolbarItems = config.items;
24149     }
24150     
24151     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24152     
24153     this.cursor = 0;
24154     
24155     if (this.ds) { 
24156         this.bind(this.ds);
24157     }
24158     
24159     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24160     
24161 };
24162
24163 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24164     /**
24165      * @cfg {Roo.data.Store} dataSource
24166      * The underlying data store providing the paged data
24167      */
24168     /**
24169      * @cfg {String/HTMLElement/Element} container
24170      * container The id or element that will contain the toolbar
24171      */
24172     /**
24173      * @cfg {Boolean} displayInfo
24174      * True to display the displayMsg (defaults to false)
24175      */
24176     /**
24177      * @cfg {Number} pageSize
24178      * The number of records to display per page (defaults to 20)
24179      */
24180     pageSize: 20,
24181     /**
24182      * @cfg {String} displayMsg
24183      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24184      */
24185     displayMsg : 'Displaying {0} - {1} of {2}',
24186     /**
24187      * @cfg {String} emptyMsg
24188      * The message to display when no records are found (defaults to "No data to display")
24189      */
24190     emptyMsg : 'No data to display',
24191     /**
24192      * Customizable piece of the default paging text (defaults to "Page")
24193      * @type String
24194      */
24195     beforePageText : "Page",
24196     /**
24197      * Customizable piece of the default paging text (defaults to "of %0")
24198      * @type String
24199      */
24200     afterPageText : "of {0}",
24201     /**
24202      * Customizable piece of the default paging text (defaults to "First Page")
24203      * @type String
24204      */
24205     firstText : "First Page",
24206     /**
24207      * Customizable piece of the default paging text (defaults to "Previous Page")
24208      * @type String
24209      */
24210     prevText : "Previous Page",
24211     /**
24212      * Customizable piece of the default paging text (defaults to "Next Page")
24213      * @type String
24214      */
24215     nextText : "Next Page",
24216     /**
24217      * Customizable piece of the default paging text (defaults to "Last Page")
24218      * @type String
24219      */
24220     lastText : "Last Page",
24221     /**
24222      * Customizable piece of the default paging text (defaults to "Refresh")
24223      * @type String
24224      */
24225     refreshText : "Refresh",
24226
24227     buttons : false,
24228     // private
24229     onRender : function(ct, position) 
24230     {
24231         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24232         this.navgroup.parentId = this.id;
24233         this.navgroup.onRender(this.el, null);
24234         // add the buttons to the navgroup
24235         
24236         if(this.displayInfo){
24237             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24238             this.displayEl = this.el.select('.x-paging-info', true).first();
24239 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24240 //            this.displayEl = navel.el.select('span',true).first();
24241         }
24242         
24243         var _this = this;
24244         
24245         if(this.buttons){
24246             Roo.each(_this.buttons, function(e){ // this might need to use render????
24247                Roo.factory(e).onRender(_this.el, null);
24248             });
24249         }
24250             
24251         Roo.each(_this.toolbarItems, function(e) {
24252             _this.navgroup.addItem(e);
24253         });
24254         
24255         
24256         this.first = this.navgroup.addItem({
24257             tooltip: this.firstText,
24258             cls: "prev",
24259             icon : 'fa fa-backward',
24260             disabled: true,
24261             preventDefault: true,
24262             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24263         });
24264         
24265         this.prev =  this.navgroup.addItem({
24266             tooltip: this.prevText,
24267             cls: "prev",
24268             icon : 'fa fa-step-backward',
24269             disabled: true,
24270             preventDefault: true,
24271             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24272         });
24273     //this.addSeparator();
24274         
24275         
24276         var field = this.navgroup.addItem( {
24277             tagtype : 'span',
24278             cls : 'x-paging-position',
24279             
24280             html : this.beforePageText  +
24281                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24282                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24283          } ); //?? escaped?
24284         
24285         this.field = field.el.select('input', true).first();
24286         this.field.on("keydown", this.onPagingKeydown, this);
24287         this.field.on("focus", function(){this.dom.select();});
24288     
24289     
24290         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24291         //this.field.setHeight(18);
24292         //this.addSeparator();
24293         this.next = this.navgroup.addItem({
24294             tooltip: this.nextText,
24295             cls: "next",
24296             html : ' <i class="fa fa-step-forward">',
24297             disabled: true,
24298             preventDefault: true,
24299             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24300         });
24301         this.last = this.navgroup.addItem({
24302             tooltip: this.lastText,
24303             icon : 'fa fa-forward',
24304             cls: "next",
24305             disabled: true,
24306             preventDefault: true,
24307             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24308         });
24309     //this.addSeparator();
24310         this.loading = this.navgroup.addItem({
24311             tooltip: this.refreshText,
24312             icon: 'fa fa-refresh',
24313             preventDefault: true,
24314             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24315         });
24316         
24317     },
24318
24319     // private
24320     updateInfo : function(){
24321         if(this.displayEl){
24322             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24323             var msg = count == 0 ?
24324                 this.emptyMsg :
24325                 String.format(
24326                     this.displayMsg,
24327                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24328                 );
24329             this.displayEl.update(msg);
24330         }
24331     },
24332
24333     // private
24334     onLoad : function(ds, r, o)
24335     {
24336         this.cursor = o.params ? o.params.start : 0;
24337         var d = this.getPageData(),
24338             ap = d.activePage,
24339             ps = d.pages;
24340         
24341         
24342         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24343         this.field.dom.value = ap;
24344         this.first.setDisabled(ap == 1);
24345         this.prev.setDisabled(ap == 1);
24346         this.next.setDisabled(ap == ps);
24347         this.last.setDisabled(ap == ps);
24348         this.loading.enable();
24349         this.updateInfo();
24350     },
24351
24352     // private
24353     getPageData : function(){
24354         var total = this.ds.getTotalCount();
24355         return {
24356             total : total,
24357             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24358             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24359         };
24360     },
24361
24362     // private
24363     onLoadError : function(){
24364         this.loading.enable();
24365     },
24366
24367     // private
24368     onPagingKeydown : function(e){
24369         var k = e.getKey();
24370         var d = this.getPageData();
24371         if(k == e.RETURN){
24372             var v = this.field.dom.value, pageNum;
24373             if(!v || isNaN(pageNum = parseInt(v, 10))){
24374                 this.field.dom.value = d.activePage;
24375                 return;
24376             }
24377             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24378             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24379             e.stopEvent();
24380         }
24381         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))
24382         {
24383           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24384           this.field.dom.value = pageNum;
24385           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24386           e.stopEvent();
24387         }
24388         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24389         {
24390           var v = this.field.dom.value, pageNum; 
24391           var increment = (e.shiftKey) ? 10 : 1;
24392           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24393                 increment *= -1;
24394           }
24395           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24396             this.field.dom.value = d.activePage;
24397             return;
24398           }
24399           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24400           {
24401             this.field.dom.value = parseInt(v, 10) + increment;
24402             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24403             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24404           }
24405           e.stopEvent();
24406         }
24407     },
24408
24409     // private
24410     beforeLoad : function(){
24411         if(this.loading){
24412             this.loading.disable();
24413         }
24414     },
24415
24416     // private
24417     onClick : function(which){
24418         
24419         var ds = this.ds;
24420         if (!ds) {
24421             return;
24422         }
24423         
24424         switch(which){
24425             case "first":
24426                 ds.load({params:{start: 0, limit: this.pageSize}});
24427             break;
24428             case "prev":
24429                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24430             break;
24431             case "next":
24432                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24433             break;
24434             case "last":
24435                 var total = ds.getTotalCount();
24436                 var extra = total % this.pageSize;
24437                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24438                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24439             break;
24440             case "refresh":
24441                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24442             break;
24443         }
24444     },
24445
24446     /**
24447      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24448      * @param {Roo.data.Store} store The data store to unbind
24449      */
24450     unbind : function(ds){
24451         ds.un("beforeload", this.beforeLoad, this);
24452         ds.un("load", this.onLoad, this);
24453         ds.un("loadexception", this.onLoadError, this);
24454         ds.un("remove", this.updateInfo, this);
24455         ds.un("add", this.updateInfo, this);
24456         this.ds = undefined;
24457     },
24458
24459     /**
24460      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24461      * @param {Roo.data.Store} store The data store to bind
24462      */
24463     bind : function(ds){
24464         ds.on("beforeload", this.beforeLoad, this);
24465         ds.on("load", this.onLoad, this);
24466         ds.on("loadexception", this.onLoadError, this);
24467         ds.on("remove", this.updateInfo, this);
24468         ds.on("add", this.updateInfo, this);
24469         this.ds = ds;
24470     }
24471 });/*
24472  * - LGPL
24473  *
24474  * element
24475  * 
24476  */
24477
24478 /**
24479  * @class Roo.bootstrap.MessageBar
24480  * @extends Roo.bootstrap.Component
24481  * Bootstrap MessageBar class
24482  * @cfg {String} html contents of the MessageBar
24483  * @cfg {String} weight (info | success | warning | danger) default info
24484  * @cfg {String} beforeClass insert the bar before the given class
24485  * @cfg {Boolean} closable (true | false) default false
24486  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24487  * 
24488  * @constructor
24489  * Create a new Element
24490  * @param {Object} config The config object
24491  */
24492
24493 Roo.bootstrap.MessageBar = function(config){
24494     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24495 };
24496
24497 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24498     
24499     html: '',
24500     weight: 'info',
24501     closable: false,
24502     fixed: false,
24503     beforeClass: 'bootstrap-sticky-wrap',
24504     
24505     getAutoCreate : function(){
24506         
24507         var cfg = {
24508             tag: 'div',
24509             cls: 'alert alert-dismissable alert-' + this.weight,
24510             cn: [
24511                 {
24512                     tag: 'span',
24513                     cls: 'message',
24514                     html: this.html || ''
24515                 }
24516             ]
24517         };
24518         
24519         if(this.fixed){
24520             cfg.cls += ' alert-messages-fixed';
24521         }
24522         
24523         if(this.closable){
24524             cfg.cn.push({
24525                 tag: 'button',
24526                 cls: 'close',
24527                 html: 'x'
24528             });
24529         }
24530         
24531         return cfg;
24532     },
24533     
24534     onRender : function(ct, position)
24535     {
24536         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24537         
24538         if(!this.el){
24539             var cfg = Roo.apply({},  this.getAutoCreate());
24540             cfg.id = Roo.id();
24541             
24542             if (this.cls) {
24543                 cfg.cls += ' ' + this.cls;
24544             }
24545             if (this.style) {
24546                 cfg.style = this.style;
24547             }
24548             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24549             
24550             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24551         }
24552         
24553         this.el.select('>button.close').on('click', this.hide, this);
24554         
24555     },
24556     
24557     show : function()
24558     {
24559         if (!this.rendered) {
24560             this.render();
24561         }
24562         
24563         this.el.show();
24564         
24565         this.fireEvent('show', this);
24566         
24567     },
24568     
24569     hide : function()
24570     {
24571         if (!this.rendered) {
24572             this.render();
24573         }
24574         
24575         this.el.hide();
24576         
24577         this.fireEvent('hide', this);
24578     },
24579     
24580     update : function()
24581     {
24582 //        var e = this.el.dom.firstChild;
24583 //        
24584 //        if(this.closable){
24585 //            e = e.nextSibling;
24586 //        }
24587 //        
24588 //        e.data = this.html || '';
24589
24590         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24591     }
24592    
24593 });
24594
24595  
24596
24597      /*
24598  * - LGPL
24599  *
24600  * Graph
24601  * 
24602  */
24603
24604
24605 /**
24606  * @class Roo.bootstrap.Graph
24607  * @extends Roo.bootstrap.Component
24608  * Bootstrap Graph class
24609 > Prameters
24610  -sm {number} sm 4
24611  -md {number} md 5
24612  @cfg {String} graphtype  bar | vbar | pie
24613  @cfg {number} g_x coodinator | centre x (pie)
24614  @cfg {number} g_y coodinator | centre y (pie)
24615  @cfg {number} g_r radius (pie)
24616  @cfg {number} g_height height of the chart (respected by all elements in the set)
24617  @cfg {number} g_width width of the chart (respected by all elements in the set)
24618  @cfg {Object} title The title of the chart
24619     
24620  -{Array}  values
24621  -opts (object) options for the chart 
24622      o {
24623      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24624      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24625      o vgutter (number)
24626      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.
24627      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24628      o to
24629      o stretch (boolean)
24630      o }
24631  -opts (object) options for the pie
24632      o{
24633      o cut
24634      o startAngle (number)
24635      o endAngle (number)
24636      } 
24637  *
24638  * @constructor
24639  * Create a new Input
24640  * @param {Object} config The config object
24641  */
24642
24643 Roo.bootstrap.Graph = function(config){
24644     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24645     
24646     this.addEvents({
24647         // img events
24648         /**
24649          * @event click
24650          * The img click event for the img.
24651          * @param {Roo.EventObject} e
24652          */
24653         "click" : true
24654     });
24655 };
24656
24657 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24658     
24659     sm: 4,
24660     md: 5,
24661     graphtype: 'bar',
24662     g_height: 250,
24663     g_width: 400,
24664     g_x: 50,
24665     g_y: 50,
24666     g_r: 30,
24667     opts:{
24668         //g_colors: this.colors,
24669         g_type: 'soft',
24670         g_gutter: '20%'
24671
24672     },
24673     title : false,
24674
24675     getAutoCreate : function(){
24676         
24677         var cfg = {
24678             tag: 'div',
24679             html : null
24680         };
24681         
24682         
24683         return  cfg;
24684     },
24685
24686     onRender : function(ct,position){
24687         
24688         
24689         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24690         
24691         if (typeof(Raphael) == 'undefined') {
24692             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24693             return;
24694         }
24695         
24696         this.raphael = Raphael(this.el.dom);
24697         
24698                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24699                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24700                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24701                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24702                 /*
24703                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24704                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24705                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24706                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24707                 
24708                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24709                 r.barchart(330, 10, 300, 220, data1);
24710                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24711                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24712                 */
24713                 
24714                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24715                 // r.barchart(30, 30, 560, 250,  xdata, {
24716                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24717                 //     axis : "0 0 1 1",
24718                 //     axisxlabels :  xdata
24719                 //     //yvalues : cols,
24720                    
24721                 // });
24722 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24723 //        
24724 //        this.load(null,xdata,{
24725 //                axis : "0 0 1 1",
24726 //                axisxlabels :  xdata
24727 //                });
24728
24729     },
24730
24731     load : function(graphtype,xdata,opts)
24732     {
24733         this.raphael.clear();
24734         if(!graphtype) {
24735             graphtype = this.graphtype;
24736         }
24737         if(!opts){
24738             opts = this.opts;
24739         }
24740         var r = this.raphael,
24741             fin = function () {
24742                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24743             },
24744             fout = function () {
24745                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24746             },
24747             pfin = function() {
24748                 this.sector.stop();
24749                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24750
24751                 if (this.label) {
24752                     this.label[0].stop();
24753                     this.label[0].attr({ r: 7.5 });
24754                     this.label[1].attr({ "font-weight": 800 });
24755                 }
24756             },
24757             pfout = function() {
24758                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24759
24760                 if (this.label) {
24761                     this.label[0].animate({ r: 5 }, 500, "bounce");
24762                     this.label[1].attr({ "font-weight": 400 });
24763                 }
24764             };
24765
24766         switch(graphtype){
24767             case 'bar':
24768                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24769                 break;
24770             case 'hbar':
24771                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24772                 break;
24773             case 'pie':
24774 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24775 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24776 //            
24777                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24778                 
24779                 break;
24780
24781         }
24782         
24783         if(this.title){
24784             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24785         }
24786         
24787     },
24788     
24789     setTitle: function(o)
24790     {
24791         this.title = o;
24792     },
24793     
24794     initEvents: function() {
24795         
24796         if(!this.href){
24797             this.el.on('click', this.onClick, this);
24798         }
24799     },
24800     
24801     onClick : function(e)
24802     {
24803         Roo.log('img onclick');
24804         this.fireEvent('click', this, e);
24805     }
24806    
24807 });
24808
24809  
24810 /*
24811  * - LGPL
24812  *
24813  * numberBox
24814  * 
24815  */
24816 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24817
24818 /**
24819  * @class Roo.bootstrap.dash.NumberBox
24820  * @extends Roo.bootstrap.Component
24821  * Bootstrap NumberBox class
24822  * @cfg {String} headline Box headline
24823  * @cfg {String} content Box content
24824  * @cfg {String} icon Box icon
24825  * @cfg {String} footer Footer text
24826  * @cfg {String} fhref Footer href
24827  * 
24828  * @constructor
24829  * Create a new NumberBox
24830  * @param {Object} config The config object
24831  */
24832
24833
24834 Roo.bootstrap.dash.NumberBox = function(config){
24835     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24836     
24837 };
24838
24839 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24840     
24841     headline : '',
24842     content : '',
24843     icon : '',
24844     footer : '',
24845     fhref : '',
24846     ficon : '',
24847     
24848     getAutoCreate : function(){
24849         
24850         var cfg = {
24851             tag : 'div',
24852             cls : 'small-box ',
24853             cn : [
24854                 {
24855                     tag : 'div',
24856                     cls : 'inner',
24857                     cn :[
24858                         {
24859                             tag : 'h3',
24860                             cls : 'roo-headline',
24861                             html : this.headline
24862                         },
24863                         {
24864                             tag : 'p',
24865                             cls : 'roo-content',
24866                             html : this.content
24867                         }
24868                     ]
24869                 }
24870             ]
24871         };
24872         
24873         if(this.icon){
24874             cfg.cn.push({
24875                 tag : 'div',
24876                 cls : 'icon',
24877                 cn :[
24878                     {
24879                         tag : 'i',
24880                         cls : 'ion ' + this.icon
24881                     }
24882                 ]
24883             });
24884         }
24885         
24886         if(this.footer){
24887             var footer = {
24888                 tag : 'a',
24889                 cls : 'small-box-footer',
24890                 href : this.fhref || '#',
24891                 html : this.footer
24892             };
24893             
24894             cfg.cn.push(footer);
24895             
24896         }
24897         
24898         return  cfg;
24899     },
24900
24901     onRender : function(ct,position){
24902         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24903
24904
24905        
24906                 
24907     },
24908
24909     setHeadline: function (value)
24910     {
24911         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24912     },
24913     
24914     setFooter: function (value, href)
24915     {
24916         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24917         
24918         if(href){
24919             this.el.select('a.small-box-footer',true).first().attr('href', href);
24920         }
24921         
24922     },
24923
24924     setContent: function (value)
24925     {
24926         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24927     },
24928
24929     initEvents: function() 
24930     {   
24931         
24932     }
24933     
24934 });
24935
24936  
24937 /*
24938  * - LGPL
24939  *
24940  * TabBox
24941  * 
24942  */
24943 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24944
24945 /**
24946  * @class Roo.bootstrap.dash.TabBox
24947  * @extends Roo.bootstrap.Component
24948  * Bootstrap TabBox class
24949  * @cfg {String} title Title of the TabBox
24950  * @cfg {String} icon Icon of the TabBox
24951  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24952  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24953  * 
24954  * @constructor
24955  * Create a new TabBox
24956  * @param {Object} config The config object
24957  */
24958
24959
24960 Roo.bootstrap.dash.TabBox = function(config){
24961     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24962     this.addEvents({
24963         // raw events
24964         /**
24965          * @event addpane
24966          * When a pane is added
24967          * @param {Roo.bootstrap.dash.TabPane} pane
24968          */
24969         "addpane" : true,
24970         /**
24971          * @event activatepane
24972          * When a pane is activated
24973          * @param {Roo.bootstrap.dash.TabPane} pane
24974          */
24975         "activatepane" : true
24976         
24977          
24978     });
24979     
24980     this.panes = [];
24981 };
24982
24983 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24984
24985     title : '',
24986     icon : false,
24987     showtabs : true,
24988     tabScrollable : false,
24989     
24990     getChildContainer : function()
24991     {
24992         return this.el.select('.tab-content', true).first();
24993     },
24994     
24995     getAutoCreate : function(){
24996         
24997         var header = {
24998             tag: 'li',
24999             cls: 'pull-left header',
25000             html: this.title,
25001             cn : []
25002         };
25003         
25004         if(this.icon){
25005             header.cn.push({
25006                 tag: 'i',
25007                 cls: 'fa ' + this.icon
25008             });
25009         }
25010         
25011         var h = {
25012             tag: 'ul',
25013             cls: 'nav nav-tabs pull-right',
25014             cn: [
25015                 header
25016             ]
25017         };
25018         
25019         if(this.tabScrollable){
25020             h = {
25021                 tag: 'div',
25022                 cls: 'tab-header',
25023                 cn: [
25024                     {
25025                         tag: 'ul',
25026                         cls: 'nav nav-tabs pull-right',
25027                         cn: [
25028                             header
25029                         ]
25030                     }
25031                 ]
25032             };
25033         }
25034         
25035         var cfg = {
25036             tag: 'div',
25037             cls: 'nav-tabs-custom',
25038             cn: [
25039                 h,
25040                 {
25041                     tag: 'div',
25042                     cls: 'tab-content no-padding',
25043                     cn: []
25044                 }
25045             ]
25046         };
25047
25048         return  cfg;
25049     },
25050     initEvents : function()
25051     {
25052         //Roo.log('add add pane handler');
25053         this.on('addpane', this.onAddPane, this);
25054     },
25055      /**
25056      * Updates the box title
25057      * @param {String} html to set the title to.
25058      */
25059     setTitle : function(value)
25060     {
25061         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25062     },
25063     onAddPane : function(pane)
25064     {
25065         this.panes.push(pane);
25066         //Roo.log('addpane');
25067         //Roo.log(pane);
25068         // tabs are rendere left to right..
25069         if(!this.showtabs){
25070             return;
25071         }
25072         
25073         var ctr = this.el.select('.nav-tabs', true).first();
25074          
25075          
25076         var existing = ctr.select('.nav-tab',true);
25077         var qty = existing.getCount();;
25078         
25079         
25080         var tab = ctr.createChild({
25081             tag : 'li',
25082             cls : 'nav-tab' + (qty ? '' : ' active'),
25083             cn : [
25084                 {
25085                     tag : 'a',
25086                     href:'#',
25087                     html : pane.title
25088                 }
25089             ]
25090         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25091         pane.tab = tab;
25092         
25093         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25094         if (!qty) {
25095             pane.el.addClass('active');
25096         }
25097         
25098                 
25099     },
25100     onTabClick : function(ev,un,ob,pane)
25101     {
25102         //Roo.log('tab - prev default');
25103         ev.preventDefault();
25104         
25105         
25106         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25107         pane.tab.addClass('active');
25108         //Roo.log(pane.title);
25109         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25110         // technically we should have a deactivate event.. but maybe add later.
25111         // and it should not de-activate the selected tab...
25112         this.fireEvent('activatepane', pane);
25113         pane.el.addClass('active');
25114         pane.fireEvent('activate');
25115         
25116         
25117     },
25118     
25119     getActivePane : function()
25120     {
25121         var r = false;
25122         Roo.each(this.panes, function(p) {
25123             if(p.el.hasClass('active')){
25124                 r = p;
25125                 return false;
25126             }
25127             
25128             return;
25129         });
25130         
25131         return r;
25132     }
25133     
25134     
25135 });
25136
25137  
25138 /*
25139  * - LGPL
25140  *
25141  * Tab pane
25142  * 
25143  */
25144 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25145 /**
25146  * @class Roo.bootstrap.TabPane
25147  * @extends Roo.bootstrap.Component
25148  * Bootstrap TabPane class
25149  * @cfg {Boolean} active (false | true) Default false
25150  * @cfg {String} title title of panel
25151
25152  * 
25153  * @constructor
25154  * Create a new TabPane
25155  * @param {Object} config The config object
25156  */
25157
25158 Roo.bootstrap.dash.TabPane = function(config){
25159     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25160     
25161     this.addEvents({
25162         // raw events
25163         /**
25164          * @event activate
25165          * When a pane is activated
25166          * @param {Roo.bootstrap.dash.TabPane} pane
25167          */
25168         "activate" : true
25169          
25170     });
25171 };
25172
25173 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25174     
25175     active : false,
25176     title : '',
25177     
25178     // the tabBox that this is attached to.
25179     tab : false,
25180      
25181     getAutoCreate : function() 
25182     {
25183         var cfg = {
25184             tag: 'div',
25185             cls: 'tab-pane'
25186         };
25187         
25188         if(this.active){
25189             cfg.cls += ' active';
25190         }
25191         
25192         return cfg;
25193     },
25194     initEvents  : function()
25195     {
25196         //Roo.log('trigger add pane handler');
25197         this.parent().fireEvent('addpane', this)
25198     },
25199     
25200      /**
25201      * Updates the tab title 
25202      * @param {String} html to set the title to.
25203      */
25204     setTitle: function(str)
25205     {
25206         if (!this.tab) {
25207             return;
25208         }
25209         this.title = str;
25210         this.tab.select('a', true).first().dom.innerHTML = str;
25211         
25212     }
25213     
25214     
25215     
25216 });
25217
25218  
25219
25220
25221  /*
25222  * - LGPL
25223  *
25224  * menu
25225  * 
25226  */
25227 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25228
25229 /**
25230  * @class Roo.bootstrap.menu.Menu
25231  * @extends Roo.bootstrap.Component
25232  * Bootstrap Menu class - container for Menu
25233  * @cfg {String} html Text of the menu
25234  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25235  * @cfg {String} icon Font awesome icon
25236  * @cfg {String} pos Menu align to (top | bottom) default bottom
25237  * 
25238  * 
25239  * @constructor
25240  * Create a new Menu
25241  * @param {Object} config The config object
25242  */
25243
25244
25245 Roo.bootstrap.menu.Menu = function(config){
25246     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25247     
25248     this.addEvents({
25249         /**
25250          * @event beforeshow
25251          * Fires before this menu is displayed
25252          * @param {Roo.bootstrap.menu.Menu} this
25253          */
25254         beforeshow : true,
25255         /**
25256          * @event beforehide
25257          * Fires before this menu is hidden
25258          * @param {Roo.bootstrap.menu.Menu} this
25259          */
25260         beforehide : true,
25261         /**
25262          * @event show
25263          * Fires after this menu is displayed
25264          * @param {Roo.bootstrap.menu.Menu} this
25265          */
25266         show : true,
25267         /**
25268          * @event hide
25269          * Fires after this menu is hidden
25270          * @param {Roo.bootstrap.menu.Menu} this
25271          */
25272         hide : true,
25273         /**
25274          * @event click
25275          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25276          * @param {Roo.bootstrap.menu.Menu} this
25277          * @param {Roo.EventObject} e
25278          */
25279         click : true
25280     });
25281     
25282 };
25283
25284 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25285     
25286     submenu : false,
25287     html : '',
25288     weight : 'default',
25289     icon : false,
25290     pos : 'bottom',
25291     
25292     
25293     getChildContainer : function() {
25294         if(this.isSubMenu){
25295             return this.el;
25296         }
25297         
25298         return this.el.select('ul.dropdown-menu', true).first();  
25299     },
25300     
25301     getAutoCreate : function()
25302     {
25303         var text = [
25304             {
25305                 tag : 'span',
25306                 cls : 'roo-menu-text',
25307                 html : this.html
25308             }
25309         ];
25310         
25311         if(this.icon){
25312             text.unshift({
25313                 tag : 'i',
25314                 cls : 'fa ' + this.icon
25315             })
25316         }
25317         
25318         
25319         var cfg = {
25320             tag : 'div',
25321             cls : 'btn-group',
25322             cn : [
25323                 {
25324                     tag : 'button',
25325                     cls : 'dropdown-button btn btn-' + this.weight,
25326                     cn : text
25327                 },
25328                 {
25329                     tag : 'button',
25330                     cls : 'dropdown-toggle btn btn-' + this.weight,
25331                     cn : [
25332                         {
25333                             tag : 'span',
25334                             cls : 'caret'
25335                         }
25336                     ]
25337                 },
25338                 {
25339                     tag : 'ul',
25340                     cls : 'dropdown-menu'
25341                 }
25342             ]
25343             
25344         };
25345         
25346         if(this.pos == 'top'){
25347             cfg.cls += ' dropup';
25348         }
25349         
25350         if(this.isSubMenu){
25351             cfg = {
25352                 tag : 'ul',
25353                 cls : 'dropdown-menu'
25354             }
25355         }
25356         
25357         return cfg;
25358     },
25359     
25360     onRender : function(ct, position)
25361     {
25362         this.isSubMenu = ct.hasClass('dropdown-submenu');
25363         
25364         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25365     },
25366     
25367     initEvents : function() 
25368     {
25369         if(this.isSubMenu){
25370             return;
25371         }
25372         
25373         this.hidden = true;
25374         
25375         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25376         this.triggerEl.on('click', this.onTriggerPress, this);
25377         
25378         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25379         this.buttonEl.on('click', this.onClick, this);
25380         
25381     },
25382     
25383     list : function()
25384     {
25385         if(this.isSubMenu){
25386             return this.el;
25387         }
25388         
25389         return this.el.select('ul.dropdown-menu', true).first();
25390     },
25391     
25392     onClick : function(e)
25393     {
25394         this.fireEvent("click", this, e);
25395     },
25396     
25397     onTriggerPress  : function(e)
25398     {   
25399         if (this.isVisible()) {
25400             this.hide();
25401         } else {
25402             this.show();
25403         }
25404     },
25405     
25406     isVisible : function(){
25407         return !this.hidden;
25408     },
25409     
25410     show : function()
25411     {
25412         this.fireEvent("beforeshow", this);
25413         
25414         this.hidden = false;
25415         this.el.addClass('open');
25416         
25417         Roo.get(document).on("mouseup", this.onMouseUp, this);
25418         
25419         this.fireEvent("show", this);
25420         
25421         
25422     },
25423     
25424     hide : function()
25425     {
25426         this.fireEvent("beforehide", this);
25427         
25428         this.hidden = true;
25429         this.el.removeClass('open');
25430         
25431         Roo.get(document).un("mouseup", this.onMouseUp);
25432         
25433         this.fireEvent("hide", this);
25434     },
25435     
25436     onMouseUp : function()
25437     {
25438         this.hide();
25439     }
25440     
25441 });
25442
25443  
25444  /*
25445  * - LGPL
25446  *
25447  * menu item
25448  * 
25449  */
25450 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25451
25452 /**
25453  * @class Roo.bootstrap.menu.Item
25454  * @extends Roo.bootstrap.Component
25455  * Bootstrap MenuItem class
25456  * @cfg {Boolean} submenu (true | false) default false
25457  * @cfg {String} html text of the item
25458  * @cfg {String} href the link
25459  * @cfg {Boolean} disable (true | false) default false
25460  * @cfg {Boolean} preventDefault (true | false) default true
25461  * @cfg {String} icon Font awesome icon
25462  * @cfg {String} pos Submenu align to (left | right) default right 
25463  * 
25464  * 
25465  * @constructor
25466  * Create a new Item
25467  * @param {Object} config The config object
25468  */
25469
25470
25471 Roo.bootstrap.menu.Item = function(config){
25472     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25473     this.addEvents({
25474         /**
25475          * @event mouseover
25476          * Fires when the mouse is hovering over this menu
25477          * @param {Roo.bootstrap.menu.Item} this
25478          * @param {Roo.EventObject} e
25479          */
25480         mouseover : true,
25481         /**
25482          * @event mouseout
25483          * Fires when the mouse exits this menu
25484          * @param {Roo.bootstrap.menu.Item} this
25485          * @param {Roo.EventObject} e
25486          */
25487         mouseout : true,
25488         // raw events
25489         /**
25490          * @event click
25491          * The raw click event for the entire grid.
25492          * @param {Roo.EventObject} e
25493          */
25494         click : true
25495     });
25496 };
25497
25498 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25499     
25500     submenu : false,
25501     href : '',
25502     html : '',
25503     preventDefault: true,
25504     disable : false,
25505     icon : false,
25506     pos : 'right',
25507     
25508     getAutoCreate : function()
25509     {
25510         var text = [
25511             {
25512                 tag : 'span',
25513                 cls : 'roo-menu-item-text',
25514                 html : this.html
25515             }
25516         ];
25517         
25518         if(this.icon){
25519             text.unshift({
25520                 tag : 'i',
25521                 cls : 'fa ' + this.icon
25522             })
25523         }
25524         
25525         var cfg = {
25526             tag : 'li',
25527             cn : [
25528                 {
25529                     tag : 'a',
25530                     href : this.href || '#',
25531                     cn : text
25532                 }
25533             ]
25534         };
25535         
25536         if(this.disable){
25537             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25538         }
25539         
25540         if(this.submenu){
25541             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25542             
25543             if(this.pos == 'left'){
25544                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25545             }
25546         }
25547         
25548         return cfg;
25549     },
25550     
25551     initEvents : function() 
25552     {
25553         this.el.on('mouseover', this.onMouseOver, this);
25554         this.el.on('mouseout', this.onMouseOut, this);
25555         
25556         this.el.select('a', true).first().on('click', this.onClick, this);
25557         
25558     },
25559     
25560     onClick : function(e)
25561     {
25562         if(this.preventDefault){
25563             e.preventDefault();
25564         }
25565         
25566         this.fireEvent("click", this, e);
25567     },
25568     
25569     onMouseOver : function(e)
25570     {
25571         if(this.submenu && this.pos == 'left'){
25572             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25573         }
25574         
25575         this.fireEvent("mouseover", this, e);
25576     },
25577     
25578     onMouseOut : function(e)
25579     {
25580         this.fireEvent("mouseout", this, e);
25581     }
25582 });
25583
25584  
25585
25586  /*
25587  * - LGPL
25588  *
25589  * menu separator
25590  * 
25591  */
25592 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25593
25594 /**
25595  * @class Roo.bootstrap.menu.Separator
25596  * @extends Roo.bootstrap.Component
25597  * Bootstrap Separator class
25598  * 
25599  * @constructor
25600  * Create a new Separator
25601  * @param {Object} config The config object
25602  */
25603
25604
25605 Roo.bootstrap.menu.Separator = function(config){
25606     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25607 };
25608
25609 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25610     
25611     getAutoCreate : function(){
25612         var cfg = {
25613             tag : 'li',
25614             cls: 'divider'
25615         };
25616         
25617         return cfg;
25618     }
25619    
25620 });
25621
25622  
25623
25624  /*
25625  * - LGPL
25626  *
25627  * Tooltip
25628  * 
25629  */
25630
25631 /**
25632  * @class Roo.bootstrap.Tooltip
25633  * Bootstrap Tooltip class
25634  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25635  * to determine which dom element triggers the tooltip.
25636  * 
25637  * It needs to add support for additional attributes like tooltip-position
25638  * 
25639  * @constructor
25640  * Create a new Toolti
25641  * @param {Object} config The config object
25642  */
25643
25644 Roo.bootstrap.Tooltip = function(config){
25645     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25646     
25647     this.alignment = Roo.bootstrap.Tooltip.alignment;
25648     
25649     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25650         this.alignment = config.alignment;
25651     }
25652     
25653 };
25654
25655 Roo.apply(Roo.bootstrap.Tooltip, {
25656     /**
25657      * @function init initialize tooltip monitoring.
25658      * @static
25659      */
25660     currentEl : false,
25661     currentTip : false,
25662     currentRegion : false,
25663     
25664     //  init : delay?
25665     
25666     init : function()
25667     {
25668         Roo.get(document).on('mouseover', this.enter ,this);
25669         Roo.get(document).on('mouseout', this.leave, this);
25670          
25671         
25672         this.currentTip = new Roo.bootstrap.Tooltip();
25673     },
25674     
25675     enter : function(ev)
25676     {
25677         var dom = ev.getTarget();
25678         
25679         //Roo.log(['enter',dom]);
25680         var el = Roo.fly(dom);
25681         if (this.currentEl) {
25682             //Roo.log(dom);
25683             //Roo.log(this.currentEl);
25684             //Roo.log(this.currentEl.contains(dom));
25685             if (this.currentEl == el) {
25686                 return;
25687             }
25688             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25689                 return;
25690             }
25691
25692         }
25693         
25694         if (this.currentTip.el) {
25695             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25696         }    
25697         //Roo.log(ev);
25698         
25699         if(!el || el.dom == document){
25700             return;
25701         }
25702         
25703         var bindEl = el;
25704         
25705         // you can not look for children, as if el is the body.. then everythign is the child..
25706         if (!el.attr('tooltip')) { //
25707             if (!el.select("[tooltip]").elements.length) {
25708                 return;
25709             }
25710             // is the mouse over this child...?
25711             bindEl = el.select("[tooltip]").first();
25712             var xy = ev.getXY();
25713             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25714                 //Roo.log("not in region.");
25715                 return;
25716             }
25717             //Roo.log("child element over..");
25718             
25719         }
25720         this.currentEl = bindEl;
25721         this.currentTip.bind(bindEl);
25722         this.currentRegion = Roo.lib.Region.getRegion(dom);
25723         this.currentTip.enter();
25724         
25725     },
25726     leave : function(ev)
25727     {
25728         var dom = ev.getTarget();
25729         //Roo.log(['leave',dom]);
25730         if (!this.currentEl) {
25731             return;
25732         }
25733         
25734         
25735         if (dom != this.currentEl.dom) {
25736             return;
25737         }
25738         var xy = ev.getXY();
25739         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25740             return;
25741         }
25742         // only activate leave if mouse cursor is outside... bounding box..
25743         
25744         
25745         
25746         
25747         if (this.currentTip) {
25748             this.currentTip.leave();
25749         }
25750         //Roo.log('clear currentEl');
25751         this.currentEl = false;
25752         
25753         
25754     },
25755     alignment : {
25756         'left' : ['r-l', [-2,0], 'right'],
25757         'right' : ['l-r', [2,0], 'left'],
25758         'bottom' : ['t-b', [0,2], 'top'],
25759         'top' : [ 'b-t', [0,-2], 'bottom']
25760     }
25761     
25762 });
25763
25764
25765 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25766     
25767     
25768     bindEl : false,
25769     
25770     delay : null, // can be { show : 300 , hide: 500}
25771     
25772     timeout : null,
25773     
25774     hoverState : null, //???
25775     
25776     placement : 'bottom', 
25777     
25778     alignment : false,
25779     
25780     getAutoCreate : function(){
25781     
25782         var cfg = {
25783            cls : 'tooltip',
25784            role : 'tooltip',
25785            cn : [
25786                 {
25787                     cls : 'tooltip-arrow'
25788                 },
25789                 {
25790                     cls : 'tooltip-inner'
25791                 }
25792            ]
25793         };
25794         
25795         return cfg;
25796     },
25797     bind : function(el)
25798     {
25799         this.bindEl = el;
25800     },
25801       
25802     
25803     enter : function () {
25804        
25805         if (this.timeout != null) {
25806             clearTimeout(this.timeout);
25807         }
25808         
25809         this.hoverState = 'in';
25810          //Roo.log("enter - show");
25811         if (!this.delay || !this.delay.show) {
25812             this.show();
25813             return;
25814         }
25815         var _t = this;
25816         this.timeout = setTimeout(function () {
25817             if (_t.hoverState == 'in') {
25818                 _t.show();
25819             }
25820         }, this.delay.show);
25821     },
25822     leave : function()
25823     {
25824         clearTimeout(this.timeout);
25825     
25826         this.hoverState = 'out';
25827          if (!this.delay || !this.delay.hide) {
25828             this.hide();
25829             return;
25830         }
25831        
25832         var _t = this;
25833         this.timeout = setTimeout(function () {
25834             //Roo.log("leave - timeout");
25835             
25836             if (_t.hoverState == 'out') {
25837                 _t.hide();
25838                 Roo.bootstrap.Tooltip.currentEl = false;
25839             }
25840         }, delay);
25841     },
25842     
25843     show : function (msg)
25844     {
25845         if (!this.el) {
25846             this.render(document.body);
25847         }
25848         // set content.
25849         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25850         
25851         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25852         
25853         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25854         
25855         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25856         
25857         var placement = typeof this.placement == 'function' ?
25858             this.placement.call(this, this.el, on_el) :
25859             this.placement;
25860             
25861         var autoToken = /\s?auto?\s?/i;
25862         var autoPlace = autoToken.test(placement);
25863         if (autoPlace) {
25864             placement = placement.replace(autoToken, '') || 'top';
25865         }
25866         
25867         //this.el.detach()
25868         //this.el.setXY([0,0]);
25869         this.el.show();
25870         //this.el.dom.style.display='block';
25871         
25872         //this.el.appendTo(on_el);
25873         
25874         var p = this.getPosition();
25875         var box = this.el.getBox();
25876         
25877         if (autoPlace) {
25878             // fixme..
25879         }
25880         
25881         var align = this.alignment[placement];
25882         
25883         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25884         
25885         if(placement == 'top' || placement == 'bottom'){
25886             if(xy[0] < 0){
25887                 placement = 'right';
25888             }
25889             
25890             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25891                 placement = 'left';
25892             }
25893             
25894             var scroll = Roo.select('body', true).first().getScroll();
25895             
25896             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25897                 placement = 'top';
25898             }
25899             
25900         }
25901         
25902         this.el.alignTo(this.bindEl, align[0],align[1]);
25903         //var arrow = this.el.select('.arrow',true).first();
25904         //arrow.set(align[2], 
25905         
25906         this.el.addClass(placement);
25907         
25908         this.el.addClass('in fade');
25909         
25910         this.hoverState = null;
25911         
25912         if (this.el.hasClass('fade')) {
25913             // fade it?
25914         }
25915         
25916     },
25917     hide : function()
25918     {
25919          
25920         if (!this.el) {
25921             return;
25922         }
25923         //this.el.setXY([0,0]);
25924         this.el.removeClass('in');
25925         //this.el.hide();
25926         
25927     }
25928     
25929 });
25930  
25931
25932  /*
25933  * - LGPL
25934  *
25935  * Location Picker
25936  * 
25937  */
25938
25939 /**
25940  * @class Roo.bootstrap.LocationPicker
25941  * @extends Roo.bootstrap.Component
25942  * Bootstrap LocationPicker class
25943  * @cfg {Number} latitude Position when init default 0
25944  * @cfg {Number} longitude Position when init default 0
25945  * @cfg {Number} zoom default 15
25946  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25947  * @cfg {Boolean} mapTypeControl default false
25948  * @cfg {Boolean} disableDoubleClickZoom default false
25949  * @cfg {Boolean} scrollwheel default true
25950  * @cfg {Boolean} streetViewControl default false
25951  * @cfg {Number} radius default 0
25952  * @cfg {String} locationName
25953  * @cfg {Boolean} draggable default true
25954  * @cfg {Boolean} enableAutocomplete default false
25955  * @cfg {Boolean} enableReverseGeocode default true
25956  * @cfg {String} markerTitle
25957  * 
25958  * @constructor
25959  * Create a new LocationPicker
25960  * @param {Object} config The config object
25961  */
25962
25963
25964 Roo.bootstrap.LocationPicker = function(config){
25965     
25966     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25967     
25968     this.addEvents({
25969         /**
25970          * @event initial
25971          * Fires when the picker initialized.
25972          * @param {Roo.bootstrap.LocationPicker} this
25973          * @param {Google Location} location
25974          */
25975         initial : true,
25976         /**
25977          * @event positionchanged
25978          * Fires when the picker position changed.
25979          * @param {Roo.bootstrap.LocationPicker} this
25980          * @param {Google Location} location
25981          */
25982         positionchanged : true,
25983         /**
25984          * @event resize
25985          * Fires when the map resize.
25986          * @param {Roo.bootstrap.LocationPicker} this
25987          */
25988         resize : true,
25989         /**
25990          * @event show
25991          * Fires when the map show.
25992          * @param {Roo.bootstrap.LocationPicker} this
25993          */
25994         show : true,
25995         /**
25996          * @event hide
25997          * Fires when the map hide.
25998          * @param {Roo.bootstrap.LocationPicker} this
25999          */
26000         hide : true,
26001         /**
26002          * @event mapClick
26003          * Fires when click the map.
26004          * @param {Roo.bootstrap.LocationPicker} this
26005          * @param {Map event} e
26006          */
26007         mapClick : true,
26008         /**
26009          * @event mapRightClick
26010          * Fires when right click the map.
26011          * @param {Roo.bootstrap.LocationPicker} this
26012          * @param {Map event} e
26013          */
26014         mapRightClick : true,
26015         /**
26016          * @event markerClick
26017          * Fires when click the marker.
26018          * @param {Roo.bootstrap.LocationPicker} this
26019          * @param {Map event} e
26020          */
26021         markerClick : true,
26022         /**
26023          * @event markerRightClick
26024          * Fires when right click the marker.
26025          * @param {Roo.bootstrap.LocationPicker} this
26026          * @param {Map event} e
26027          */
26028         markerRightClick : true,
26029         /**
26030          * @event OverlayViewDraw
26031          * Fires when OverlayView Draw
26032          * @param {Roo.bootstrap.LocationPicker} this
26033          */
26034         OverlayViewDraw : true,
26035         /**
26036          * @event OverlayViewOnAdd
26037          * Fires when OverlayView Draw
26038          * @param {Roo.bootstrap.LocationPicker} this
26039          */
26040         OverlayViewOnAdd : true,
26041         /**
26042          * @event OverlayViewOnRemove
26043          * Fires when OverlayView Draw
26044          * @param {Roo.bootstrap.LocationPicker} this
26045          */
26046         OverlayViewOnRemove : true,
26047         /**
26048          * @event OverlayViewShow
26049          * Fires when OverlayView Draw
26050          * @param {Roo.bootstrap.LocationPicker} this
26051          * @param {Pixel} cpx
26052          */
26053         OverlayViewShow : true,
26054         /**
26055          * @event OverlayViewHide
26056          * Fires when OverlayView Draw
26057          * @param {Roo.bootstrap.LocationPicker} this
26058          */
26059         OverlayViewHide : true,
26060         /**
26061          * @event loadexception
26062          * Fires when load google lib failed.
26063          * @param {Roo.bootstrap.LocationPicker} this
26064          */
26065         loadexception : true
26066     });
26067         
26068 };
26069
26070 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26071     
26072     gMapContext: false,
26073     
26074     latitude: 0,
26075     longitude: 0,
26076     zoom: 15,
26077     mapTypeId: false,
26078     mapTypeControl: false,
26079     disableDoubleClickZoom: false,
26080     scrollwheel: true,
26081     streetViewControl: false,
26082     radius: 0,
26083     locationName: '',
26084     draggable: true,
26085     enableAutocomplete: false,
26086     enableReverseGeocode: true,
26087     markerTitle: '',
26088     
26089     getAutoCreate: function()
26090     {
26091
26092         var cfg = {
26093             tag: 'div',
26094             cls: 'roo-location-picker'
26095         };
26096         
26097         return cfg
26098     },
26099     
26100     initEvents: function(ct, position)
26101     {       
26102         if(!this.el.getWidth() || this.isApplied()){
26103             return;
26104         }
26105         
26106         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26107         
26108         this.initial();
26109     },
26110     
26111     initial: function()
26112     {
26113         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26114             this.fireEvent('loadexception', this);
26115             return;
26116         }
26117         
26118         if(!this.mapTypeId){
26119             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26120         }
26121         
26122         this.gMapContext = this.GMapContext();
26123         
26124         this.initOverlayView();
26125         
26126         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26127         
26128         var _this = this;
26129                 
26130         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26131             _this.setPosition(_this.gMapContext.marker.position);
26132         });
26133         
26134         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26135             _this.fireEvent('mapClick', this, event);
26136             
26137         });
26138
26139         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26140             _this.fireEvent('mapRightClick', this, event);
26141             
26142         });
26143         
26144         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26145             _this.fireEvent('markerClick', this, event);
26146             
26147         });
26148
26149         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26150             _this.fireEvent('markerRightClick', this, event);
26151             
26152         });
26153         
26154         this.setPosition(this.gMapContext.location);
26155         
26156         this.fireEvent('initial', this, this.gMapContext.location);
26157     },
26158     
26159     initOverlayView: function()
26160     {
26161         var _this = this;
26162         
26163         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26164             
26165             draw: function()
26166             {
26167                 _this.fireEvent('OverlayViewDraw', _this);
26168             },
26169             
26170             onAdd: function()
26171             {
26172                 _this.fireEvent('OverlayViewOnAdd', _this);
26173             },
26174             
26175             onRemove: function()
26176             {
26177                 _this.fireEvent('OverlayViewOnRemove', _this);
26178             },
26179             
26180             show: function(cpx)
26181             {
26182                 _this.fireEvent('OverlayViewShow', _this, cpx);
26183             },
26184             
26185             hide: function()
26186             {
26187                 _this.fireEvent('OverlayViewHide', _this);
26188             }
26189             
26190         });
26191     },
26192     
26193     fromLatLngToContainerPixel: function(event)
26194     {
26195         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26196     },
26197     
26198     isApplied: function() 
26199     {
26200         return this.getGmapContext() == false ? false : true;
26201     },
26202     
26203     getGmapContext: function() 
26204     {
26205         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26206     },
26207     
26208     GMapContext: function() 
26209     {
26210         var position = new google.maps.LatLng(this.latitude, this.longitude);
26211         
26212         var _map = new google.maps.Map(this.el.dom, {
26213             center: position,
26214             zoom: this.zoom,
26215             mapTypeId: this.mapTypeId,
26216             mapTypeControl: this.mapTypeControl,
26217             disableDoubleClickZoom: this.disableDoubleClickZoom,
26218             scrollwheel: this.scrollwheel,
26219             streetViewControl: this.streetViewControl,
26220             locationName: this.locationName,
26221             draggable: this.draggable,
26222             enableAutocomplete: this.enableAutocomplete,
26223             enableReverseGeocode: this.enableReverseGeocode
26224         });
26225         
26226         var _marker = new google.maps.Marker({
26227             position: position,
26228             map: _map,
26229             title: this.markerTitle,
26230             draggable: this.draggable
26231         });
26232         
26233         return {
26234             map: _map,
26235             marker: _marker,
26236             circle: null,
26237             location: position,
26238             radius: this.radius,
26239             locationName: this.locationName,
26240             addressComponents: {
26241                 formatted_address: null,
26242                 addressLine1: null,
26243                 addressLine2: null,
26244                 streetName: null,
26245                 streetNumber: null,
26246                 city: null,
26247                 district: null,
26248                 state: null,
26249                 stateOrProvince: null
26250             },
26251             settings: this,
26252             domContainer: this.el.dom,
26253             geodecoder: new google.maps.Geocoder()
26254         };
26255     },
26256     
26257     drawCircle: function(center, radius, options) 
26258     {
26259         if (this.gMapContext.circle != null) {
26260             this.gMapContext.circle.setMap(null);
26261         }
26262         if (radius > 0) {
26263             radius *= 1;
26264             options = Roo.apply({}, options, {
26265                 strokeColor: "#0000FF",
26266                 strokeOpacity: .35,
26267                 strokeWeight: 2,
26268                 fillColor: "#0000FF",
26269                 fillOpacity: .2
26270             });
26271             
26272             options.map = this.gMapContext.map;
26273             options.radius = radius;
26274             options.center = center;
26275             this.gMapContext.circle = new google.maps.Circle(options);
26276             return this.gMapContext.circle;
26277         }
26278         
26279         return null;
26280     },
26281     
26282     setPosition: function(location) 
26283     {
26284         this.gMapContext.location = location;
26285         this.gMapContext.marker.setPosition(location);
26286         this.gMapContext.map.panTo(location);
26287         this.drawCircle(location, this.gMapContext.radius, {});
26288         
26289         var _this = this;
26290         
26291         if (this.gMapContext.settings.enableReverseGeocode) {
26292             this.gMapContext.geodecoder.geocode({
26293                 latLng: this.gMapContext.location
26294             }, function(results, status) {
26295                 
26296                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26297                     _this.gMapContext.locationName = results[0].formatted_address;
26298                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26299                     
26300                     _this.fireEvent('positionchanged', this, location);
26301                 }
26302             });
26303             
26304             return;
26305         }
26306         
26307         this.fireEvent('positionchanged', this, location);
26308     },
26309     
26310     resize: function()
26311     {
26312         google.maps.event.trigger(this.gMapContext.map, "resize");
26313         
26314         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26315         
26316         this.fireEvent('resize', this);
26317     },
26318     
26319     setPositionByLatLng: function(latitude, longitude)
26320     {
26321         this.setPosition(new google.maps.LatLng(latitude, longitude));
26322     },
26323     
26324     getCurrentPosition: function() 
26325     {
26326         return {
26327             latitude: this.gMapContext.location.lat(),
26328             longitude: this.gMapContext.location.lng()
26329         };
26330     },
26331     
26332     getAddressName: function() 
26333     {
26334         return this.gMapContext.locationName;
26335     },
26336     
26337     getAddressComponents: function() 
26338     {
26339         return this.gMapContext.addressComponents;
26340     },
26341     
26342     address_component_from_google_geocode: function(address_components) 
26343     {
26344         var result = {};
26345         
26346         for (var i = 0; i < address_components.length; i++) {
26347             var component = address_components[i];
26348             if (component.types.indexOf("postal_code") >= 0) {
26349                 result.postalCode = component.short_name;
26350             } else if (component.types.indexOf("street_number") >= 0) {
26351                 result.streetNumber = component.short_name;
26352             } else if (component.types.indexOf("route") >= 0) {
26353                 result.streetName = component.short_name;
26354             } else if (component.types.indexOf("neighborhood") >= 0) {
26355                 result.city = component.short_name;
26356             } else if (component.types.indexOf("locality") >= 0) {
26357                 result.city = component.short_name;
26358             } else if (component.types.indexOf("sublocality") >= 0) {
26359                 result.district = component.short_name;
26360             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26361                 result.stateOrProvince = component.short_name;
26362             } else if (component.types.indexOf("country") >= 0) {
26363                 result.country = component.short_name;
26364             }
26365         }
26366         
26367         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26368         result.addressLine2 = "";
26369         return result;
26370     },
26371     
26372     setZoomLevel: function(zoom)
26373     {
26374         this.gMapContext.map.setZoom(zoom);
26375     },
26376     
26377     show: function()
26378     {
26379         if(!this.el){
26380             return;
26381         }
26382         
26383         this.el.show();
26384         
26385         this.resize();
26386         
26387         this.fireEvent('show', this);
26388     },
26389     
26390     hide: function()
26391     {
26392         if(!this.el){
26393             return;
26394         }
26395         
26396         this.el.hide();
26397         
26398         this.fireEvent('hide', this);
26399     }
26400     
26401 });
26402
26403 Roo.apply(Roo.bootstrap.LocationPicker, {
26404     
26405     OverlayView : function(map, options)
26406     {
26407         options = options || {};
26408         
26409         this.setMap(map);
26410     }
26411     
26412     
26413 });/*
26414  * - LGPL
26415  *
26416  * Alert
26417  * 
26418  */
26419
26420 /**
26421  * @class Roo.bootstrap.Alert
26422  * @extends Roo.bootstrap.Component
26423  * Bootstrap Alert class
26424  * @cfg {String} title The title of alert
26425  * @cfg {String} html The content of alert
26426  * @cfg {String} weight (  success | info | warning | danger )
26427  * @cfg {String} faicon font-awesomeicon
26428  * 
26429  * @constructor
26430  * Create a new alert
26431  * @param {Object} config The config object
26432  */
26433
26434
26435 Roo.bootstrap.Alert = function(config){
26436     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26437     
26438 };
26439
26440 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26441     
26442     title: '',
26443     html: '',
26444     weight: false,
26445     faicon: false,
26446     
26447     getAutoCreate : function()
26448     {
26449         
26450         var cfg = {
26451             tag : 'div',
26452             cls : 'alert',
26453             cn : [
26454                 {
26455                     tag : 'i',
26456                     cls : 'roo-alert-icon'
26457                     
26458                 },
26459                 {
26460                     tag : 'b',
26461                     cls : 'roo-alert-title',
26462                     html : this.title
26463                 },
26464                 {
26465                     tag : 'span',
26466                     cls : 'roo-alert-text',
26467                     html : this.html
26468                 }
26469             ]
26470         };
26471         
26472         if(this.faicon){
26473             cfg.cn[0].cls += ' fa ' + this.faicon;
26474         }
26475         
26476         if(this.weight){
26477             cfg.cls += ' alert-' + this.weight;
26478         }
26479         
26480         return cfg;
26481     },
26482     
26483     initEvents: function() 
26484     {
26485         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26486     },
26487     
26488     setTitle : function(str)
26489     {
26490         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26491     },
26492     
26493     setText : function(str)
26494     {
26495         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26496     },
26497     
26498     setWeight : function(weight)
26499     {
26500         if(this.weight){
26501             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26502         }
26503         
26504         this.weight = weight;
26505         
26506         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26507     },
26508     
26509     setIcon : function(icon)
26510     {
26511         if(this.faicon){
26512             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26513         }
26514         
26515         this.faicon = icon;
26516         
26517         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26518     },
26519     
26520     hide: function() 
26521     {
26522         this.el.hide();   
26523     },
26524     
26525     show: function() 
26526     {  
26527         this.el.show();   
26528     }
26529     
26530 });
26531
26532  
26533 /*
26534 * Licence: LGPL
26535 */
26536
26537 /**
26538  * @class Roo.bootstrap.UploadCropbox
26539  * @extends Roo.bootstrap.Component
26540  * Bootstrap UploadCropbox class
26541  * @cfg {String} emptyText show when image has been loaded
26542  * @cfg {String} rotateNotify show when image too small to rotate
26543  * @cfg {Number} errorTimeout default 3000
26544  * @cfg {Number} minWidth default 300
26545  * @cfg {Number} minHeight default 300
26546  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26547  * @cfg {Boolean} isDocument (true|false) default false
26548  * @cfg {String} url action url
26549  * @cfg {String} paramName default 'imageUpload'
26550  * @cfg {String} method default POST
26551  * @cfg {Boolean} loadMask (true|false) default true
26552  * @cfg {Boolean} loadingText default 'Loading...'
26553  * 
26554  * @constructor
26555  * Create a new UploadCropbox
26556  * @param {Object} config The config object
26557  */
26558
26559 Roo.bootstrap.UploadCropbox = function(config){
26560     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26561     
26562     this.addEvents({
26563         /**
26564          * @event beforeselectfile
26565          * Fire before select file
26566          * @param {Roo.bootstrap.UploadCropbox} this
26567          */
26568         "beforeselectfile" : true,
26569         /**
26570          * @event initial
26571          * Fire after initEvent
26572          * @param {Roo.bootstrap.UploadCropbox} this
26573          */
26574         "initial" : true,
26575         /**
26576          * @event crop
26577          * Fire after initEvent
26578          * @param {Roo.bootstrap.UploadCropbox} this
26579          * @param {String} data
26580          */
26581         "crop" : true,
26582         /**
26583          * @event prepare
26584          * Fire when preparing the file data
26585          * @param {Roo.bootstrap.UploadCropbox} this
26586          * @param {Object} file
26587          */
26588         "prepare" : true,
26589         /**
26590          * @event exception
26591          * Fire when get exception
26592          * @param {Roo.bootstrap.UploadCropbox} this
26593          * @param {XMLHttpRequest} xhr
26594          */
26595         "exception" : true,
26596         /**
26597          * @event beforeloadcanvas
26598          * Fire before load the canvas
26599          * @param {Roo.bootstrap.UploadCropbox} this
26600          * @param {String} src
26601          */
26602         "beforeloadcanvas" : true,
26603         /**
26604          * @event trash
26605          * Fire when trash image
26606          * @param {Roo.bootstrap.UploadCropbox} this
26607          */
26608         "trash" : true,
26609         /**
26610          * @event download
26611          * Fire when download the image
26612          * @param {Roo.bootstrap.UploadCropbox} this
26613          */
26614         "download" : true,
26615         /**
26616          * @event footerbuttonclick
26617          * Fire when footerbuttonclick
26618          * @param {Roo.bootstrap.UploadCropbox} this
26619          * @param {String} type
26620          */
26621         "footerbuttonclick" : true,
26622         /**
26623          * @event resize
26624          * Fire when resize
26625          * @param {Roo.bootstrap.UploadCropbox} this
26626          */
26627         "resize" : true,
26628         /**
26629          * @event rotate
26630          * Fire when rotate the image
26631          * @param {Roo.bootstrap.UploadCropbox} this
26632          * @param {String} pos
26633          */
26634         "rotate" : true,
26635         /**
26636          * @event inspect
26637          * Fire when inspect the file
26638          * @param {Roo.bootstrap.UploadCropbox} this
26639          * @param {Object} file
26640          */
26641         "inspect" : true,
26642         /**
26643          * @event upload
26644          * Fire when xhr upload the file
26645          * @param {Roo.bootstrap.UploadCropbox} this
26646          * @param {Object} data
26647          */
26648         "upload" : true,
26649         /**
26650          * @event arrange
26651          * Fire when arrange the file data
26652          * @param {Roo.bootstrap.UploadCropbox} this
26653          * @param {Object} formData
26654          */
26655         "arrange" : true
26656     });
26657     
26658     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26659 };
26660
26661 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26662     
26663     emptyText : 'Click to upload image',
26664     rotateNotify : 'Image is too small to rotate',
26665     errorTimeout : 3000,
26666     scale : 0,
26667     baseScale : 1,
26668     rotate : 0,
26669     dragable : false,
26670     pinching : false,
26671     mouseX : 0,
26672     mouseY : 0,
26673     cropData : false,
26674     minWidth : 300,
26675     minHeight : 300,
26676     file : false,
26677     exif : {},
26678     baseRotate : 1,
26679     cropType : 'image/jpeg',
26680     buttons : false,
26681     canvasLoaded : false,
26682     isDocument : false,
26683     method : 'POST',
26684     paramName : 'imageUpload',
26685     loadMask : true,
26686     loadingText : 'Loading...',
26687     maskEl : false,
26688     
26689     getAutoCreate : function()
26690     {
26691         var cfg = {
26692             tag : 'div',
26693             cls : 'roo-upload-cropbox',
26694             cn : [
26695                 {
26696                     tag : 'input',
26697                     cls : 'roo-upload-cropbox-selector',
26698                     type : 'file'
26699                 },
26700                 {
26701                     tag : 'div',
26702                     cls : 'roo-upload-cropbox-body',
26703                     style : 'cursor:pointer',
26704                     cn : [
26705                         {
26706                             tag : 'div',
26707                             cls : 'roo-upload-cropbox-preview'
26708                         },
26709                         {
26710                             tag : 'div',
26711                             cls : 'roo-upload-cropbox-thumb'
26712                         },
26713                         {
26714                             tag : 'div',
26715                             cls : 'roo-upload-cropbox-empty-notify',
26716                             html : this.emptyText
26717                         },
26718                         {
26719                             tag : 'div',
26720                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26721                             html : this.rotateNotify
26722                         }
26723                     ]
26724                 },
26725                 {
26726                     tag : 'div',
26727                     cls : 'roo-upload-cropbox-footer',
26728                     cn : {
26729                         tag : 'div',
26730                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26731                         cn : []
26732                     }
26733                 }
26734             ]
26735         };
26736         
26737         return cfg;
26738     },
26739     
26740     onRender : function(ct, position)
26741     {
26742         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26743         
26744         if (this.buttons.length) {
26745             
26746             Roo.each(this.buttons, function(bb) {
26747                 
26748                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26749                 
26750                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26751                 
26752             }, this);
26753         }
26754         
26755         if(this.loadMask){
26756             this.maskEl = this.el;
26757         }
26758     },
26759     
26760     initEvents : function()
26761     {
26762         this.urlAPI = (window.createObjectURL && window) || 
26763                                 (window.URL && URL.revokeObjectURL && URL) || 
26764                                 (window.webkitURL && webkitURL);
26765                         
26766         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26767         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26768         
26769         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26770         this.selectorEl.hide();
26771         
26772         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26773         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26774         
26775         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26776         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26777         this.thumbEl.hide();
26778         
26779         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26780         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26781         
26782         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26783         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26784         this.errorEl.hide();
26785         
26786         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26787         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26788         this.footerEl.hide();
26789         
26790         this.setThumbBoxSize();
26791         
26792         this.bind();
26793         
26794         this.resize();
26795         
26796         this.fireEvent('initial', this);
26797     },
26798
26799     bind : function()
26800     {
26801         var _this = this;
26802         
26803         window.addEventListener("resize", function() { _this.resize(); } );
26804         
26805         this.bodyEl.on('click', this.beforeSelectFile, this);
26806         
26807         if(Roo.isTouch){
26808             this.bodyEl.on('touchstart', this.onTouchStart, this);
26809             this.bodyEl.on('touchmove', this.onTouchMove, this);
26810             this.bodyEl.on('touchend', this.onTouchEnd, this);
26811         }
26812         
26813         if(!Roo.isTouch){
26814             this.bodyEl.on('mousedown', this.onMouseDown, this);
26815             this.bodyEl.on('mousemove', this.onMouseMove, this);
26816             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26817             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26818             Roo.get(document).on('mouseup', this.onMouseUp, this);
26819         }
26820         
26821         this.selectorEl.on('change', this.onFileSelected, this);
26822     },
26823     
26824     reset : function()
26825     {    
26826         this.scale = 0;
26827         this.baseScale = 1;
26828         this.rotate = 0;
26829         this.baseRotate = 1;
26830         this.dragable = false;
26831         this.pinching = false;
26832         this.mouseX = 0;
26833         this.mouseY = 0;
26834         this.cropData = false;
26835         this.notifyEl.dom.innerHTML = this.emptyText;
26836         
26837         this.selectorEl.dom.value = '';
26838         
26839     },
26840     
26841     resize : function()
26842     {
26843         if(this.fireEvent('resize', this) != false){
26844             this.setThumbBoxPosition();
26845             this.setCanvasPosition();
26846         }
26847     },
26848     
26849     onFooterButtonClick : function(e, el, o, type)
26850     {
26851         switch (type) {
26852             case 'rotate-left' :
26853                 this.onRotateLeft(e);
26854                 break;
26855             case 'rotate-right' :
26856                 this.onRotateRight(e);
26857                 break;
26858             case 'picture' :
26859                 this.beforeSelectFile(e);
26860                 break;
26861             case 'trash' :
26862                 this.trash(e);
26863                 break;
26864             case 'crop' :
26865                 this.crop(e);
26866                 break;
26867             case 'download' :
26868                 this.download(e);
26869                 break;
26870             default :
26871                 break;
26872         }
26873         
26874         this.fireEvent('footerbuttonclick', this, type);
26875     },
26876     
26877     beforeSelectFile : function(e)
26878     {
26879         e.preventDefault();
26880         
26881         if(this.fireEvent('beforeselectfile', this) != false){
26882             this.selectorEl.dom.click();
26883         }
26884     },
26885     
26886     onFileSelected : function(e)
26887     {
26888         e.preventDefault();
26889         
26890         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26891             return;
26892         }
26893         
26894         var file = this.selectorEl.dom.files[0];
26895         
26896         if(this.fireEvent('inspect', this, file) != false){
26897             this.prepare(file);
26898         }
26899         
26900     },
26901     
26902     trash : function(e)
26903     {
26904         this.fireEvent('trash', this);
26905     },
26906     
26907     download : function(e)
26908     {
26909         this.fireEvent('download', this);
26910     },
26911     
26912     loadCanvas : function(src)
26913     {   
26914         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26915             
26916             this.reset();
26917             
26918             this.imageEl = document.createElement('img');
26919             
26920             var _this = this;
26921             
26922             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26923             
26924             this.imageEl.src = src;
26925         }
26926     },
26927     
26928     onLoadCanvas : function()
26929     {   
26930         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26931         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26932         
26933         this.bodyEl.un('click', this.beforeSelectFile, this);
26934         
26935         this.notifyEl.hide();
26936         this.thumbEl.show();
26937         this.footerEl.show();
26938         
26939         this.baseRotateLevel();
26940         
26941         if(this.isDocument){
26942             this.setThumbBoxSize();
26943         }
26944         
26945         this.setThumbBoxPosition();
26946         
26947         this.baseScaleLevel();
26948         
26949         this.draw();
26950         
26951         this.resize();
26952         
26953         this.canvasLoaded = true;
26954         
26955         if(this.loadMask){
26956             this.maskEl.unmask();
26957         }
26958         
26959     },
26960     
26961     setCanvasPosition : function()
26962     {   
26963         if(!this.canvasEl){
26964             return;
26965         }
26966         
26967         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26968         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26969         
26970         this.previewEl.setLeft(pw);
26971         this.previewEl.setTop(ph);
26972         
26973     },
26974     
26975     onMouseDown : function(e)
26976     {   
26977         e.stopEvent();
26978         
26979         this.dragable = true;
26980         this.pinching = false;
26981         
26982         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26983             this.dragable = false;
26984             return;
26985         }
26986         
26987         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26988         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26989         
26990     },
26991     
26992     onMouseMove : function(e)
26993     {   
26994         e.stopEvent();
26995         
26996         if(!this.canvasLoaded){
26997             return;
26998         }
26999         
27000         if (!this.dragable){
27001             return;
27002         }
27003         
27004         var minX = Math.ceil(this.thumbEl.getLeft(true));
27005         var minY = Math.ceil(this.thumbEl.getTop(true));
27006         
27007         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27008         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27009         
27010         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27011         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27012         
27013         x = x - this.mouseX;
27014         y = y - this.mouseY;
27015         
27016         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27017         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27018         
27019         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27020         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27021         
27022         this.previewEl.setLeft(bgX);
27023         this.previewEl.setTop(bgY);
27024         
27025         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27026         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27027     },
27028     
27029     onMouseUp : function(e)
27030     {   
27031         e.stopEvent();
27032         
27033         this.dragable = false;
27034     },
27035     
27036     onMouseWheel : function(e)
27037     {   
27038         e.stopEvent();
27039         
27040         this.startScale = this.scale;
27041         
27042         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27043         
27044         if(!this.zoomable()){
27045             this.scale = this.startScale;
27046             return;
27047         }
27048         
27049         this.draw();
27050         
27051         return;
27052     },
27053     
27054     zoomable : function()
27055     {
27056         var minScale = this.thumbEl.getWidth() / this.minWidth;
27057         
27058         if(this.minWidth < this.minHeight){
27059             minScale = this.thumbEl.getHeight() / this.minHeight;
27060         }
27061         
27062         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27063         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27064         
27065         if(
27066                 this.isDocument &&
27067                 (this.rotate == 0 || this.rotate == 180) && 
27068                 (
27069                     width > this.imageEl.OriginWidth || 
27070                     height > this.imageEl.OriginHeight ||
27071                     (width < this.minWidth && height < this.minHeight)
27072                 )
27073         ){
27074             return false;
27075         }
27076         
27077         if(
27078                 this.isDocument &&
27079                 (this.rotate == 90 || this.rotate == 270) && 
27080                 (
27081                     width > this.imageEl.OriginWidth || 
27082                     height > this.imageEl.OriginHeight ||
27083                     (width < this.minHeight && height < this.minWidth)
27084                 )
27085         ){
27086             return false;
27087         }
27088         
27089         if(
27090                 !this.isDocument &&
27091                 (this.rotate == 0 || this.rotate == 180) && 
27092                 (
27093                     width < this.minWidth || 
27094                     width > this.imageEl.OriginWidth || 
27095                     height < this.minHeight || 
27096                     height > this.imageEl.OriginHeight
27097                 )
27098         ){
27099             return false;
27100         }
27101         
27102         if(
27103                 !this.isDocument &&
27104                 (this.rotate == 90 || this.rotate == 270) && 
27105                 (
27106                     width < this.minHeight || 
27107                     width > this.imageEl.OriginWidth || 
27108                     height < this.minWidth || 
27109                     height > this.imageEl.OriginHeight
27110                 )
27111         ){
27112             return false;
27113         }
27114         
27115         return true;
27116         
27117     },
27118     
27119     onRotateLeft : function(e)
27120     {   
27121         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27122             
27123             var minScale = this.thumbEl.getWidth() / this.minWidth;
27124             
27125             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27126             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27127             
27128             this.startScale = this.scale;
27129             
27130             while (this.getScaleLevel() < minScale){
27131             
27132                 this.scale = this.scale + 1;
27133                 
27134                 if(!this.zoomable()){
27135                     break;
27136                 }
27137                 
27138                 if(
27139                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27140                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27141                 ){
27142                     continue;
27143                 }
27144                 
27145                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27146
27147                 this.draw();
27148                 
27149                 return;
27150             }
27151             
27152             this.scale = this.startScale;
27153             
27154             this.onRotateFail();
27155             
27156             return false;
27157         }
27158         
27159         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27160
27161         if(this.isDocument){
27162             this.setThumbBoxSize();
27163             this.setThumbBoxPosition();
27164             this.setCanvasPosition();
27165         }
27166         
27167         this.draw();
27168         
27169         this.fireEvent('rotate', this, 'left');
27170         
27171     },
27172     
27173     onRotateRight : function(e)
27174     {
27175         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27176             
27177             var minScale = this.thumbEl.getWidth() / this.minWidth;
27178         
27179             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27180             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27181             
27182             this.startScale = this.scale;
27183             
27184             while (this.getScaleLevel() < minScale){
27185             
27186                 this.scale = this.scale + 1;
27187                 
27188                 if(!this.zoomable()){
27189                     break;
27190                 }
27191                 
27192                 if(
27193                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27194                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27195                 ){
27196                     continue;
27197                 }
27198                 
27199                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27200
27201                 this.draw();
27202                 
27203                 return;
27204             }
27205             
27206             this.scale = this.startScale;
27207             
27208             this.onRotateFail();
27209             
27210             return false;
27211         }
27212         
27213         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27214
27215         if(this.isDocument){
27216             this.setThumbBoxSize();
27217             this.setThumbBoxPosition();
27218             this.setCanvasPosition();
27219         }
27220         
27221         this.draw();
27222         
27223         this.fireEvent('rotate', this, 'right');
27224     },
27225     
27226     onRotateFail : function()
27227     {
27228         this.errorEl.show(true);
27229         
27230         var _this = this;
27231         
27232         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27233     },
27234     
27235     draw : function()
27236     {
27237         this.previewEl.dom.innerHTML = '';
27238         
27239         var canvasEl = document.createElement("canvas");
27240         
27241         var contextEl = canvasEl.getContext("2d");
27242         
27243         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27244         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27245         var center = this.imageEl.OriginWidth / 2;
27246         
27247         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27248             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27249             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27250             center = this.imageEl.OriginHeight / 2;
27251         }
27252         
27253         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27254         
27255         contextEl.translate(center, center);
27256         contextEl.rotate(this.rotate * Math.PI / 180);
27257
27258         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27259         
27260         this.canvasEl = document.createElement("canvas");
27261         
27262         this.contextEl = this.canvasEl.getContext("2d");
27263         
27264         switch (this.rotate) {
27265             case 0 :
27266                 
27267                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27268                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27269                 
27270                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27271                 
27272                 break;
27273             case 90 : 
27274                 
27275                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27276                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27277                 
27278                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27279                     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);
27280                     break;
27281                 }
27282                 
27283                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27284                 
27285                 break;
27286             case 180 :
27287                 
27288                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27289                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27290                 
27291                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27292                     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);
27293                     break;
27294                 }
27295                 
27296                 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);
27297                 
27298                 break;
27299             case 270 :
27300                 
27301                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27302                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27303         
27304                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27305                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27306                     break;
27307                 }
27308                 
27309                 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);
27310                 
27311                 break;
27312             default : 
27313                 break;
27314         }
27315         
27316         this.previewEl.appendChild(this.canvasEl);
27317         
27318         this.setCanvasPosition();
27319     },
27320     
27321     crop : function()
27322     {
27323         if(!this.canvasLoaded){
27324             return;
27325         }
27326         
27327         var imageCanvas = document.createElement("canvas");
27328         
27329         var imageContext = imageCanvas.getContext("2d");
27330         
27331         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27332         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27333         
27334         var center = imageCanvas.width / 2;
27335         
27336         imageContext.translate(center, center);
27337         
27338         imageContext.rotate(this.rotate * Math.PI / 180);
27339         
27340         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27341         
27342         var canvas = document.createElement("canvas");
27343         
27344         var context = canvas.getContext("2d");
27345                 
27346         canvas.width = this.minWidth;
27347         canvas.height = this.minHeight;
27348
27349         switch (this.rotate) {
27350             case 0 :
27351                 
27352                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27353                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27354                 
27355                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27356                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27357                 
27358                 var targetWidth = this.minWidth - 2 * x;
27359                 var targetHeight = this.minHeight - 2 * y;
27360                 
27361                 var scale = 1;
27362                 
27363                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27364                     scale = targetWidth / width;
27365                 }
27366                 
27367                 if(x > 0 && y == 0){
27368                     scale = targetHeight / height;
27369                 }
27370                 
27371                 if(x > 0 && y > 0){
27372                     scale = targetWidth / width;
27373                     
27374                     if(width < height){
27375                         scale = targetHeight / height;
27376                     }
27377                 }
27378                 
27379                 context.scale(scale, scale);
27380                 
27381                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27382                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27383
27384                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27385                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27386
27387                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27388                 
27389                 break;
27390             case 90 : 
27391                 
27392                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27393                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27394                 
27395                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27396                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27397                 
27398                 var targetWidth = this.minWidth - 2 * x;
27399                 var targetHeight = this.minHeight - 2 * y;
27400                 
27401                 var scale = 1;
27402                 
27403                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27404                     scale = targetWidth / width;
27405                 }
27406                 
27407                 if(x > 0 && y == 0){
27408                     scale = targetHeight / height;
27409                 }
27410                 
27411                 if(x > 0 && y > 0){
27412                     scale = targetWidth / width;
27413                     
27414                     if(width < height){
27415                         scale = targetHeight / height;
27416                     }
27417                 }
27418                 
27419                 context.scale(scale, scale);
27420                 
27421                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27422                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27423
27424                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27425                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27426                 
27427                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27428                 
27429                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27430                 
27431                 break;
27432             case 180 :
27433                 
27434                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27435                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27436                 
27437                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27438                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27439                 
27440                 var targetWidth = this.minWidth - 2 * x;
27441                 var targetHeight = this.minHeight - 2 * y;
27442                 
27443                 var scale = 1;
27444                 
27445                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27446                     scale = targetWidth / width;
27447                 }
27448                 
27449                 if(x > 0 && y == 0){
27450                     scale = targetHeight / height;
27451                 }
27452                 
27453                 if(x > 0 && y > 0){
27454                     scale = targetWidth / width;
27455                     
27456                     if(width < height){
27457                         scale = targetHeight / height;
27458                     }
27459                 }
27460                 
27461                 context.scale(scale, scale);
27462                 
27463                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27464                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27465
27466                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27467                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27468
27469                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27470                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27471                 
27472                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27473                 
27474                 break;
27475             case 270 :
27476                 
27477                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27478                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27479                 
27480                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27481                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27482                 
27483                 var targetWidth = this.minWidth - 2 * x;
27484                 var targetHeight = this.minHeight - 2 * y;
27485                 
27486                 var scale = 1;
27487                 
27488                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27489                     scale = targetWidth / width;
27490                 }
27491                 
27492                 if(x > 0 && y == 0){
27493                     scale = targetHeight / height;
27494                 }
27495                 
27496                 if(x > 0 && y > 0){
27497                     scale = targetWidth / width;
27498                     
27499                     if(width < height){
27500                         scale = targetHeight / height;
27501                     }
27502                 }
27503                 
27504                 context.scale(scale, scale);
27505                 
27506                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27507                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27508
27509                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27510                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27511                 
27512                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27513                 
27514                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27515                 
27516                 break;
27517             default : 
27518                 break;
27519         }
27520         
27521         this.cropData = canvas.toDataURL(this.cropType);
27522         
27523         if(this.fireEvent('crop', this, this.cropData) !== false){
27524             this.process(this.file, this.cropData);
27525         }
27526         
27527         return;
27528         
27529     },
27530     
27531     setThumbBoxSize : function()
27532     {
27533         var width, height;
27534         
27535         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27536             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27537             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27538             
27539             this.minWidth = width;
27540             this.minHeight = height;
27541             
27542             if(this.rotate == 90 || this.rotate == 270){
27543                 this.minWidth = height;
27544                 this.minHeight = width;
27545             }
27546         }
27547         
27548         height = 300;
27549         width = Math.ceil(this.minWidth * height / this.minHeight);
27550         
27551         if(this.minWidth > this.minHeight){
27552             width = 300;
27553             height = Math.ceil(this.minHeight * width / this.minWidth);
27554         }
27555         
27556         this.thumbEl.setStyle({
27557             width : width + 'px',
27558             height : height + 'px'
27559         });
27560
27561         return;
27562             
27563     },
27564     
27565     setThumbBoxPosition : function()
27566     {
27567         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27568         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27569         
27570         this.thumbEl.setLeft(x);
27571         this.thumbEl.setTop(y);
27572         
27573     },
27574     
27575     baseRotateLevel : function()
27576     {
27577         this.baseRotate = 1;
27578         
27579         if(
27580                 typeof(this.exif) != 'undefined' &&
27581                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27582                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27583         ){
27584             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27585         }
27586         
27587         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27588         
27589     },
27590     
27591     baseScaleLevel : function()
27592     {
27593         var width, height;
27594         
27595         if(this.isDocument){
27596             
27597             if(this.baseRotate == 6 || this.baseRotate == 8){
27598             
27599                 height = this.thumbEl.getHeight();
27600                 this.baseScale = height / this.imageEl.OriginWidth;
27601
27602                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27603                     width = this.thumbEl.getWidth();
27604                     this.baseScale = width / this.imageEl.OriginHeight;
27605                 }
27606
27607                 return;
27608             }
27609
27610             height = this.thumbEl.getHeight();
27611             this.baseScale = height / this.imageEl.OriginHeight;
27612
27613             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27614                 width = this.thumbEl.getWidth();
27615                 this.baseScale = width / this.imageEl.OriginWidth;
27616             }
27617
27618             return;
27619         }
27620         
27621         if(this.baseRotate == 6 || this.baseRotate == 8){
27622             
27623             width = this.thumbEl.getHeight();
27624             this.baseScale = width / this.imageEl.OriginHeight;
27625             
27626             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27627                 height = this.thumbEl.getWidth();
27628                 this.baseScale = height / this.imageEl.OriginHeight;
27629             }
27630             
27631             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27632                 height = this.thumbEl.getWidth();
27633                 this.baseScale = height / this.imageEl.OriginHeight;
27634                 
27635                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27636                     width = this.thumbEl.getHeight();
27637                     this.baseScale = width / this.imageEl.OriginWidth;
27638                 }
27639             }
27640             
27641             return;
27642         }
27643         
27644         width = this.thumbEl.getWidth();
27645         this.baseScale = width / this.imageEl.OriginWidth;
27646         
27647         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27648             height = this.thumbEl.getHeight();
27649             this.baseScale = height / this.imageEl.OriginHeight;
27650         }
27651         
27652         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27653             
27654             height = this.thumbEl.getHeight();
27655             this.baseScale = height / this.imageEl.OriginHeight;
27656             
27657             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27658                 width = this.thumbEl.getWidth();
27659                 this.baseScale = width / this.imageEl.OriginWidth;
27660             }
27661             
27662         }
27663         
27664         return;
27665     },
27666     
27667     getScaleLevel : function()
27668     {
27669         return this.baseScale * Math.pow(1.1, this.scale);
27670     },
27671     
27672     onTouchStart : function(e)
27673     {
27674         if(!this.canvasLoaded){
27675             this.beforeSelectFile(e);
27676             return;
27677         }
27678         
27679         var touches = e.browserEvent.touches;
27680         
27681         if(!touches){
27682             return;
27683         }
27684         
27685         if(touches.length == 1){
27686             this.onMouseDown(e);
27687             return;
27688         }
27689         
27690         if(touches.length != 2){
27691             return;
27692         }
27693         
27694         var coords = [];
27695         
27696         for(var i = 0, finger; finger = touches[i]; i++){
27697             coords.push(finger.pageX, finger.pageY);
27698         }
27699         
27700         var x = Math.pow(coords[0] - coords[2], 2);
27701         var y = Math.pow(coords[1] - coords[3], 2);
27702         
27703         this.startDistance = Math.sqrt(x + y);
27704         
27705         this.startScale = this.scale;
27706         
27707         this.pinching = true;
27708         this.dragable = false;
27709         
27710     },
27711     
27712     onTouchMove : function(e)
27713     {
27714         if(!this.pinching && !this.dragable){
27715             return;
27716         }
27717         
27718         var touches = e.browserEvent.touches;
27719         
27720         if(!touches){
27721             return;
27722         }
27723         
27724         if(this.dragable){
27725             this.onMouseMove(e);
27726             return;
27727         }
27728         
27729         var coords = [];
27730         
27731         for(var i = 0, finger; finger = touches[i]; i++){
27732             coords.push(finger.pageX, finger.pageY);
27733         }
27734         
27735         var x = Math.pow(coords[0] - coords[2], 2);
27736         var y = Math.pow(coords[1] - coords[3], 2);
27737         
27738         this.endDistance = Math.sqrt(x + y);
27739         
27740         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27741         
27742         if(!this.zoomable()){
27743             this.scale = this.startScale;
27744             return;
27745         }
27746         
27747         this.draw();
27748         
27749     },
27750     
27751     onTouchEnd : function(e)
27752     {
27753         this.pinching = false;
27754         this.dragable = false;
27755         
27756     },
27757     
27758     process : function(file, crop)
27759     {
27760         if(this.loadMask){
27761             this.maskEl.mask(this.loadingText);
27762         }
27763         
27764         this.xhr = new XMLHttpRequest();
27765         
27766         file.xhr = this.xhr;
27767
27768         this.xhr.open(this.method, this.url, true);
27769         
27770         var headers = {
27771             "Accept": "application/json",
27772             "Cache-Control": "no-cache",
27773             "X-Requested-With": "XMLHttpRequest"
27774         };
27775         
27776         for (var headerName in headers) {
27777             var headerValue = headers[headerName];
27778             if (headerValue) {
27779                 this.xhr.setRequestHeader(headerName, headerValue);
27780             }
27781         }
27782         
27783         var _this = this;
27784         
27785         this.xhr.onload = function()
27786         {
27787             _this.xhrOnLoad(_this.xhr);
27788         }
27789         
27790         this.xhr.onerror = function()
27791         {
27792             _this.xhrOnError(_this.xhr);
27793         }
27794         
27795         var formData = new FormData();
27796
27797         formData.append('returnHTML', 'NO');
27798         
27799         if(crop){
27800             formData.append('crop', crop);
27801         }
27802         
27803         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27804             formData.append(this.paramName, file, file.name);
27805         }
27806         
27807         if(typeof(file.filename) != 'undefined'){
27808             formData.append('filename', file.filename);
27809         }
27810         
27811         if(typeof(file.mimetype) != 'undefined'){
27812             formData.append('mimetype', file.mimetype);
27813         }
27814         
27815         if(this.fireEvent('arrange', this, formData) != false){
27816             this.xhr.send(formData);
27817         };
27818     },
27819     
27820     xhrOnLoad : function(xhr)
27821     {
27822         if(this.loadMask){
27823             this.maskEl.unmask();
27824         }
27825         
27826         if (xhr.readyState !== 4) {
27827             this.fireEvent('exception', this, xhr);
27828             return;
27829         }
27830
27831         var response = Roo.decode(xhr.responseText);
27832         
27833         if(!response.success){
27834             this.fireEvent('exception', this, xhr);
27835             return;
27836         }
27837         
27838         var response = Roo.decode(xhr.responseText);
27839         
27840         this.fireEvent('upload', this, response);
27841         
27842     },
27843     
27844     xhrOnError : function()
27845     {
27846         if(this.loadMask){
27847             this.maskEl.unmask();
27848         }
27849         
27850         Roo.log('xhr on error');
27851         
27852         var response = Roo.decode(xhr.responseText);
27853           
27854         Roo.log(response);
27855         
27856     },
27857     
27858     prepare : function(file)
27859     {   
27860         if(this.loadMask){
27861             this.maskEl.mask(this.loadingText);
27862         }
27863         
27864         this.file = false;
27865         this.exif = {};
27866         
27867         if(typeof(file) === 'string'){
27868             this.loadCanvas(file);
27869             return;
27870         }
27871         
27872         if(!file || !this.urlAPI){
27873             return;
27874         }
27875         
27876         this.file = file;
27877         this.cropType = file.type;
27878         
27879         var _this = this;
27880         
27881         if(this.fireEvent('prepare', this, this.file) != false){
27882             
27883             var reader = new FileReader();
27884             
27885             reader.onload = function (e) {
27886                 if (e.target.error) {
27887                     Roo.log(e.target.error);
27888                     return;
27889                 }
27890                 
27891                 var buffer = e.target.result,
27892                     dataView = new DataView(buffer),
27893                     offset = 2,
27894                     maxOffset = dataView.byteLength - 4,
27895                     markerBytes,
27896                     markerLength;
27897                 
27898                 if (dataView.getUint16(0) === 0xffd8) {
27899                     while (offset < maxOffset) {
27900                         markerBytes = dataView.getUint16(offset);
27901                         
27902                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27903                             markerLength = dataView.getUint16(offset + 2) + 2;
27904                             if (offset + markerLength > dataView.byteLength) {
27905                                 Roo.log('Invalid meta data: Invalid segment size.');
27906                                 break;
27907                             }
27908                             
27909                             if(markerBytes == 0xffe1){
27910                                 _this.parseExifData(
27911                                     dataView,
27912                                     offset,
27913                                     markerLength
27914                                 );
27915                             }
27916                             
27917                             offset += markerLength;
27918                             
27919                             continue;
27920                         }
27921                         
27922                         break;
27923                     }
27924                     
27925                 }
27926                 
27927                 var url = _this.urlAPI.createObjectURL(_this.file);
27928                 
27929                 _this.loadCanvas(url);
27930                 
27931                 return;
27932             }
27933             
27934             reader.readAsArrayBuffer(this.file);
27935             
27936         }
27937         
27938     },
27939     
27940     parseExifData : function(dataView, offset, length)
27941     {
27942         var tiffOffset = offset + 10,
27943             littleEndian,
27944             dirOffset;
27945     
27946         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27947             // No Exif data, might be XMP data instead
27948             return;
27949         }
27950         
27951         // Check for the ASCII code for "Exif" (0x45786966):
27952         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27953             // No Exif data, might be XMP data instead
27954             return;
27955         }
27956         if (tiffOffset + 8 > dataView.byteLength) {
27957             Roo.log('Invalid Exif data: Invalid segment size.');
27958             return;
27959         }
27960         // Check for the two null bytes:
27961         if (dataView.getUint16(offset + 8) !== 0x0000) {
27962             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27963             return;
27964         }
27965         // Check the byte alignment:
27966         switch (dataView.getUint16(tiffOffset)) {
27967         case 0x4949:
27968             littleEndian = true;
27969             break;
27970         case 0x4D4D:
27971             littleEndian = false;
27972             break;
27973         default:
27974             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27975             return;
27976         }
27977         // Check for the TIFF tag marker (0x002A):
27978         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27979             Roo.log('Invalid Exif data: Missing TIFF marker.');
27980             return;
27981         }
27982         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27983         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27984         
27985         this.parseExifTags(
27986             dataView,
27987             tiffOffset,
27988             tiffOffset + dirOffset,
27989             littleEndian
27990         );
27991     },
27992     
27993     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27994     {
27995         var tagsNumber,
27996             dirEndOffset,
27997             i;
27998         if (dirOffset + 6 > dataView.byteLength) {
27999             Roo.log('Invalid Exif data: Invalid directory offset.');
28000             return;
28001         }
28002         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28003         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28004         if (dirEndOffset + 4 > dataView.byteLength) {
28005             Roo.log('Invalid Exif data: Invalid directory size.');
28006             return;
28007         }
28008         for (i = 0; i < tagsNumber; i += 1) {
28009             this.parseExifTag(
28010                 dataView,
28011                 tiffOffset,
28012                 dirOffset + 2 + 12 * i, // tag offset
28013                 littleEndian
28014             );
28015         }
28016         // Return the offset to the next directory:
28017         return dataView.getUint32(dirEndOffset, littleEndian);
28018     },
28019     
28020     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28021     {
28022         var tag = dataView.getUint16(offset, littleEndian);
28023         
28024         this.exif[tag] = this.getExifValue(
28025             dataView,
28026             tiffOffset,
28027             offset,
28028             dataView.getUint16(offset + 2, littleEndian), // tag type
28029             dataView.getUint32(offset + 4, littleEndian), // tag length
28030             littleEndian
28031         );
28032     },
28033     
28034     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28035     {
28036         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28037             tagSize,
28038             dataOffset,
28039             values,
28040             i,
28041             str,
28042             c;
28043     
28044         if (!tagType) {
28045             Roo.log('Invalid Exif data: Invalid tag type.');
28046             return;
28047         }
28048         
28049         tagSize = tagType.size * length;
28050         // Determine if the value is contained in the dataOffset bytes,
28051         // or if the value at the dataOffset is a pointer to the actual data:
28052         dataOffset = tagSize > 4 ?
28053                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28054         if (dataOffset + tagSize > dataView.byteLength) {
28055             Roo.log('Invalid Exif data: Invalid data offset.');
28056             return;
28057         }
28058         if (length === 1) {
28059             return tagType.getValue(dataView, dataOffset, littleEndian);
28060         }
28061         values = [];
28062         for (i = 0; i < length; i += 1) {
28063             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28064         }
28065         
28066         if (tagType.ascii) {
28067             str = '';
28068             // Concatenate the chars:
28069             for (i = 0; i < values.length; i += 1) {
28070                 c = values[i];
28071                 // Ignore the terminating NULL byte(s):
28072                 if (c === '\u0000') {
28073                     break;
28074                 }
28075                 str += c;
28076             }
28077             return str;
28078         }
28079         return values;
28080     }
28081     
28082 });
28083
28084 Roo.apply(Roo.bootstrap.UploadCropbox, {
28085     tags : {
28086         'Orientation': 0x0112
28087     },
28088     
28089     Orientation: {
28090             1: 0, //'top-left',
28091 //            2: 'top-right',
28092             3: 180, //'bottom-right',
28093 //            4: 'bottom-left',
28094 //            5: 'left-top',
28095             6: 90, //'right-top',
28096 //            7: 'right-bottom',
28097             8: 270 //'left-bottom'
28098     },
28099     
28100     exifTagTypes : {
28101         // byte, 8-bit unsigned int:
28102         1: {
28103             getValue: function (dataView, dataOffset) {
28104                 return dataView.getUint8(dataOffset);
28105             },
28106             size: 1
28107         },
28108         // ascii, 8-bit byte:
28109         2: {
28110             getValue: function (dataView, dataOffset) {
28111                 return String.fromCharCode(dataView.getUint8(dataOffset));
28112             },
28113             size: 1,
28114             ascii: true
28115         },
28116         // short, 16 bit int:
28117         3: {
28118             getValue: function (dataView, dataOffset, littleEndian) {
28119                 return dataView.getUint16(dataOffset, littleEndian);
28120             },
28121             size: 2
28122         },
28123         // long, 32 bit int:
28124         4: {
28125             getValue: function (dataView, dataOffset, littleEndian) {
28126                 return dataView.getUint32(dataOffset, littleEndian);
28127             },
28128             size: 4
28129         },
28130         // rational = two long values, first is numerator, second is denominator:
28131         5: {
28132             getValue: function (dataView, dataOffset, littleEndian) {
28133                 return dataView.getUint32(dataOffset, littleEndian) /
28134                     dataView.getUint32(dataOffset + 4, littleEndian);
28135             },
28136             size: 8
28137         },
28138         // slong, 32 bit signed int:
28139         9: {
28140             getValue: function (dataView, dataOffset, littleEndian) {
28141                 return dataView.getInt32(dataOffset, littleEndian);
28142             },
28143             size: 4
28144         },
28145         // srational, two slongs, first is numerator, second is denominator:
28146         10: {
28147             getValue: function (dataView, dataOffset, littleEndian) {
28148                 return dataView.getInt32(dataOffset, littleEndian) /
28149                     dataView.getInt32(dataOffset + 4, littleEndian);
28150             },
28151             size: 8
28152         }
28153     },
28154     
28155     footer : {
28156         STANDARD : [
28157             {
28158                 tag : 'div',
28159                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28160                 action : 'rotate-left',
28161                 cn : [
28162                     {
28163                         tag : 'button',
28164                         cls : 'btn btn-default',
28165                         html : '<i class="fa fa-undo"></i>'
28166                     }
28167                 ]
28168             },
28169             {
28170                 tag : 'div',
28171                 cls : 'btn-group roo-upload-cropbox-picture',
28172                 action : 'picture',
28173                 cn : [
28174                     {
28175                         tag : 'button',
28176                         cls : 'btn btn-default',
28177                         html : '<i class="fa fa-picture-o"></i>'
28178                     }
28179                 ]
28180             },
28181             {
28182                 tag : 'div',
28183                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28184                 action : 'rotate-right',
28185                 cn : [
28186                     {
28187                         tag : 'button',
28188                         cls : 'btn btn-default',
28189                         html : '<i class="fa fa-repeat"></i>'
28190                     }
28191                 ]
28192             }
28193         ],
28194         DOCUMENT : [
28195             {
28196                 tag : 'div',
28197                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28198                 action : 'rotate-left',
28199                 cn : [
28200                     {
28201                         tag : 'button',
28202                         cls : 'btn btn-default',
28203                         html : '<i class="fa fa-undo"></i>'
28204                     }
28205                 ]
28206             },
28207             {
28208                 tag : 'div',
28209                 cls : 'btn-group roo-upload-cropbox-download',
28210                 action : 'download',
28211                 cn : [
28212                     {
28213                         tag : 'button',
28214                         cls : 'btn btn-default',
28215                         html : '<i class="fa fa-download"></i>'
28216                     }
28217                 ]
28218             },
28219             {
28220                 tag : 'div',
28221                 cls : 'btn-group roo-upload-cropbox-crop',
28222                 action : 'crop',
28223                 cn : [
28224                     {
28225                         tag : 'button',
28226                         cls : 'btn btn-default',
28227                         html : '<i class="fa fa-crop"></i>'
28228                     }
28229                 ]
28230             },
28231             {
28232                 tag : 'div',
28233                 cls : 'btn-group roo-upload-cropbox-trash',
28234                 action : 'trash',
28235                 cn : [
28236                     {
28237                         tag : 'button',
28238                         cls : 'btn btn-default',
28239                         html : '<i class="fa fa-trash"></i>'
28240                     }
28241                 ]
28242             },
28243             {
28244                 tag : 'div',
28245                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28246                 action : 'rotate-right',
28247                 cn : [
28248                     {
28249                         tag : 'button',
28250                         cls : 'btn btn-default',
28251                         html : '<i class="fa fa-repeat"></i>'
28252                     }
28253                 ]
28254             }
28255         ],
28256         ROTATOR : [
28257             {
28258                 tag : 'div',
28259                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28260                 action : 'rotate-left',
28261                 cn : [
28262                     {
28263                         tag : 'button',
28264                         cls : 'btn btn-default',
28265                         html : '<i class="fa fa-undo"></i>'
28266                     }
28267                 ]
28268             },
28269             {
28270                 tag : 'div',
28271                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28272                 action : 'rotate-right',
28273                 cn : [
28274                     {
28275                         tag : 'button',
28276                         cls : 'btn btn-default',
28277                         html : '<i class="fa fa-repeat"></i>'
28278                     }
28279                 ]
28280             }
28281         ]
28282     }
28283 });
28284
28285 /*
28286 * Licence: LGPL
28287 */
28288
28289 /**
28290  * @class Roo.bootstrap.DocumentManager
28291  * @extends Roo.bootstrap.Component
28292  * Bootstrap DocumentManager class
28293  * @cfg {String} paramName default 'imageUpload'
28294  * @cfg {String} toolTipName default 'filename'
28295  * @cfg {String} method default POST
28296  * @cfg {String} url action url
28297  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28298  * @cfg {Boolean} multiple multiple upload default true
28299  * @cfg {Number} thumbSize default 300
28300  * @cfg {String} fieldLabel
28301  * @cfg {Number} labelWidth default 4
28302  * @cfg {String} labelAlign (left|top) default left
28303  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28304 * @cfg {Number} labellg set the width of label (1-12)
28305  * @cfg {Number} labelmd set the width of label (1-12)
28306  * @cfg {Number} labelsm set the width of label (1-12)
28307  * @cfg {Number} labelxs set the width of label (1-12)
28308  * 
28309  * @constructor
28310  * Create a new DocumentManager
28311  * @param {Object} config The config object
28312  */
28313
28314 Roo.bootstrap.DocumentManager = function(config){
28315     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28316     
28317     this.files = [];
28318     this.delegates = [];
28319     
28320     this.addEvents({
28321         /**
28322          * @event initial
28323          * Fire when initial the DocumentManager
28324          * @param {Roo.bootstrap.DocumentManager} this
28325          */
28326         "initial" : true,
28327         /**
28328          * @event inspect
28329          * inspect selected file
28330          * @param {Roo.bootstrap.DocumentManager} this
28331          * @param {File} file
28332          */
28333         "inspect" : true,
28334         /**
28335          * @event exception
28336          * Fire when xhr load exception
28337          * @param {Roo.bootstrap.DocumentManager} this
28338          * @param {XMLHttpRequest} xhr
28339          */
28340         "exception" : true,
28341         /**
28342          * @event afterupload
28343          * Fire when xhr load exception
28344          * @param {Roo.bootstrap.DocumentManager} this
28345          * @param {XMLHttpRequest} xhr
28346          */
28347         "afterupload" : true,
28348         /**
28349          * @event prepare
28350          * prepare the form data
28351          * @param {Roo.bootstrap.DocumentManager} this
28352          * @param {Object} formData
28353          */
28354         "prepare" : true,
28355         /**
28356          * @event remove
28357          * Fire when remove the file
28358          * @param {Roo.bootstrap.DocumentManager} this
28359          * @param {Object} file
28360          */
28361         "remove" : true,
28362         /**
28363          * @event refresh
28364          * Fire after refresh the file
28365          * @param {Roo.bootstrap.DocumentManager} this
28366          */
28367         "refresh" : true,
28368         /**
28369          * @event click
28370          * Fire after click the image
28371          * @param {Roo.bootstrap.DocumentManager} this
28372          * @param {Object} file
28373          */
28374         "click" : true,
28375         /**
28376          * @event edit
28377          * Fire when upload a image and editable set to true
28378          * @param {Roo.bootstrap.DocumentManager} this
28379          * @param {Object} file
28380          */
28381         "edit" : true,
28382         /**
28383          * @event beforeselectfile
28384          * Fire before select file
28385          * @param {Roo.bootstrap.DocumentManager} this
28386          */
28387         "beforeselectfile" : true,
28388         /**
28389          * @event process
28390          * Fire before process file
28391          * @param {Roo.bootstrap.DocumentManager} this
28392          * @param {Object} file
28393          */
28394         "process" : true
28395         
28396     });
28397 };
28398
28399 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28400     
28401     boxes : 0,
28402     inputName : '',
28403     thumbSize : 300,
28404     multiple : true,
28405     files : false,
28406     method : 'POST',
28407     url : '',
28408     paramName : 'imageUpload',
28409     toolTipName : 'filename',
28410     fieldLabel : '',
28411     labelWidth : 4,
28412     labelAlign : 'left',
28413     editable : true,
28414     delegates : false,
28415     xhr : false, 
28416     
28417     labellg : 0,
28418     labelmd : 0,
28419     labelsm : 0,
28420     labelxs : 0,
28421     
28422     getAutoCreate : function()
28423     {   
28424         var managerWidget = {
28425             tag : 'div',
28426             cls : 'roo-document-manager',
28427             cn : [
28428                 {
28429                     tag : 'input',
28430                     cls : 'roo-document-manager-selector',
28431                     type : 'file'
28432                 },
28433                 {
28434                     tag : 'div',
28435                     cls : 'roo-document-manager-uploader',
28436                     cn : [
28437                         {
28438                             tag : 'div',
28439                             cls : 'roo-document-manager-upload-btn',
28440                             html : '<i class="fa fa-plus"></i>'
28441                         }
28442                     ]
28443                     
28444                 }
28445             ]
28446         };
28447         
28448         var content = [
28449             {
28450                 tag : 'div',
28451                 cls : 'column col-md-12',
28452                 cn : managerWidget
28453             }
28454         ];
28455         
28456         if(this.fieldLabel.length){
28457             
28458             content = [
28459                 {
28460                     tag : 'div',
28461                     cls : 'column col-md-12',
28462                     html : this.fieldLabel
28463                 },
28464                 {
28465                     tag : 'div',
28466                     cls : 'column col-md-12',
28467                     cn : managerWidget
28468                 }
28469             ];
28470
28471             if(this.labelAlign == 'left'){
28472                 content = [
28473                     {
28474                         tag : 'div',
28475                         cls : 'column',
28476                         html : this.fieldLabel
28477                     },
28478                     {
28479                         tag : 'div',
28480                         cls : 'column',
28481                         cn : managerWidget
28482                     }
28483                 ];
28484                 
28485                 if(this.labelWidth > 12){
28486                     content[0].style = "width: " + this.labelWidth + 'px';
28487                 }
28488
28489                 if(this.labelWidth < 13 && this.labelmd == 0){
28490                     this.labelmd = this.labelWidth;
28491                 }
28492
28493                 if(this.labellg > 0){
28494                     content[0].cls += ' col-lg-' + this.labellg;
28495                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28496                 }
28497
28498                 if(this.labelmd > 0){
28499                     content[0].cls += ' col-md-' + this.labelmd;
28500                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28501                 }
28502
28503                 if(this.labelsm > 0){
28504                     content[0].cls += ' col-sm-' + this.labelsm;
28505                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28506                 }
28507
28508                 if(this.labelxs > 0){
28509                     content[0].cls += ' col-xs-' + this.labelxs;
28510                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28511                 }
28512                 
28513             }
28514         }
28515         
28516         var cfg = {
28517             tag : 'div',
28518             cls : 'row clearfix',
28519             cn : content
28520         };
28521         
28522         return cfg;
28523         
28524     },
28525     
28526     initEvents : function()
28527     {
28528         this.managerEl = this.el.select('.roo-document-manager', true).first();
28529         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28530         
28531         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28532         this.selectorEl.hide();
28533         
28534         if(this.multiple){
28535             this.selectorEl.attr('multiple', 'multiple');
28536         }
28537         
28538         this.selectorEl.on('change', this.onFileSelected, this);
28539         
28540         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28541         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28542         
28543         this.uploader.on('click', this.onUploaderClick, this);
28544         
28545         this.renderProgressDialog();
28546         
28547         var _this = this;
28548         
28549         window.addEventListener("resize", function() { _this.refresh(); } );
28550         
28551         this.fireEvent('initial', this);
28552     },
28553     
28554     renderProgressDialog : function()
28555     {
28556         var _this = this;
28557         
28558         this.progressDialog = new Roo.bootstrap.Modal({
28559             cls : 'roo-document-manager-progress-dialog',
28560             allow_close : false,
28561             title : '',
28562             buttons : [
28563                 {
28564                     name  :'cancel',
28565                     weight : 'danger',
28566                     html : 'Cancel'
28567                 }
28568             ], 
28569             listeners : { 
28570                 btnclick : function() {
28571                     _this.uploadCancel();
28572                     this.hide();
28573                 }
28574             }
28575         });
28576          
28577         this.progressDialog.render(Roo.get(document.body));
28578          
28579         this.progress = new Roo.bootstrap.Progress({
28580             cls : 'roo-document-manager-progress',
28581             active : true,
28582             striped : true
28583         });
28584         
28585         this.progress.render(this.progressDialog.getChildContainer());
28586         
28587         this.progressBar = new Roo.bootstrap.ProgressBar({
28588             cls : 'roo-document-manager-progress-bar',
28589             aria_valuenow : 0,
28590             aria_valuemin : 0,
28591             aria_valuemax : 12,
28592             panel : 'success'
28593         });
28594         
28595         this.progressBar.render(this.progress.getChildContainer());
28596     },
28597     
28598     onUploaderClick : function(e)
28599     {
28600         e.preventDefault();
28601      
28602         if(this.fireEvent('beforeselectfile', this) != false){
28603             this.selectorEl.dom.click();
28604         }
28605         
28606     },
28607     
28608     onFileSelected : function(e)
28609     {
28610         e.preventDefault();
28611         
28612         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28613             return;
28614         }
28615         
28616         Roo.each(this.selectorEl.dom.files, function(file){
28617             if(this.fireEvent('inspect', this, file) != false){
28618                 this.files.push(file);
28619             }
28620         }, this);
28621         
28622         this.queue();
28623         
28624     },
28625     
28626     queue : function()
28627     {
28628         this.selectorEl.dom.value = '';
28629         
28630         if(!this.files.length){
28631             return;
28632         }
28633         
28634         if(this.boxes > 0 && this.files.length > this.boxes){
28635             this.files = this.files.slice(0, this.boxes);
28636         }
28637         
28638         this.uploader.show();
28639         
28640         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28641             this.uploader.hide();
28642         }
28643         
28644         var _this = this;
28645         
28646         var files = [];
28647         
28648         var docs = [];
28649         
28650         Roo.each(this.files, function(file){
28651             
28652             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28653                 var f = this.renderPreview(file);
28654                 files.push(f);
28655                 return;
28656             }
28657             
28658             if(file.type.indexOf('image') != -1){
28659                 this.delegates.push(
28660                     (function(){
28661                         _this.process(file);
28662                     }).createDelegate(this)
28663                 );
28664         
28665                 return;
28666             }
28667             
28668             docs.push(
28669                 (function(){
28670                     _this.process(file);
28671                 }).createDelegate(this)
28672             );
28673             
28674         }, this);
28675         
28676         this.files = files;
28677         
28678         this.delegates = this.delegates.concat(docs);
28679         
28680         if(!this.delegates.length){
28681             this.refresh();
28682             return;
28683         }
28684         
28685         this.progressBar.aria_valuemax = this.delegates.length;
28686         
28687         this.arrange();
28688         
28689         return;
28690     },
28691     
28692     arrange : function()
28693     {
28694         if(!this.delegates.length){
28695             this.progressDialog.hide();
28696             this.refresh();
28697             return;
28698         }
28699         
28700         var delegate = this.delegates.shift();
28701         
28702         this.progressDialog.show();
28703         
28704         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28705         
28706         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28707         
28708         delegate();
28709     },
28710     
28711     refresh : function()
28712     {
28713         this.uploader.show();
28714         
28715         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28716             this.uploader.hide();
28717         }
28718         
28719         Roo.isTouch ? this.closable(false) : this.closable(true);
28720         
28721         this.fireEvent('refresh', this);
28722     },
28723     
28724     onRemove : function(e, el, o)
28725     {
28726         e.preventDefault();
28727         
28728         this.fireEvent('remove', this, o);
28729         
28730     },
28731     
28732     remove : function(o)
28733     {
28734         var files = [];
28735         
28736         Roo.each(this.files, function(file){
28737             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28738                 files.push(file);
28739                 return;
28740             }
28741
28742             o.target.remove();
28743
28744         }, this);
28745         
28746         this.files = files;
28747         
28748         this.refresh();
28749     },
28750     
28751     clear : function()
28752     {
28753         Roo.each(this.files, function(file){
28754             if(!file.target){
28755                 return;
28756             }
28757             
28758             file.target.remove();
28759
28760         }, this);
28761         
28762         this.files = [];
28763         
28764         this.refresh();
28765     },
28766     
28767     onClick : function(e, el, o)
28768     {
28769         e.preventDefault();
28770         
28771         this.fireEvent('click', this, o);
28772         
28773     },
28774     
28775     closable : function(closable)
28776     {
28777         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28778             
28779             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28780             
28781             if(closable){
28782                 el.show();
28783                 return;
28784             }
28785             
28786             el.hide();
28787             
28788         }, this);
28789     },
28790     
28791     xhrOnLoad : function(xhr)
28792     {
28793         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28794             el.remove();
28795         }, this);
28796         
28797         if (xhr.readyState !== 4) {
28798             this.arrange();
28799             this.fireEvent('exception', this, xhr);
28800             return;
28801         }
28802
28803         var response = Roo.decode(xhr.responseText);
28804         
28805         if(!response.success){
28806             this.arrange();
28807             this.fireEvent('exception', this, xhr);
28808             return;
28809         }
28810         
28811         var file = this.renderPreview(response.data);
28812         
28813         this.files.push(file);
28814         
28815         this.arrange();
28816         
28817         this.fireEvent('afterupload', this, xhr);
28818         
28819     },
28820     
28821     xhrOnError : function(xhr)
28822     {
28823         Roo.log('xhr on error');
28824         
28825         var response = Roo.decode(xhr.responseText);
28826           
28827         Roo.log(response);
28828         
28829         this.arrange();
28830     },
28831     
28832     process : function(file)
28833     {
28834         if(this.fireEvent('process', this, file) !== false){
28835             if(this.editable && file.type.indexOf('image') != -1){
28836                 this.fireEvent('edit', this, file);
28837                 return;
28838             }
28839
28840             this.uploadStart(file, false);
28841
28842             return;
28843         }
28844         
28845     },
28846     
28847     uploadStart : function(file, crop)
28848     {
28849         this.xhr = new XMLHttpRequest();
28850         
28851         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28852             this.arrange();
28853             return;
28854         }
28855         
28856         file.xhr = this.xhr;
28857             
28858         this.managerEl.createChild({
28859             tag : 'div',
28860             cls : 'roo-document-manager-loading',
28861             cn : [
28862                 {
28863                     tag : 'div',
28864                     tooltip : file.name,
28865                     cls : 'roo-document-manager-thumb',
28866                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28867                 }
28868             ]
28869
28870         });
28871
28872         this.xhr.open(this.method, this.url, true);
28873         
28874         var headers = {
28875             "Accept": "application/json",
28876             "Cache-Control": "no-cache",
28877             "X-Requested-With": "XMLHttpRequest"
28878         };
28879         
28880         for (var headerName in headers) {
28881             var headerValue = headers[headerName];
28882             if (headerValue) {
28883                 this.xhr.setRequestHeader(headerName, headerValue);
28884             }
28885         }
28886         
28887         var _this = this;
28888         
28889         this.xhr.onload = function()
28890         {
28891             _this.xhrOnLoad(_this.xhr);
28892         }
28893         
28894         this.xhr.onerror = function()
28895         {
28896             _this.xhrOnError(_this.xhr);
28897         }
28898         
28899         var formData = new FormData();
28900
28901         formData.append('returnHTML', 'NO');
28902         
28903         if(crop){
28904             formData.append('crop', crop);
28905         }
28906         
28907         formData.append(this.paramName, file, file.name);
28908         
28909         var options = {
28910             file : file, 
28911             manually : false
28912         };
28913         
28914         if(this.fireEvent('prepare', this, formData, options) != false){
28915             
28916             if(options.manually){
28917                 return;
28918             }
28919             
28920             this.xhr.send(formData);
28921             return;
28922         };
28923         
28924         this.uploadCancel();
28925     },
28926     
28927     uploadCancel : function()
28928     {
28929         if (this.xhr) {
28930             this.xhr.abort();
28931         }
28932         
28933         this.delegates = [];
28934         
28935         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28936             el.remove();
28937         }, this);
28938         
28939         this.arrange();
28940     },
28941     
28942     renderPreview : function(file)
28943     {
28944         if(typeof(file.target) != 'undefined' && file.target){
28945             return file;
28946         }
28947         
28948         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28949         
28950         var previewEl = this.managerEl.createChild({
28951             tag : 'div',
28952             cls : 'roo-document-manager-preview',
28953             cn : [
28954                 {
28955                     tag : 'div',
28956                     tooltip : file[this.toolTipName],
28957                     cls : 'roo-document-manager-thumb',
28958                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28959                 },
28960                 {
28961                     tag : 'button',
28962                     cls : 'close',
28963                     html : '<i class="fa fa-times-circle"></i>'
28964                 }
28965             ]
28966         });
28967
28968         var close = previewEl.select('button.close', true).first();
28969
28970         close.on('click', this.onRemove, this, file);
28971
28972         file.target = previewEl;
28973
28974         var image = previewEl.select('img', true).first();
28975         
28976         var _this = this;
28977         
28978         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28979         
28980         image.on('click', this.onClick, this, file);
28981         
28982         return file;
28983         
28984     },
28985     
28986     onPreviewLoad : function(file, image)
28987     {
28988         if(typeof(file.target) == 'undefined' || !file.target){
28989             return;
28990         }
28991         
28992         var width = image.dom.naturalWidth || image.dom.width;
28993         var height = image.dom.naturalHeight || image.dom.height;
28994         
28995         if(width > height){
28996             file.target.addClass('wide');
28997             return;
28998         }
28999         
29000         file.target.addClass('tall');
29001         return;
29002         
29003     },
29004     
29005     uploadFromSource : function(file, crop)
29006     {
29007         this.xhr = new XMLHttpRequest();
29008         
29009         this.managerEl.createChild({
29010             tag : 'div',
29011             cls : 'roo-document-manager-loading',
29012             cn : [
29013                 {
29014                     tag : 'div',
29015                     tooltip : file.name,
29016                     cls : 'roo-document-manager-thumb',
29017                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29018                 }
29019             ]
29020
29021         });
29022
29023         this.xhr.open(this.method, this.url, true);
29024         
29025         var headers = {
29026             "Accept": "application/json",
29027             "Cache-Control": "no-cache",
29028             "X-Requested-With": "XMLHttpRequest"
29029         };
29030         
29031         for (var headerName in headers) {
29032             var headerValue = headers[headerName];
29033             if (headerValue) {
29034                 this.xhr.setRequestHeader(headerName, headerValue);
29035             }
29036         }
29037         
29038         var _this = this;
29039         
29040         this.xhr.onload = function()
29041         {
29042             _this.xhrOnLoad(_this.xhr);
29043         }
29044         
29045         this.xhr.onerror = function()
29046         {
29047             _this.xhrOnError(_this.xhr);
29048         }
29049         
29050         var formData = new FormData();
29051
29052         formData.append('returnHTML', 'NO');
29053         
29054         formData.append('crop', crop);
29055         
29056         if(typeof(file.filename) != 'undefined'){
29057             formData.append('filename', file.filename);
29058         }
29059         
29060         if(typeof(file.mimetype) != 'undefined'){
29061             formData.append('mimetype', file.mimetype);
29062         }
29063         
29064         Roo.log(formData);
29065         
29066         if(this.fireEvent('prepare', this, formData) != false){
29067             this.xhr.send(formData);
29068         };
29069     }
29070 });
29071
29072 /*
29073 * Licence: LGPL
29074 */
29075
29076 /**
29077  * @class Roo.bootstrap.DocumentViewer
29078  * @extends Roo.bootstrap.Component
29079  * Bootstrap DocumentViewer class
29080  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29081  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29082  * 
29083  * @constructor
29084  * Create a new DocumentViewer
29085  * @param {Object} config The config object
29086  */
29087
29088 Roo.bootstrap.DocumentViewer = function(config){
29089     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29090     
29091     this.addEvents({
29092         /**
29093          * @event initial
29094          * Fire after initEvent
29095          * @param {Roo.bootstrap.DocumentViewer} this
29096          */
29097         "initial" : true,
29098         /**
29099          * @event click
29100          * Fire after click
29101          * @param {Roo.bootstrap.DocumentViewer} this
29102          */
29103         "click" : true,
29104         /**
29105          * @event download
29106          * Fire after download button
29107          * @param {Roo.bootstrap.DocumentViewer} this
29108          */
29109         "download" : true,
29110         /**
29111          * @event trash
29112          * Fire after trash button
29113          * @param {Roo.bootstrap.DocumentViewer} this
29114          */
29115         "trash" : true
29116         
29117     });
29118 };
29119
29120 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29121     
29122     showDownload : true,
29123     
29124     showTrash : true,
29125     
29126     getAutoCreate : function()
29127     {
29128         var cfg = {
29129             tag : 'div',
29130             cls : 'roo-document-viewer',
29131             cn : [
29132                 {
29133                     tag : 'div',
29134                     cls : 'roo-document-viewer-body',
29135                     cn : [
29136                         {
29137                             tag : 'div',
29138                             cls : 'roo-document-viewer-thumb',
29139                             cn : [
29140                                 {
29141                                     tag : 'img',
29142                                     cls : 'roo-document-viewer-image'
29143                                 }
29144                             ]
29145                         }
29146                     ]
29147                 },
29148                 {
29149                     tag : 'div',
29150                     cls : 'roo-document-viewer-footer',
29151                     cn : {
29152                         tag : 'div',
29153                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29154                         cn : [
29155                             {
29156                                 tag : 'div',
29157                                 cls : 'btn-group roo-document-viewer-download',
29158                                 cn : [
29159                                     {
29160                                         tag : 'button',
29161                                         cls : 'btn btn-default',
29162                                         html : '<i class="fa fa-download"></i>'
29163                                     }
29164                                 ]
29165                             },
29166                             {
29167                                 tag : 'div',
29168                                 cls : 'btn-group roo-document-viewer-trash',
29169                                 cn : [
29170                                     {
29171                                         tag : 'button',
29172                                         cls : 'btn btn-default',
29173                                         html : '<i class="fa fa-trash"></i>'
29174                                     }
29175                                 ]
29176                             }
29177                         ]
29178                     }
29179                 }
29180             ]
29181         };
29182         
29183         return cfg;
29184     },
29185     
29186     initEvents : function()
29187     {
29188         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29189         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29190         
29191         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29192         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29193         
29194         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29195         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29196         
29197         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29198         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29199         
29200         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29201         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29202         
29203         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29204         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29205         
29206         this.bodyEl.on('click', this.onClick, this);
29207         this.downloadBtn.on('click', this.onDownload, this);
29208         this.trashBtn.on('click', this.onTrash, this);
29209         
29210         this.downloadBtn.hide();
29211         this.trashBtn.hide();
29212         
29213         if(this.showDownload){
29214             this.downloadBtn.show();
29215         }
29216         
29217         if(this.showTrash){
29218             this.trashBtn.show();
29219         }
29220         
29221         if(!this.showDownload && !this.showTrash) {
29222             this.footerEl.hide();
29223         }
29224         
29225     },
29226     
29227     initial : function()
29228     {
29229         this.fireEvent('initial', this);
29230         
29231     },
29232     
29233     onClick : function(e)
29234     {
29235         e.preventDefault();
29236         
29237         this.fireEvent('click', this);
29238     },
29239     
29240     onDownload : function(e)
29241     {
29242         e.preventDefault();
29243         
29244         this.fireEvent('download', this);
29245     },
29246     
29247     onTrash : function(e)
29248     {
29249         e.preventDefault();
29250         
29251         this.fireEvent('trash', this);
29252     }
29253     
29254 });
29255 /*
29256  * - LGPL
29257  *
29258  * nav progress bar
29259  * 
29260  */
29261
29262 /**
29263  * @class Roo.bootstrap.NavProgressBar
29264  * @extends Roo.bootstrap.Component
29265  * Bootstrap NavProgressBar class
29266  * 
29267  * @constructor
29268  * Create a new nav progress bar
29269  * @param {Object} config The config object
29270  */
29271
29272 Roo.bootstrap.NavProgressBar = function(config){
29273     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29274
29275     this.bullets = this.bullets || [];
29276    
29277 //    Roo.bootstrap.NavProgressBar.register(this);
29278      this.addEvents({
29279         /**
29280              * @event changed
29281              * Fires when the active item changes
29282              * @param {Roo.bootstrap.NavProgressBar} this
29283              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29284              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29285          */
29286         'changed': true
29287      });
29288     
29289 };
29290
29291 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29292     
29293     bullets : [],
29294     barItems : [],
29295     
29296     getAutoCreate : function()
29297     {
29298         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29299         
29300         cfg = {
29301             tag : 'div',
29302             cls : 'roo-navigation-bar-group',
29303             cn : [
29304                 {
29305                     tag : 'div',
29306                     cls : 'roo-navigation-top-bar'
29307                 },
29308                 {
29309                     tag : 'div',
29310                     cls : 'roo-navigation-bullets-bar',
29311                     cn : [
29312                         {
29313                             tag : 'ul',
29314                             cls : 'roo-navigation-bar'
29315                         }
29316                     ]
29317                 },
29318                 
29319                 {
29320                     tag : 'div',
29321                     cls : 'roo-navigation-bottom-bar'
29322                 }
29323             ]
29324             
29325         };
29326         
29327         return cfg;
29328         
29329     },
29330     
29331     initEvents: function() 
29332     {
29333         
29334     },
29335     
29336     onRender : function(ct, position) 
29337     {
29338         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29339         
29340         if(this.bullets.length){
29341             Roo.each(this.bullets, function(b){
29342                this.addItem(b);
29343             }, this);
29344         }
29345         
29346         this.format();
29347         
29348     },
29349     
29350     addItem : function(cfg)
29351     {
29352         var item = new Roo.bootstrap.NavProgressItem(cfg);
29353         
29354         item.parentId = this.id;
29355         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29356         
29357         if(cfg.html){
29358             var top = new Roo.bootstrap.Element({
29359                 tag : 'div',
29360                 cls : 'roo-navigation-bar-text'
29361             });
29362             
29363             var bottom = new Roo.bootstrap.Element({
29364                 tag : 'div',
29365                 cls : 'roo-navigation-bar-text'
29366             });
29367             
29368             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29369             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29370             
29371             var topText = new Roo.bootstrap.Element({
29372                 tag : 'span',
29373                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29374             });
29375             
29376             var bottomText = new Roo.bootstrap.Element({
29377                 tag : 'span',
29378                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29379             });
29380             
29381             topText.onRender(top.el, null);
29382             bottomText.onRender(bottom.el, null);
29383             
29384             item.topEl = top;
29385             item.bottomEl = bottom;
29386         }
29387         
29388         this.barItems.push(item);
29389         
29390         return item;
29391     },
29392     
29393     getActive : function()
29394     {
29395         var active = false;
29396         
29397         Roo.each(this.barItems, function(v){
29398             
29399             if (!v.isActive()) {
29400                 return;
29401             }
29402             
29403             active = v;
29404             return false;
29405             
29406         });
29407         
29408         return active;
29409     },
29410     
29411     setActiveItem : function(item)
29412     {
29413         var prev = false;
29414         
29415         Roo.each(this.barItems, function(v){
29416             if (v.rid == item.rid) {
29417                 return ;
29418             }
29419             
29420             if (v.isActive()) {
29421                 v.setActive(false);
29422                 prev = v;
29423             }
29424         });
29425
29426         item.setActive(true);
29427         
29428         this.fireEvent('changed', this, item, prev);
29429     },
29430     
29431     getBarItem: function(rid)
29432     {
29433         var ret = false;
29434         
29435         Roo.each(this.barItems, function(e) {
29436             if (e.rid != rid) {
29437                 return;
29438             }
29439             
29440             ret =  e;
29441             return false;
29442         });
29443         
29444         return ret;
29445     },
29446     
29447     indexOfItem : function(item)
29448     {
29449         var index = false;
29450         
29451         Roo.each(this.barItems, function(v, i){
29452             
29453             if (v.rid != item.rid) {
29454                 return;
29455             }
29456             
29457             index = i;
29458             return false
29459         });
29460         
29461         return index;
29462     },
29463     
29464     setActiveNext : function()
29465     {
29466         var i = this.indexOfItem(this.getActive());
29467         
29468         if (i > this.barItems.length) {
29469             return;
29470         }
29471         
29472         this.setActiveItem(this.barItems[i+1]);
29473     },
29474     
29475     setActivePrev : function()
29476     {
29477         var i = this.indexOfItem(this.getActive());
29478         
29479         if (i  < 1) {
29480             return;
29481         }
29482         
29483         this.setActiveItem(this.barItems[i-1]);
29484     },
29485     
29486     format : function()
29487     {
29488         if(!this.barItems.length){
29489             return;
29490         }
29491      
29492         var width = 100 / this.barItems.length;
29493         
29494         Roo.each(this.barItems, function(i){
29495             i.el.setStyle('width', width + '%');
29496             i.topEl.el.setStyle('width', width + '%');
29497             i.bottomEl.el.setStyle('width', width + '%');
29498         }, this);
29499         
29500     }
29501     
29502 });
29503 /*
29504  * - LGPL
29505  *
29506  * Nav Progress Item
29507  * 
29508  */
29509
29510 /**
29511  * @class Roo.bootstrap.NavProgressItem
29512  * @extends Roo.bootstrap.Component
29513  * Bootstrap NavProgressItem class
29514  * @cfg {String} rid the reference id
29515  * @cfg {Boolean} active (true|false) Is item active default false
29516  * @cfg {Boolean} disabled (true|false) Is item active default false
29517  * @cfg {String} html
29518  * @cfg {String} position (top|bottom) text position default bottom
29519  * @cfg {String} icon show icon instead of number
29520  * 
29521  * @constructor
29522  * Create a new NavProgressItem
29523  * @param {Object} config The config object
29524  */
29525 Roo.bootstrap.NavProgressItem = function(config){
29526     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29527     this.addEvents({
29528         // raw events
29529         /**
29530          * @event click
29531          * The raw click event for the entire grid.
29532          * @param {Roo.bootstrap.NavProgressItem} this
29533          * @param {Roo.EventObject} e
29534          */
29535         "click" : true
29536     });
29537    
29538 };
29539
29540 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29541     
29542     rid : '',
29543     active : false,
29544     disabled : false,
29545     html : '',
29546     position : 'bottom',
29547     icon : false,
29548     
29549     getAutoCreate : function()
29550     {
29551         var iconCls = 'roo-navigation-bar-item-icon';
29552         
29553         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29554         
29555         var cfg = {
29556             tag: 'li',
29557             cls: 'roo-navigation-bar-item',
29558             cn : [
29559                 {
29560                     tag : 'i',
29561                     cls : iconCls
29562                 }
29563             ]
29564         };
29565         
29566         if(this.active){
29567             cfg.cls += ' active';
29568         }
29569         if(this.disabled){
29570             cfg.cls += ' disabled';
29571         }
29572         
29573         return cfg;
29574     },
29575     
29576     disable : function()
29577     {
29578         this.setDisabled(true);
29579     },
29580     
29581     enable : function()
29582     {
29583         this.setDisabled(false);
29584     },
29585     
29586     initEvents: function() 
29587     {
29588         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29589         
29590         this.iconEl.on('click', this.onClick, this);
29591     },
29592     
29593     onClick : function(e)
29594     {
29595         e.preventDefault();
29596         
29597         if(this.disabled){
29598             return;
29599         }
29600         
29601         if(this.fireEvent('click', this, e) === false){
29602             return;
29603         };
29604         
29605         this.parent().setActiveItem(this);
29606     },
29607     
29608     isActive: function () 
29609     {
29610         return this.active;
29611     },
29612     
29613     setActive : function(state)
29614     {
29615         if(this.active == state){
29616             return;
29617         }
29618         
29619         this.active = state;
29620         
29621         if (state) {
29622             this.el.addClass('active');
29623             return;
29624         }
29625         
29626         this.el.removeClass('active');
29627         
29628         return;
29629     },
29630     
29631     setDisabled : function(state)
29632     {
29633         if(this.disabled == state){
29634             return;
29635         }
29636         
29637         this.disabled = state;
29638         
29639         if (state) {
29640             this.el.addClass('disabled');
29641             return;
29642         }
29643         
29644         this.el.removeClass('disabled');
29645     },
29646     
29647     tooltipEl : function()
29648     {
29649         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29650     }
29651 });
29652  
29653
29654  /*
29655  * - LGPL
29656  *
29657  * FieldLabel
29658  * 
29659  */
29660
29661 /**
29662  * @class Roo.bootstrap.FieldLabel
29663  * @extends Roo.bootstrap.Component
29664  * Bootstrap FieldLabel class
29665  * @cfg {String} html contents of the element
29666  * @cfg {String} tag tag of the element default label
29667  * @cfg {String} cls class of the element
29668  * @cfg {String} target label target 
29669  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29670  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29671  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29672  * @cfg {String} iconTooltip default "This field is required"
29673  * 
29674  * @constructor
29675  * Create a new FieldLabel
29676  * @param {Object} config The config object
29677  */
29678
29679 Roo.bootstrap.FieldLabel = function(config){
29680     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29681     
29682     this.addEvents({
29683             /**
29684              * @event invalid
29685              * Fires after the field has been marked as invalid.
29686              * @param {Roo.form.FieldLabel} this
29687              * @param {String} msg The validation message
29688              */
29689             invalid : true,
29690             /**
29691              * @event valid
29692              * Fires after the field has been validated with no errors.
29693              * @param {Roo.form.FieldLabel} this
29694              */
29695             valid : true
29696         });
29697 };
29698
29699 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29700     
29701     tag: 'label',
29702     cls: '',
29703     html: '',
29704     target: '',
29705     allowBlank : true,
29706     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29707     validClass : 'text-success fa fa-lg fa-check',
29708     iconTooltip : 'This field is required',
29709     
29710     getAutoCreate : function(){
29711         
29712         var cfg = {
29713             tag : this.tag,
29714             cls : 'roo-bootstrap-field-label ' + this.cls,
29715             for : this.target,
29716             cn : [
29717                 {
29718                     tag : 'i',
29719                     cls : '',
29720                     tooltip : this.iconTooltip
29721                 },
29722                 {
29723                     tag : 'span',
29724                     html : this.html
29725                 }
29726             ] 
29727         };
29728         
29729         return cfg;
29730     },
29731     
29732     initEvents: function() 
29733     {
29734         Roo.bootstrap.Element.superclass.initEvents.call(this);
29735         
29736         this.iconEl = this.el.select('i', true).first();
29737         
29738         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29739         
29740         Roo.bootstrap.FieldLabel.register(this);
29741     },
29742     
29743     /**
29744      * Mark this field as valid
29745      */
29746     markValid : function()
29747     {
29748         this.iconEl.show();
29749         
29750         this.iconEl.removeClass(this.invalidClass);
29751         
29752         this.iconEl.addClass(this.validClass);
29753         
29754         this.fireEvent('valid', this);
29755     },
29756     
29757     /**
29758      * Mark this field as invalid
29759      * @param {String} msg The validation message
29760      */
29761     markInvalid : function(msg)
29762     {
29763         this.iconEl.show();
29764         
29765         this.iconEl.removeClass(this.validClass);
29766         
29767         this.iconEl.addClass(this.invalidClass);
29768         
29769         this.fireEvent('invalid', this, msg);
29770     }
29771     
29772    
29773 });
29774
29775 Roo.apply(Roo.bootstrap.FieldLabel, {
29776     
29777     groups: {},
29778     
29779      /**
29780     * register a FieldLabel Group
29781     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29782     */
29783     register : function(label)
29784     {
29785         if(this.groups.hasOwnProperty(label.target)){
29786             return;
29787         }
29788      
29789         this.groups[label.target] = label;
29790         
29791     },
29792     /**
29793     * fetch a FieldLabel Group based on the target
29794     * @param {string} target
29795     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29796     */
29797     get: function(target) {
29798         if (typeof(this.groups[target]) == 'undefined') {
29799             return false;
29800         }
29801         
29802         return this.groups[target] ;
29803     }
29804 });
29805
29806  
29807
29808  /*
29809  * - LGPL
29810  *
29811  * page DateSplitField.
29812  * 
29813  */
29814
29815
29816 /**
29817  * @class Roo.bootstrap.DateSplitField
29818  * @extends Roo.bootstrap.Component
29819  * Bootstrap DateSplitField class
29820  * @cfg {string} fieldLabel - the label associated
29821  * @cfg {Number} labelWidth set the width of label (0-12)
29822  * @cfg {String} labelAlign (top|left)
29823  * @cfg {Boolean} dayAllowBlank (true|false) default false
29824  * @cfg {Boolean} monthAllowBlank (true|false) default false
29825  * @cfg {Boolean} yearAllowBlank (true|false) default false
29826  * @cfg {string} dayPlaceholder 
29827  * @cfg {string} monthPlaceholder
29828  * @cfg {string} yearPlaceholder
29829  * @cfg {string} dayFormat default 'd'
29830  * @cfg {string} monthFormat default 'm'
29831  * @cfg {string} yearFormat default 'Y'
29832  * @cfg {Number} labellg set the width of label (1-12)
29833  * @cfg {Number} labelmd set the width of label (1-12)
29834  * @cfg {Number} labelsm set the width of label (1-12)
29835  * @cfg {Number} labelxs set the width of label (1-12)
29836
29837  *     
29838  * @constructor
29839  * Create a new DateSplitField
29840  * @param {Object} config The config object
29841  */
29842
29843 Roo.bootstrap.DateSplitField = function(config){
29844     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29845     
29846     this.addEvents({
29847         // raw events
29848          /**
29849          * @event years
29850          * getting the data of years
29851          * @param {Roo.bootstrap.DateSplitField} this
29852          * @param {Object} years
29853          */
29854         "years" : true,
29855         /**
29856          * @event days
29857          * getting the data of days
29858          * @param {Roo.bootstrap.DateSplitField} this
29859          * @param {Object} days
29860          */
29861         "days" : true,
29862         /**
29863          * @event invalid
29864          * Fires after the field has been marked as invalid.
29865          * @param {Roo.form.Field} this
29866          * @param {String} msg The validation message
29867          */
29868         invalid : true,
29869        /**
29870          * @event valid
29871          * Fires after the field has been validated with no errors.
29872          * @param {Roo.form.Field} this
29873          */
29874         valid : true
29875     });
29876 };
29877
29878 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29879     
29880     fieldLabel : '',
29881     labelAlign : 'top',
29882     labelWidth : 3,
29883     dayAllowBlank : false,
29884     monthAllowBlank : false,
29885     yearAllowBlank : false,
29886     dayPlaceholder : '',
29887     monthPlaceholder : '',
29888     yearPlaceholder : '',
29889     dayFormat : 'd',
29890     monthFormat : 'm',
29891     yearFormat : 'Y',
29892     isFormField : true,
29893     labellg : 0,
29894     labelmd : 0,
29895     labelsm : 0,
29896     labelxs : 0,
29897     
29898     getAutoCreate : function()
29899     {
29900         var cfg = {
29901             tag : 'div',
29902             cls : 'row roo-date-split-field-group',
29903             cn : [
29904                 {
29905                     tag : 'input',
29906                     type : 'hidden',
29907                     cls : 'form-hidden-field roo-date-split-field-group-value',
29908                     name : this.name
29909                 }
29910             ]
29911         };
29912         
29913         var labelCls = 'col-md-12';
29914         var contentCls = 'col-md-4';
29915         
29916         if(this.fieldLabel){
29917             
29918             var label = {
29919                 tag : 'div',
29920                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29921                 cn : [
29922                     {
29923                         tag : 'label',
29924                         html : this.fieldLabel
29925                     }
29926                 ]
29927             };
29928             
29929             if(this.labelAlign == 'left'){
29930             
29931                 if(this.labelWidth > 12){
29932                     label.style = "width: " + this.labelWidth + 'px';
29933                 }
29934
29935                 if(this.labelWidth < 13 && this.labelmd == 0){
29936                     this.labelmd = this.labelWidth;
29937                 }
29938
29939                 if(this.labellg > 0){
29940                     labelCls = ' col-lg-' + this.labellg;
29941                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29942                 }
29943
29944                 if(this.labelmd > 0){
29945                     labelCls = ' col-md-' + this.labelmd;
29946                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29947                 }
29948
29949                 if(this.labelsm > 0){
29950                     labelCls = ' col-sm-' + this.labelsm;
29951                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29952                 }
29953
29954                 if(this.labelxs > 0){
29955                     labelCls = ' col-xs-' + this.labelxs;
29956                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29957                 }
29958             }
29959             
29960             label.cls += ' ' + labelCls;
29961             
29962             cfg.cn.push(label);
29963         }
29964         
29965         Roo.each(['day', 'month', 'year'], function(t){
29966             cfg.cn.push({
29967                 tag : 'div',
29968                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29969             });
29970         }, this);
29971         
29972         return cfg;
29973     },
29974     
29975     inputEl: function ()
29976     {
29977         return this.el.select('.roo-date-split-field-group-value', true).first();
29978     },
29979     
29980     onRender : function(ct, position) 
29981     {
29982         var _this = this;
29983         
29984         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29985         
29986         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29987         
29988         this.dayField = new Roo.bootstrap.ComboBox({
29989             allowBlank : this.dayAllowBlank,
29990             alwaysQuery : true,
29991             displayField : 'value',
29992             editable : false,
29993             fieldLabel : '',
29994             forceSelection : true,
29995             mode : 'local',
29996             placeholder : this.dayPlaceholder,
29997             selectOnFocus : true,
29998             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29999             triggerAction : 'all',
30000             typeAhead : true,
30001             valueField : 'value',
30002             store : new Roo.data.SimpleStore({
30003                 data : (function() {    
30004                     var days = [];
30005                     _this.fireEvent('days', _this, days);
30006                     return days;
30007                 })(),
30008                 fields : [ 'value' ]
30009             }),
30010             listeners : {
30011                 select : function (_self, record, index)
30012                 {
30013                     _this.setValue(_this.getValue());
30014                 }
30015             }
30016         });
30017
30018         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30019         
30020         this.monthField = new Roo.bootstrap.MonthField({
30021             after : '<i class=\"fa fa-calendar\"></i>',
30022             allowBlank : this.monthAllowBlank,
30023             placeholder : this.monthPlaceholder,
30024             readOnly : true,
30025             listeners : {
30026                 render : function (_self)
30027                 {
30028                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30029                         e.preventDefault();
30030                         _self.focus();
30031                     });
30032                 },
30033                 select : function (_self, oldvalue, newvalue)
30034                 {
30035                     _this.setValue(_this.getValue());
30036                 }
30037             }
30038         });
30039         
30040         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30041         
30042         this.yearField = new Roo.bootstrap.ComboBox({
30043             allowBlank : this.yearAllowBlank,
30044             alwaysQuery : true,
30045             displayField : 'value',
30046             editable : false,
30047             fieldLabel : '',
30048             forceSelection : true,
30049             mode : 'local',
30050             placeholder : this.yearPlaceholder,
30051             selectOnFocus : true,
30052             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30053             triggerAction : 'all',
30054             typeAhead : true,
30055             valueField : 'value',
30056             store : new Roo.data.SimpleStore({
30057                 data : (function() {
30058                     var years = [];
30059                     _this.fireEvent('years', _this, years);
30060                     return years;
30061                 })(),
30062                 fields : [ 'value' ]
30063             }),
30064             listeners : {
30065                 select : function (_self, record, index)
30066                 {
30067                     _this.setValue(_this.getValue());
30068                 }
30069             }
30070         });
30071
30072         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30073     },
30074     
30075     setValue : function(v, format)
30076     {
30077         this.inputEl.dom.value = v;
30078         
30079         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30080         
30081         var d = Date.parseDate(v, f);
30082         
30083         if(!d){
30084             this.validate();
30085             return;
30086         }
30087         
30088         this.setDay(d.format(this.dayFormat));
30089         this.setMonth(d.format(this.monthFormat));
30090         this.setYear(d.format(this.yearFormat));
30091         
30092         this.validate();
30093         
30094         return;
30095     },
30096     
30097     setDay : function(v)
30098     {
30099         this.dayField.setValue(v);
30100         this.inputEl.dom.value = this.getValue();
30101         this.validate();
30102         return;
30103     },
30104     
30105     setMonth : function(v)
30106     {
30107         this.monthField.setValue(v, true);
30108         this.inputEl.dom.value = this.getValue();
30109         this.validate();
30110         return;
30111     },
30112     
30113     setYear : function(v)
30114     {
30115         this.yearField.setValue(v);
30116         this.inputEl.dom.value = this.getValue();
30117         this.validate();
30118         return;
30119     },
30120     
30121     getDay : function()
30122     {
30123         return this.dayField.getValue();
30124     },
30125     
30126     getMonth : function()
30127     {
30128         return this.monthField.getValue();
30129     },
30130     
30131     getYear : function()
30132     {
30133         return this.yearField.getValue();
30134     },
30135     
30136     getValue : function()
30137     {
30138         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30139         
30140         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30141         
30142         return date;
30143     },
30144     
30145     reset : function()
30146     {
30147         this.setDay('');
30148         this.setMonth('');
30149         this.setYear('');
30150         this.inputEl.dom.value = '';
30151         this.validate();
30152         return;
30153     },
30154     
30155     validate : function()
30156     {
30157         var d = this.dayField.validate();
30158         var m = this.monthField.validate();
30159         var y = this.yearField.validate();
30160         
30161         var valid = true;
30162         
30163         if(
30164                 (!this.dayAllowBlank && !d) ||
30165                 (!this.monthAllowBlank && !m) ||
30166                 (!this.yearAllowBlank && !y)
30167         ){
30168             valid = false;
30169         }
30170         
30171         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30172             return valid;
30173         }
30174         
30175         if(valid){
30176             this.markValid();
30177             return valid;
30178         }
30179         
30180         this.markInvalid();
30181         
30182         return valid;
30183     },
30184     
30185     markValid : function()
30186     {
30187         
30188         var label = this.el.select('label', true).first();
30189         var icon = this.el.select('i.fa-star', true).first();
30190
30191         if(label && icon){
30192             icon.remove();
30193         }
30194         
30195         this.fireEvent('valid', this);
30196     },
30197     
30198      /**
30199      * Mark this field as invalid
30200      * @param {String} msg The validation message
30201      */
30202     markInvalid : function(msg)
30203     {
30204         
30205         var label = this.el.select('label', true).first();
30206         var icon = this.el.select('i.fa-star', true).first();
30207
30208         if(label && !icon){
30209             this.el.select('.roo-date-split-field-label', true).createChild({
30210                 tag : 'i',
30211                 cls : 'text-danger fa fa-lg fa-star',
30212                 tooltip : 'This field is required',
30213                 style : 'margin-right:5px;'
30214             }, label, true);
30215         }
30216         
30217         this.fireEvent('invalid', this, msg);
30218     },
30219     
30220     clearInvalid : function()
30221     {
30222         var label = this.el.select('label', true).first();
30223         var icon = this.el.select('i.fa-star', true).first();
30224
30225         if(label && icon){
30226             icon.remove();
30227         }
30228         
30229         this.fireEvent('valid', this);
30230     },
30231     
30232     getName: function()
30233     {
30234         return this.name;
30235     }
30236     
30237 });
30238
30239  /**
30240  *
30241  * This is based on 
30242  * http://masonry.desandro.com
30243  *
30244  * The idea is to render all the bricks based on vertical width...
30245  *
30246  * The original code extends 'outlayer' - we might need to use that....
30247  * 
30248  */
30249
30250
30251 /**
30252  * @class Roo.bootstrap.LayoutMasonry
30253  * @extends Roo.bootstrap.Component
30254  * Bootstrap Layout Masonry class
30255  * 
30256  * @constructor
30257  * Create a new Element
30258  * @param {Object} config The config object
30259  */
30260
30261 Roo.bootstrap.LayoutMasonry = function(config){
30262     
30263     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30264     
30265     this.bricks = [];
30266     
30267     Roo.bootstrap.LayoutMasonry.register(this);
30268     
30269     this.addEvents({
30270         // raw events
30271         /**
30272          * @event layout
30273          * Fire after layout the items
30274          * @param {Roo.bootstrap.LayoutMasonry} this
30275          * @param {Roo.EventObject} e
30276          */
30277         "layout" : true
30278     });
30279     
30280 };
30281
30282 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30283     
30284     /**
30285      * @cfg {Boolean} isLayoutInstant = no animation?
30286      */   
30287     isLayoutInstant : false, // needed?
30288    
30289     /**
30290      * @cfg {Number} boxWidth  width of the columns
30291      */   
30292     boxWidth : 450,
30293     
30294       /**
30295      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30296      */   
30297     boxHeight : 0,
30298     
30299     /**
30300      * @cfg {Number} padWidth padding below box..
30301      */   
30302     padWidth : 10, 
30303     
30304     /**
30305      * @cfg {Number} gutter gutter width..
30306      */   
30307     gutter : 10,
30308     
30309      /**
30310      * @cfg {Number} maxCols maximum number of columns
30311      */   
30312     
30313     maxCols: 0,
30314     
30315     /**
30316      * @cfg {Boolean} isAutoInitial defalut true
30317      */   
30318     isAutoInitial : true, 
30319     
30320     containerWidth: 0,
30321     
30322     /**
30323      * @cfg {Boolean} isHorizontal defalut false
30324      */   
30325     isHorizontal : false, 
30326
30327     currentSize : null,
30328     
30329     tag: 'div',
30330     
30331     cls: '',
30332     
30333     bricks: null, //CompositeElement
30334     
30335     cols : 1,
30336     
30337     _isLayoutInited : false,
30338     
30339 //    isAlternative : false, // only use for vertical layout...
30340     
30341     /**
30342      * @cfg {Number} alternativePadWidth padding below box..
30343      */   
30344     alternativePadWidth : 50,
30345     
30346     selectedBrick : [],
30347     
30348     getAutoCreate : function(){
30349         
30350         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30351         
30352         var cfg = {
30353             tag: this.tag,
30354             cls: 'blog-masonary-wrapper ' + this.cls,
30355             cn : {
30356                 cls : 'mas-boxes masonary'
30357             }
30358         };
30359         
30360         return cfg;
30361     },
30362     
30363     getChildContainer: function( )
30364     {
30365         if (this.boxesEl) {
30366             return this.boxesEl;
30367         }
30368         
30369         this.boxesEl = this.el.select('.mas-boxes').first();
30370         
30371         return this.boxesEl;
30372     },
30373     
30374     
30375     initEvents : function()
30376     {
30377         var _this = this;
30378         
30379         if(this.isAutoInitial){
30380             Roo.log('hook children rendered');
30381             this.on('childrenrendered', function() {
30382                 Roo.log('children rendered');
30383                 _this.initial();
30384             } ,this);
30385         }
30386     },
30387     
30388     initial : function()
30389     {
30390         this.selectedBrick = [];
30391         
30392         this.currentSize = this.el.getBox(true);
30393         
30394         Roo.EventManager.onWindowResize(this.resize, this); 
30395
30396         if(!this.isAutoInitial){
30397             this.layout();
30398             return;
30399         }
30400         
30401         this.layout();
30402         
30403         return;
30404         //this.layout.defer(500,this);
30405         
30406     },
30407     
30408     resize : function()
30409     {
30410         var cs = this.el.getBox(true);
30411         
30412         if (
30413                 this.currentSize.width == cs.width && 
30414                 this.currentSize.x == cs.x && 
30415                 this.currentSize.height == cs.height && 
30416                 this.currentSize.y == cs.y 
30417         ) {
30418             Roo.log("no change in with or X or Y");
30419             return;
30420         }
30421         
30422         this.currentSize = cs;
30423         
30424         this.layout();
30425         
30426     },
30427     
30428     layout : function()
30429     {   
30430         this._resetLayout();
30431         
30432         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30433         
30434         this.layoutItems( isInstant );
30435       
30436         this._isLayoutInited = true;
30437         
30438         this.fireEvent('layout', this);
30439         
30440     },
30441     
30442     _resetLayout : function()
30443     {
30444         if(this.isHorizontal){
30445             this.horizontalMeasureColumns();
30446             return;
30447         }
30448         
30449         this.verticalMeasureColumns();
30450         
30451     },
30452     
30453     verticalMeasureColumns : function()
30454     {
30455         this.getContainerWidth();
30456         
30457 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30458 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30459 //            return;
30460 //        }
30461         
30462         var boxWidth = this.boxWidth + this.padWidth;
30463         
30464         if(this.containerWidth < this.boxWidth){
30465             boxWidth = this.containerWidth
30466         }
30467         
30468         var containerWidth = this.containerWidth;
30469         
30470         var cols = Math.floor(containerWidth / boxWidth);
30471         
30472         this.cols = Math.max( cols, 1 );
30473         
30474         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30475         
30476         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30477         
30478         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30479         
30480         this.colWidth = boxWidth + avail - this.padWidth;
30481         
30482         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30483         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30484     },
30485     
30486     horizontalMeasureColumns : function()
30487     {
30488         this.getContainerWidth();
30489         
30490         var boxWidth = this.boxWidth;
30491         
30492         if(this.containerWidth < boxWidth){
30493             boxWidth = this.containerWidth;
30494         }
30495         
30496         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30497         
30498         this.el.setHeight(boxWidth);
30499         
30500     },
30501     
30502     getContainerWidth : function()
30503     {
30504         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30505     },
30506     
30507     layoutItems : function( isInstant )
30508     {
30509         Roo.log(this.bricks);
30510         
30511         var items = Roo.apply([], this.bricks);
30512         
30513         if(this.isHorizontal){
30514             this._horizontalLayoutItems( items , isInstant );
30515             return;
30516         }
30517         
30518 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30519 //            this._verticalAlternativeLayoutItems( items , isInstant );
30520 //            return;
30521 //        }
30522         
30523         this._verticalLayoutItems( items , isInstant );
30524         
30525     },
30526     
30527     _verticalLayoutItems : function ( items , isInstant)
30528     {
30529         if ( !items || !items.length ) {
30530             return;
30531         }
30532         
30533         var standard = [
30534             ['xs', 'xs', 'xs', 'tall'],
30535             ['xs', 'xs', 'tall'],
30536             ['xs', 'xs', 'sm'],
30537             ['xs', 'xs', 'xs'],
30538             ['xs', 'tall'],
30539             ['xs', 'sm'],
30540             ['xs', 'xs'],
30541             ['xs'],
30542             
30543             ['sm', 'xs', 'xs'],
30544             ['sm', 'xs'],
30545             ['sm'],
30546             
30547             ['tall', 'xs', 'xs', 'xs'],
30548             ['tall', 'xs', 'xs'],
30549             ['tall', 'xs'],
30550             ['tall']
30551             
30552         ];
30553         
30554         var queue = [];
30555         
30556         var boxes = [];
30557         
30558         var box = [];
30559         
30560         Roo.each(items, function(item, k){
30561             
30562             switch (item.size) {
30563                 // these layouts take up a full box,
30564                 case 'md' :
30565                 case 'md-left' :
30566                 case 'md-right' :
30567                 case 'wide' :
30568                     
30569                     if(box.length){
30570                         boxes.push(box);
30571                         box = [];
30572                     }
30573                     
30574                     boxes.push([item]);
30575                     
30576                     break;
30577                     
30578                 case 'xs' :
30579                 case 'sm' :
30580                 case 'tall' :
30581                     
30582                     box.push(item);
30583                     
30584                     break;
30585                 default :
30586                     break;
30587                     
30588             }
30589             
30590         }, this);
30591         
30592         if(box.length){
30593             boxes.push(box);
30594             box = [];
30595         }
30596         
30597         var filterPattern = function(box, length)
30598         {
30599             if(!box.length){
30600                 return;
30601             }
30602             
30603             var match = false;
30604             
30605             var pattern = box.slice(0, length);
30606             
30607             var format = [];
30608             
30609             Roo.each(pattern, function(i){
30610                 format.push(i.size);
30611             }, this);
30612             
30613             Roo.each(standard, function(s){
30614                 
30615                 if(String(s) != String(format)){
30616                     return;
30617                 }
30618                 
30619                 match = true;
30620                 return false;
30621                 
30622             }, this);
30623             
30624             if(!match && length == 1){
30625                 return;
30626             }
30627             
30628             if(!match){
30629                 filterPattern(box, length - 1);
30630                 return;
30631             }
30632                 
30633             queue.push(pattern);
30634
30635             box = box.slice(length, box.length);
30636
30637             filterPattern(box, 4);
30638
30639             return;
30640             
30641         }
30642         
30643         Roo.each(boxes, function(box, k){
30644             
30645             if(!box.length){
30646                 return;
30647             }
30648             
30649             if(box.length == 1){
30650                 queue.push(box);
30651                 return;
30652             }
30653             
30654             filterPattern(box, 4);
30655             
30656         }, this);
30657         
30658         this._processVerticalLayoutQueue( queue, isInstant );
30659         
30660     },
30661     
30662 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30663 //    {
30664 //        if ( !items || !items.length ) {
30665 //            return;
30666 //        }
30667 //
30668 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30669 //        
30670 //    },
30671     
30672     _horizontalLayoutItems : function ( items , isInstant)
30673     {
30674         if ( !items || !items.length || items.length < 3) {
30675             return;
30676         }
30677         
30678         items.reverse();
30679         
30680         var eItems = items.slice(0, 3);
30681         
30682         items = items.slice(3, items.length);
30683         
30684         var standard = [
30685             ['xs', 'xs', 'xs', 'wide'],
30686             ['xs', 'xs', 'wide'],
30687             ['xs', 'xs', 'sm'],
30688             ['xs', 'xs', 'xs'],
30689             ['xs', 'wide'],
30690             ['xs', 'sm'],
30691             ['xs', 'xs'],
30692             ['xs'],
30693             
30694             ['sm', 'xs', 'xs'],
30695             ['sm', 'xs'],
30696             ['sm'],
30697             
30698             ['wide', 'xs', 'xs', 'xs'],
30699             ['wide', 'xs', 'xs'],
30700             ['wide', 'xs'],
30701             ['wide'],
30702             
30703             ['wide-thin']
30704         ];
30705         
30706         var queue = [];
30707         
30708         var boxes = [];
30709         
30710         var box = [];
30711         
30712         Roo.each(items, function(item, k){
30713             
30714             switch (item.size) {
30715                 case 'md' :
30716                 case 'md-left' :
30717                 case 'md-right' :
30718                 case 'tall' :
30719                     
30720                     if(box.length){
30721                         boxes.push(box);
30722                         box = [];
30723                     }
30724                     
30725                     boxes.push([item]);
30726                     
30727                     break;
30728                     
30729                 case 'xs' :
30730                 case 'sm' :
30731                 case 'wide' :
30732                 case 'wide-thin' :
30733                     
30734                     box.push(item);
30735                     
30736                     break;
30737                 default :
30738                     break;
30739                     
30740             }
30741             
30742         }, this);
30743         
30744         if(box.length){
30745             boxes.push(box);
30746             box = [];
30747         }
30748         
30749         var filterPattern = function(box, length)
30750         {
30751             if(!box.length){
30752                 return;
30753             }
30754             
30755             var match = false;
30756             
30757             var pattern = box.slice(0, length);
30758             
30759             var format = [];
30760             
30761             Roo.each(pattern, function(i){
30762                 format.push(i.size);
30763             }, this);
30764             
30765             Roo.each(standard, function(s){
30766                 
30767                 if(String(s) != String(format)){
30768                     return;
30769                 }
30770                 
30771                 match = true;
30772                 return false;
30773                 
30774             }, this);
30775             
30776             if(!match && length == 1){
30777                 return;
30778             }
30779             
30780             if(!match){
30781                 filterPattern(box, length - 1);
30782                 return;
30783             }
30784                 
30785             queue.push(pattern);
30786
30787             box = box.slice(length, box.length);
30788
30789             filterPattern(box, 4);
30790
30791             return;
30792             
30793         }
30794         
30795         Roo.each(boxes, function(box, k){
30796             
30797             if(!box.length){
30798                 return;
30799             }
30800             
30801             if(box.length == 1){
30802                 queue.push(box);
30803                 return;
30804             }
30805             
30806             filterPattern(box, 4);
30807             
30808         }, this);
30809         
30810         
30811         var prune = [];
30812         
30813         var pos = this.el.getBox(true);
30814         
30815         var minX = pos.x;
30816         
30817         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30818         
30819         var hit_end = false;
30820         
30821         Roo.each(queue, function(box){
30822             
30823             if(hit_end){
30824                 
30825                 Roo.each(box, function(b){
30826                 
30827                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30828                     b.el.hide();
30829
30830                 }, this);
30831
30832                 return;
30833             }
30834             
30835             var mx = 0;
30836             
30837             Roo.each(box, function(b){
30838                 
30839                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30840                 b.el.show();
30841
30842                 mx = Math.max(mx, b.x);
30843                 
30844             }, this);
30845             
30846             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30847             
30848             if(maxX < minX){
30849                 
30850                 Roo.each(box, function(b){
30851                 
30852                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30853                     b.el.hide();
30854                     
30855                 }, this);
30856                 
30857                 hit_end = true;
30858                 
30859                 return;
30860             }
30861             
30862             prune.push(box);
30863             
30864         }, this);
30865         
30866         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30867     },
30868     
30869     /** Sets position of item in DOM
30870     * @param {Element} item
30871     * @param {Number} x - horizontal position
30872     * @param {Number} y - vertical position
30873     * @param {Boolean} isInstant - disables transitions
30874     */
30875     _processVerticalLayoutQueue : function( queue, isInstant )
30876     {
30877         var pos = this.el.getBox(true);
30878         var x = pos.x;
30879         var y = pos.y;
30880         var maxY = [];
30881         
30882         for (var i = 0; i < this.cols; i++){
30883             maxY[i] = pos.y;
30884         }
30885         
30886         Roo.each(queue, function(box, k){
30887             
30888             var col = k % this.cols;
30889             
30890             Roo.each(box, function(b,kk){
30891                 
30892                 b.el.position('absolute');
30893                 
30894                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30895                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30896                 
30897                 if(b.size == 'md-left' || b.size == 'md-right'){
30898                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30899                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30900                 }
30901                 
30902                 b.el.setWidth(width);
30903                 b.el.setHeight(height);
30904                 // iframe?
30905                 b.el.select('iframe',true).setSize(width,height);
30906                 
30907             }, this);
30908             
30909             for (var i = 0; i < this.cols; i++){
30910                 
30911                 if(maxY[i] < maxY[col]){
30912                     col = i;
30913                     continue;
30914                 }
30915                 
30916                 col = Math.min(col, i);
30917                 
30918             }
30919             
30920             x = pos.x + col * (this.colWidth + this.padWidth);
30921             
30922             y = maxY[col];
30923             
30924             var positions = [];
30925             
30926             switch (box.length){
30927                 case 1 :
30928                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30929                     break;
30930                 case 2 :
30931                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30932                     break;
30933                 case 3 :
30934                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30935                     break;
30936                 case 4 :
30937                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30938                     break;
30939                 default :
30940                     break;
30941             }
30942             
30943             Roo.each(box, function(b,kk){
30944                 
30945                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30946                 
30947                 var sz = b.el.getSize();
30948                 
30949                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30950                 
30951             }, this);
30952             
30953         }, this);
30954         
30955         var mY = 0;
30956         
30957         for (var i = 0; i < this.cols; i++){
30958             mY = Math.max(mY, maxY[i]);
30959         }
30960         
30961         this.el.setHeight(mY - pos.y);
30962         
30963     },
30964     
30965 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30966 //    {
30967 //        var pos = this.el.getBox(true);
30968 //        var x = pos.x;
30969 //        var y = pos.y;
30970 //        var maxX = pos.right;
30971 //        
30972 //        var maxHeight = 0;
30973 //        
30974 //        Roo.each(items, function(item, k){
30975 //            
30976 //            var c = k % 2;
30977 //            
30978 //            item.el.position('absolute');
30979 //                
30980 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30981 //
30982 //            item.el.setWidth(width);
30983 //
30984 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30985 //
30986 //            item.el.setHeight(height);
30987 //            
30988 //            if(c == 0){
30989 //                item.el.setXY([x, y], isInstant ? false : true);
30990 //            } else {
30991 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30992 //            }
30993 //            
30994 //            y = y + height + this.alternativePadWidth;
30995 //            
30996 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30997 //            
30998 //        }, this);
30999 //        
31000 //        this.el.setHeight(maxHeight);
31001 //        
31002 //    },
31003     
31004     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31005     {
31006         var pos = this.el.getBox(true);
31007         
31008         var minX = pos.x;
31009         var minY = pos.y;
31010         
31011         var maxX = pos.right;
31012         
31013         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31014         
31015         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31016         
31017         Roo.each(queue, function(box, k){
31018             
31019             Roo.each(box, function(b, kk){
31020                 
31021                 b.el.position('absolute');
31022                 
31023                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31024                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31025                 
31026                 if(b.size == 'md-left' || b.size == 'md-right'){
31027                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31028                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31029                 }
31030                 
31031                 b.el.setWidth(width);
31032                 b.el.setHeight(height);
31033                 
31034             }, this);
31035             
31036             if(!box.length){
31037                 return;
31038             }
31039             
31040             var positions = [];
31041             
31042             switch (box.length){
31043                 case 1 :
31044                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31045                     break;
31046                 case 2 :
31047                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31048                     break;
31049                 case 3 :
31050                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31051                     break;
31052                 case 4 :
31053                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31054                     break;
31055                 default :
31056                     break;
31057             }
31058             
31059             Roo.each(box, function(b,kk){
31060                 
31061                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31062                 
31063                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31064                 
31065             }, this);
31066             
31067         }, this);
31068         
31069     },
31070     
31071     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31072     {
31073         Roo.each(eItems, function(b,k){
31074             
31075             b.size = (k == 0) ? 'sm' : 'xs';
31076             b.x = (k == 0) ? 2 : 1;
31077             b.y = (k == 0) ? 2 : 1;
31078             
31079             b.el.position('absolute');
31080             
31081             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31082                 
31083             b.el.setWidth(width);
31084             
31085             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31086             
31087             b.el.setHeight(height);
31088             
31089         }, this);
31090
31091         var positions = [];
31092         
31093         positions.push({
31094             x : maxX - this.unitWidth * 2 - this.gutter,
31095             y : minY
31096         });
31097         
31098         positions.push({
31099             x : maxX - this.unitWidth,
31100             y : minY + (this.unitWidth + this.gutter) * 2
31101         });
31102         
31103         positions.push({
31104             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31105             y : minY
31106         });
31107         
31108         Roo.each(eItems, function(b,k){
31109             
31110             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31111
31112         }, this);
31113         
31114     },
31115     
31116     getVerticalOneBoxColPositions : function(x, y, box)
31117     {
31118         var pos = [];
31119         
31120         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31121         
31122         if(box[0].size == 'md-left'){
31123             rand = 0;
31124         }
31125         
31126         if(box[0].size == 'md-right'){
31127             rand = 1;
31128         }
31129         
31130         pos.push({
31131             x : x + (this.unitWidth + this.gutter) * rand,
31132             y : y
31133         });
31134         
31135         return pos;
31136     },
31137     
31138     getVerticalTwoBoxColPositions : function(x, y, box)
31139     {
31140         var pos = [];
31141         
31142         if(box[0].size == 'xs'){
31143             
31144             pos.push({
31145                 x : x,
31146                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31147             });
31148
31149             pos.push({
31150                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31151                 y : y
31152             });
31153             
31154             return pos;
31155             
31156         }
31157         
31158         pos.push({
31159             x : x,
31160             y : y
31161         });
31162
31163         pos.push({
31164             x : x + (this.unitWidth + this.gutter) * 2,
31165             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31166         });
31167         
31168         return pos;
31169         
31170     },
31171     
31172     getVerticalThreeBoxColPositions : function(x, y, box)
31173     {
31174         var pos = [];
31175         
31176         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31177             
31178             pos.push({
31179                 x : x,
31180                 y : y
31181             });
31182
31183             pos.push({
31184                 x : x + (this.unitWidth + this.gutter) * 1,
31185                 y : y
31186             });
31187             
31188             pos.push({
31189                 x : x + (this.unitWidth + this.gutter) * 2,
31190                 y : y
31191             });
31192             
31193             return pos;
31194             
31195         }
31196         
31197         if(box[0].size == 'xs' && box[1].size == 'xs'){
31198             
31199             pos.push({
31200                 x : x,
31201                 y : y
31202             });
31203
31204             pos.push({
31205                 x : x,
31206                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31207             });
31208             
31209             pos.push({
31210                 x : x + (this.unitWidth + this.gutter) * 1,
31211                 y : y
31212             });
31213             
31214             return pos;
31215             
31216         }
31217         
31218         pos.push({
31219             x : x,
31220             y : y
31221         });
31222
31223         pos.push({
31224             x : x + (this.unitWidth + this.gutter) * 2,
31225             y : y
31226         });
31227
31228         pos.push({
31229             x : x + (this.unitWidth + this.gutter) * 2,
31230             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31231         });
31232             
31233         return pos;
31234         
31235     },
31236     
31237     getVerticalFourBoxColPositions : function(x, y, box)
31238     {
31239         var pos = [];
31240         
31241         if(box[0].size == 'xs'){
31242             
31243             pos.push({
31244                 x : x,
31245                 y : y
31246             });
31247
31248             pos.push({
31249                 x : x,
31250                 y : y + (this.unitHeight + this.gutter) * 1
31251             });
31252             
31253             pos.push({
31254                 x : x,
31255                 y : y + (this.unitHeight + this.gutter) * 2
31256             });
31257             
31258             pos.push({
31259                 x : x + (this.unitWidth + this.gutter) * 1,
31260                 y : y
31261             });
31262             
31263             return pos;
31264             
31265         }
31266         
31267         pos.push({
31268             x : x,
31269             y : y
31270         });
31271
31272         pos.push({
31273             x : x + (this.unitWidth + this.gutter) * 2,
31274             y : y
31275         });
31276
31277         pos.push({
31278             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31279             y : y + (this.unitHeight + this.gutter) * 1
31280         });
31281
31282         pos.push({
31283             x : x + (this.unitWidth + this.gutter) * 2,
31284             y : y + (this.unitWidth + this.gutter) * 2
31285         });
31286
31287         return pos;
31288         
31289     },
31290     
31291     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31292     {
31293         var pos = [];
31294         
31295         if(box[0].size == 'md-left'){
31296             pos.push({
31297                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31298                 y : minY
31299             });
31300             
31301             return pos;
31302         }
31303         
31304         if(box[0].size == 'md-right'){
31305             pos.push({
31306                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31307                 y : minY + (this.unitWidth + this.gutter) * 1
31308             });
31309             
31310             return pos;
31311         }
31312         
31313         var rand = Math.floor(Math.random() * (4 - box[0].y));
31314         
31315         pos.push({
31316             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31317             y : minY + (this.unitWidth + this.gutter) * rand
31318         });
31319         
31320         return pos;
31321         
31322     },
31323     
31324     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31325     {
31326         var pos = [];
31327         
31328         if(box[0].size == 'xs'){
31329             
31330             pos.push({
31331                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31332                 y : minY
31333             });
31334
31335             pos.push({
31336                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31337                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31338             });
31339             
31340             return pos;
31341             
31342         }
31343         
31344         pos.push({
31345             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31346             y : minY
31347         });
31348
31349         pos.push({
31350             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31351             y : minY + (this.unitWidth + this.gutter) * 2
31352         });
31353         
31354         return pos;
31355         
31356     },
31357     
31358     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31359     {
31360         var pos = [];
31361         
31362         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31363             
31364             pos.push({
31365                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31366                 y : minY
31367             });
31368
31369             pos.push({
31370                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31371                 y : minY + (this.unitWidth + this.gutter) * 1
31372             });
31373             
31374             pos.push({
31375                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31376                 y : minY + (this.unitWidth + this.gutter) * 2
31377             });
31378             
31379             return pos;
31380             
31381         }
31382         
31383         if(box[0].size == 'xs' && box[1].size == 'xs'){
31384             
31385             pos.push({
31386                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31387                 y : minY
31388             });
31389
31390             pos.push({
31391                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31392                 y : minY
31393             });
31394             
31395             pos.push({
31396                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31397                 y : minY + (this.unitWidth + this.gutter) * 1
31398             });
31399             
31400             return pos;
31401             
31402         }
31403         
31404         pos.push({
31405             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31406             y : minY
31407         });
31408
31409         pos.push({
31410             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31411             y : minY + (this.unitWidth + this.gutter) * 2
31412         });
31413
31414         pos.push({
31415             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31416             y : minY + (this.unitWidth + this.gutter) * 2
31417         });
31418             
31419         return pos;
31420         
31421     },
31422     
31423     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31424     {
31425         var pos = [];
31426         
31427         if(box[0].size == 'xs'){
31428             
31429             pos.push({
31430                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31431                 y : minY
31432             });
31433
31434             pos.push({
31435                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31436                 y : minY
31437             });
31438             
31439             pos.push({
31440                 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),
31441                 y : minY
31442             });
31443             
31444             pos.push({
31445                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31446                 y : minY + (this.unitWidth + this.gutter) * 1
31447             });
31448             
31449             return pos;
31450             
31451         }
31452         
31453         pos.push({
31454             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31455             y : minY
31456         });
31457         
31458         pos.push({
31459             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31460             y : minY + (this.unitWidth + this.gutter) * 2
31461         });
31462         
31463         pos.push({
31464             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31465             y : minY + (this.unitWidth + this.gutter) * 2
31466         });
31467         
31468         pos.push({
31469             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),
31470             y : minY + (this.unitWidth + this.gutter) * 2
31471         });
31472
31473         return pos;
31474         
31475     },
31476     
31477     /**
31478     * remove a Masonry Brick
31479     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31480     */
31481     removeBrick : function(brick_id)
31482     {
31483         if (!brick_id) {
31484             return;
31485         }
31486         
31487         for (var i = 0; i<this.bricks.length; i++) {
31488             if (this.bricks[i].id == brick_id) {
31489                 this.bricks.splice(i,1);
31490                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31491                 this.initial();
31492             }
31493         }
31494     },
31495     
31496     /**
31497     * adds a Masonry Brick
31498     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31499     */
31500     addBrick : function(cfg)
31501     {
31502         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31503         //this.register(cn);
31504         cn.parentId = this.id;
31505         cn.onRender(this.el, null);
31506         return cn;
31507     },
31508     
31509     /**
31510     * register a Masonry Brick
31511     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31512     */
31513     
31514     register : function(brick)
31515     {
31516         this.bricks.push(brick);
31517         brick.masonryId = this.id;
31518     },
31519     
31520     /**
31521     * clear all the Masonry Brick
31522     */
31523     clearAll : function()
31524     {
31525         this.bricks = [];
31526         //this.getChildContainer().dom.innerHTML = "";
31527         this.el.dom.innerHTML = '';
31528     },
31529     
31530     getSelected : function()
31531     {
31532         if (!this.selectedBrick) {
31533             return false;
31534         }
31535         
31536         return this.selectedBrick;
31537     }
31538 });
31539
31540 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31541     
31542     groups: {},
31543      /**
31544     * register a Masonry Layout
31545     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31546     */
31547     
31548     register : function(layout)
31549     {
31550         this.groups[layout.id] = layout;
31551     },
31552     /**
31553     * fetch a  Masonry Layout based on the masonry layout ID
31554     * @param {string} the masonry layout to add
31555     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31556     */
31557     
31558     get: function(layout_id) {
31559         if (typeof(this.groups[layout_id]) == 'undefined') {
31560             return false;
31561         }
31562         return this.groups[layout_id] ;
31563     }
31564     
31565     
31566     
31567 });
31568
31569  
31570
31571  /**
31572  *
31573  * This is based on 
31574  * http://masonry.desandro.com
31575  *
31576  * The idea is to render all the bricks based on vertical width...
31577  *
31578  * The original code extends 'outlayer' - we might need to use that....
31579  * 
31580  */
31581
31582
31583 /**
31584  * @class Roo.bootstrap.LayoutMasonryAuto
31585  * @extends Roo.bootstrap.Component
31586  * Bootstrap Layout Masonry class
31587  * 
31588  * @constructor
31589  * Create a new Element
31590  * @param {Object} config The config object
31591  */
31592
31593 Roo.bootstrap.LayoutMasonryAuto = function(config){
31594     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31595 };
31596
31597 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31598     
31599       /**
31600      * @cfg {Boolean} isFitWidth  - resize the width..
31601      */   
31602     isFitWidth : false,  // options..
31603     /**
31604      * @cfg {Boolean} isOriginLeft = left align?
31605      */   
31606     isOriginLeft : true,
31607     /**
31608      * @cfg {Boolean} isOriginTop = top align?
31609      */   
31610     isOriginTop : false,
31611     /**
31612      * @cfg {Boolean} isLayoutInstant = no animation?
31613      */   
31614     isLayoutInstant : false, // needed?
31615     /**
31616      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31617      */   
31618     isResizingContainer : true,
31619     /**
31620      * @cfg {Number} columnWidth  width of the columns 
31621      */   
31622     
31623     columnWidth : 0,
31624     
31625     /**
31626      * @cfg {Number} maxCols maximum number of columns
31627      */   
31628     
31629     maxCols: 0,
31630     /**
31631      * @cfg {Number} padHeight padding below box..
31632      */   
31633     
31634     padHeight : 10, 
31635     
31636     /**
31637      * @cfg {Boolean} isAutoInitial defalut true
31638      */   
31639     
31640     isAutoInitial : true, 
31641     
31642     // private?
31643     gutter : 0,
31644     
31645     containerWidth: 0,
31646     initialColumnWidth : 0,
31647     currentSize : null,
31648     
31649     colYs : null, // array.
31650     maxY : 0,
31651     padWidth: 10,
31652     
31653     
31654     tag: 'div',
31655     cls: '',
31656     bricks: null, //CompositeElement
31657     cols : 0, // array?
31658     // element : null, // wrapped now this.el
31659     _isLayoutInited : null, 
31660     
31661     
31662     getAutoCreate : function(){
31663         
31664         var cfg = {
31665             tag: this.tag,
31666             cls: 'blog-masonary-wrapper ' + this.cls,
31667             cn : {
31668                 cls : 'mas-boxes masonary'
31669             }
31670         };
31671         
31672         return cfg;
31673     },
31674     
31675     getChildContainer: function( )
31676     {
31677         if (this.boxesEl) {
31678             return this.boxesEl;
31679         }
31680         
31681         this.boxesEl = this.el.select('.mas-boxes').first();
31682         
31683         return this.boxesEl;
31684     },
31685     
31686     
31687     initEvents : function()
31688     {
31689         var _this = this;
31690         
31691         if(this.isAutoInitial){
31692             Roo.log('hook children rendered');
31693             this.on('childrenrendered', function() {
31694                 Roo.log('children rendered');
31695                 _this.initial();
31696             } ,this);
31697         }
31698         
31699     },
31700     
31701     initial : function()
31702     {
31703         this.reloadItems();
31704
31705         this.currentSize = this.el.getBox(true);
31706
31707         /// was window resize... - let's see if this works..
31708         Roo.EventManager.onWindowResize(this.resize, this); 
31709
31710         if(!this.isAutoInitial){
31711             this.layout();
31712             return;
31713         }
31714         
31715         this.layout.defer(500,this);
31716     },
31717     
31718     reloadItems: function()
31719     {
31720         this.bricks = this.el.select('.masonry-brick', true);
31721         
31722         this.bricks.each(function(b) {
31723             //Roo.log(b.getSize());
31724             if (!b.attr('originalwidth')) {
31725                 b.attr('originalwidth',  b.getSize().width);
31726             }
31727             
31728         });
31729         
31730         Roo.log(this.bricks.elements.length);
31731     },
31732     
31733     resize : function()
31734     {
31735         Roo.log('resize');
31736         var cs = this.el.getBox(true);
31737         
31738         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31739             Roo.log("no change in with or X");
31740             return;
31741         }
31742         this.currentSize = cs;
31743         this.layout();
31744     },
31745     
31746     layout : function()
31747     {
31748          Roo.log('layout');
31749         this._resetLayout();
31750         //this._manageStamps();
31751       
31752         // don't animate first layout
31753         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31754         this.layoutItems( isInstant );
31755       
31756         // flag for initalized
31757         this._isLayoutInited = true;
31758     },
31759     
31760     layoutItems : function( isInstant )
31761     {
31762         //var items = this._getItemsForLayout( this.items );
31763         // original code supports filtering layout items.. we just ignore it..
31764         
31765         this._layoutItems( this.bricks , isInstant );
31766       
31767         this._postLayout();
31768     },
31769     _layoutItems : function ( items , isInstant)
31770     {
31771        //this.fireEvent( 'layout', this, items );
31772     
31773
31774         if ( !items || !items.elements.length ) {
31775           // no items, emit event with empty array
31776             return;
31777         }
31778
31779         var queue = [];
31780         items.each(function(item) {
31781             Roo.log("layout item");
31782             Roo.log(item);
31783             // get x/y object from method
31784             var position = this._getItemLayoutPosition( item );
31785             // enqueue
31786             position.item = item;
31787             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31788             queue.push( position );
31789         }, this);
31790       
31791         this._processLayoutQueue( queue );
31792     },
31793     /** Sets position of item in DOM
31794     * @param {Element} item
31795     * @param {Number} x - horizontal position
31796     * @param {Number} y - vertical position
31797     * @param {Boolean} isInstant - disables transitions
31798     */
31799     _processLayoutQueue : function( queue )
31800     {
31801         for ( var i=0, len = queue.length; i < len; i++ ) {
31802             var obj = queue[i];
31803             obj.item.position('absolute');
31804             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31805         }
31806     },
31807       
31808     
31809     /**
31810     * Any logic you want to do after each layout,
31811     * i.e. size the container
31812     */
31813     _postLayout : function()
31814     {
31815         this.resizeContainer();
31816     },
31817     
31818     resizeContainer : function()
31819     {
31820         if ( !this.isResizingContainer ) {
31821             return;
31822         }
31823         var size = this._getContainerSize();
31824         if ( size ) {
31825             this.el.setSize(size.width,size.height);
31826             this.boxesEl.setSize(size.width,size.height);
31827         }
31828     },
31829     
31830     
31831     
31832     _resetLayout : function()
31833     {
31834         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31835         this.colWidth = this.el.getWidth();
31836         //this.gutter = this.el.getWidth(); 
31837         
31838         this.measureColumns();
31839
31840         // reset column Y
31841         var i = this.cols;
31842         this.colYs = [];
31843         while (i--) {
31844             this.colYs.push( 0 );
31845         }
31846     
31847         this.maxY = 0;
31848     },
31849
31850     measureColumns : function()
31851     {
31852         this.getContainerWidth();
31853       // if columnWidth is 0, default to outerWidth of first item
31854         if ( !this.columnWidth ) {
31855             var firstItem = this.bricks.first();
31856             Roo.log(firstItem);
31857             this.columnWidth  = this.containerWidth;
31858             if (firstItem && firstItem.attr('originalwidth') ) {
31859                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31860             }
31861             // columnWidth fall back to item of first element
31862             Roo.log("set column width?");
31863                         this.initialColumnWidth = this.columnWidth  ;
31864
31865             // if first elem has no width, default to size of container
31866             
31867         }
31868         
31869         
31870         if (this.initialColumnWidth) {
31871             this.columnWidth = this.initialColumnWidth;
31872         }
31873         
31874         
31875             
31876         // column width is fixed at the top - however if container width get's smaller we should
31877         // reduce it...
31878         
31879         // this bit calcs how man columns..
31880             
31881         var columnWidth = this.columnWidth += this.gutter;
31882       
31883         // calculate columns
31884         var containerWidth = this.containerWidth + this.gutter;
31885         
31886         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31887         // fix rounding errors, typically with gutters
31888         var excess = columnWidth - containerWidth % columnWidth;
31889         
31890         
31891         // if overshoot is less than a pixel, round up, otherwise floor it
31892         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31893         cols = Math[ mathMethod ]( cols );
31894         this.cols = Math.max( cols, 1 );
31895         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31896         
31897          // padding positioning..
31898         var totalColWidth = this.cols * this.columnWidth;
31899         var padavail = this.containerWidth - totalColWidth;
31900         // so for 2 columns - we need 3 'pads'
31901         
31902         var padNeeded = (1+this.cols) * this.padWidth;
31903         
31904         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31905         
31906         this.columnWidth += padExtra
31907         //this.padWidth = Math.floor(padavail /  ( this.cols));
31908         
31909         // adjust colum width so that padding is fixed??
31910         
31911         // we have 3 columns ... total = width * 3
31912         // we have X left over... that should be used by 
31913         
31914         //if (this.expandC) {
31915             
31916         //}
31917         
31918         
31919         
31920     },
31921     
31922     getContainerWidth : function()
31923     {
31924        /* // container is parent if fit width
31925         var container = this.isFitWidth ? this.element.parentNode : this.element;
31926         // check that this.size and size are there
31927         // IE8 triggers resize on body size change, so they might not be
31928         
31929         var size = getSize( container );  //FIXME
31930         this.containerWidth = size && size.innerWidth; //FIXME
31931         */
31932          
31933         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31934         
31935     },
31936     
31937     _getItemLayoutPosition : function( item )  // what is item?
31938     {
31939         // we resize the item to our columnWidth..
31940       
31941         item.setWidth(this.columnWidth);
31942         item.autoBoxAdjust  = false;
31943         
31944         var sz = item.getSize();
31945  
31946         // how many columns does this brick span
31947         var remainder = this.containerWidth % this.columnWidth;
31948         
31949         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31950         // round if off by 1 pixel, otherwise use ceil
31951         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31952         colSpan = Math.min( colSpan, this.cols );
31953         
31954         // normally this should be '1' as we dont' currently allow multi width columns..
31955         
31956         var colGroup = this._getColGroup( colSpan );
31957         // get the minimum Y value from the columns
31958         var minimumY = Math.min.apply( Math, colGroup );
31959         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31960         
31961         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31962          
31963         // position the brick
31964         var position = {
31965             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31966             y: this.currentSize.y + minimumY + this.padHeight
31967         };
31968         
31969         Roo.log(position);
31970         // apply setHeight to necessary columns
31971         var setHeight = minimumY + sz.height + this.padHeight;
31972         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31973         
31974         var setSpan = this.cols + 1 - colGroup.length;
31975         for ( var i = 0; i < setSpan; i++ ) {
31976           this.colYs[ shortColIndex + i ] = setHeight ;
31977         }
31978       
31979         return position;
31980     },
31981     
31982     /**
31983      * @param {Number} colSpan - number of columns the element spans
31984      * @returns {Array} colGroup
31985      */
31986     _getColGroup : function( colSpan )
31987     {
31988         if ( colSpan < 2 ) {
31989           // if brick spans only one column, use all the column Ys
31990           return this.colYs;
31991         }
31992       
31993         var colGroup = [];
31994         // how many different places could this brick fit horizontally
31995         var groupCount = this.cols + 1 - colSpan;
31996         // for each group potential horizontal position
31997         for ( var i = 0; i < groupCount; i++ ) {
31998           // make an array of colY values for that one group
31999           var groupColYs = this.colYs.slice( i, i + colSpan );
32000           // and get the max value of the array
32001           colGroup[i] = Math.max.apply( Math, groupColYs );
32002         }
32003         return colGroup;
32004     },
32005     /*
32006     _manageStamp : function( stamp )
32007     {
32008         var stampSize =  stamp.getSize();
32009         var offset = stamp.getBox();
32010         // get the columns that this stamp affects
32011         var firstX = this.isOriginLeft ? offset.x : offset.right;
32012         var lastX = firstX + stampSize.width;
32013         var firstCol = Math.floor( firstX / this.columnWidth );
32014         firstCol = Math.max( 0, firstCol );
32015         
32016         var lastCol = Math.floor( lastX / this.columnWidth );
32017         // lastCol should not go over if multiple of columnWidth #425
32018         lastCol -= lastX % this.columnWidth ? 0 : 1;
32019         lastCol = Math.min( this.cols - 1, lastCol );
32020         
32021         // set colYs to bottom of the stamp
32022         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32023             stampSize.height;
32024             
32025         for ( var i = firstCol; i <= lastCol; i++ ) {
32026           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32027         }
32028     },
32029     */
32030     
32031     _getContainerSize : function()
32032     {
32033         this.maxY = Math.max.apply( Math, this.colYs );
32034         var size = {
32035             height: this.maxY
32036         };
32037       
32038         if ( this.isFitWidth ) {
32039             size.width = this._getContainerFitWidth();
32040         }
32041       
32042         return size;
32043     },
32044     
32045     _getContainerFitWidth : function()
32046     {
32047         var unusedCols = 0;
32048         // count unused columns
32049         var i = this.cols;
32050         while ( --i ) {
32051           if ( this.colYs[i] !== 0 ) {
32052             break;
32053           }
32054           unusedCols++;
32055         }
32056         // fit container to columns that have been used
32057         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32058     },
32059     
32060     needsResizeLayout : function()
32061     {
32062         var previousWidth = this.containerWidth;
32063         this.getContainerWidth();
32064         return previousWidth !== this.containerWidth;
32065     }
32066  
32067 });
32068
32069  
32070
32071  /*
32072  * - LGPL
32073  *
32074  * element
32075  * 
32076  */
32077
32078 /**
32079  * @class Roo.bootstrap.MasonryBrick
32080  * @extends Roo.bootstrap.Component
32081  * Bootstrap MasonryBrick class
32082  * 
32083  * @constructor
32084  * Create a new MasonryBrick
32085  * @param {Object} config The config object
32086  */
32087
32088 Roo.bootstrap.MasonryBrick = function(config){
32089     
32090     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32091     
32092     Roo.bootstrap.MasonryBrick.register(this);
32093     
32094     this.addEvents({
32095         // raw events
32096         /**
32097          * @event click
32098          * When a MasonryBrick is clcik
32099          * @param {Roo.bootstrap.MasonryBrick} this
32100          * @param {Roo.EventObject} e
32101          */
32102         "click" : true
32103     });
32104 };
32105
32106 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32107     
32108     /**
32109      * @cfg {String} title
32110      */   
32111     title : '',
32112     /**
32113      * @cfg {String} html
32114      */   
32115     html : '',
32116     /**
32117      * @cfg {String} bgimage
32118      */   
32119     bgimage : '',
32120     /**
32121      * @cfg {String} videourl
32122      */   
32123     videourl : '',
32124     /**
32125      * @cfg {String} cls
32126      */   
32127     cls : '',
32128     /**
32129      * @cfg {String} href
32130      */   
32131     href : '',
32132     /**
32133      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32134      */   
32135     size : 'xs',
32136     
32137     /**
32138      * @cfg {String} placetitle (center|bottom)
32139      */   
32140     placetitle : '',
32141     
32142     /**
32143      * @cfg {Boolean} isFitContainer defalut true
32144      */   
32145     isFitContainer : true, 
32146     
32147     /**
32148      * @cfg {Boolean} preventDefault defalut false
32149      */   
32150     preventDefault : false, 
32151     
32152     /**
32153      * @cfg {Boolean} inverse defalut false
32154      */   
32155     maskInverse : false, 
32156     
32157     getAutoCreate : function()
32158     {
32159         if(!this.isFitContainer){
32160             return this.getSplitAutoCreate();
32161         }
32162         
32163         var cls = 'masonry-brick masonry-brick-full';
32164         
32165         if(this.href.length){
32166             cls += ' masonry-brick-link';
32167         }
32168         
32169         if(this.bgimage.length){
32170             cls += ' masonry-brick-image';
32171         }
32172         
32173         if(this.maskInverse){
32174             cls += ' mask-inverse';
32175         }
32176         
32177         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32178             cls += ' enable-mask';
32179         }
32180         
32181         if(this.size){
32182             cls += ' masonry-' + this.size + '-brick';
32183         }
32184         
32185         if(this.placetitle.length){
32186             
32187             switch (this.placetitle) {
32188                 case 'center' :
32189                     cls += ' masonry-center-title';
32190                     break;
32191                 case 'bottom' :
32192                     cls += ' masonry-bottom-title';
32193                     break;
32194                 default:
32195                     break;
32196             }
32197             
32198         } else {
32199             if(!this.html.length && !this.bgimage.length){
32200                 cls += ' masonry-center-title';
32201             }
32202
32203             if(!this.html.length && this.bgimage.length){
32204                 cls += ' masonry-bottom-title';
32205             }
32206         }
32207         
32208         if(this.cls){
32209             cls += ' ' + this.cls;
32210         }
32211         
32212         var cfg = {
32213             tag: (this.href.length) ? 'a' : 'div',
32214             cls: cls,
32215             cn: [
32216                 {
32217                     tag: 'div',
32218                     cls: 'masonry-brick-mask'
32219                 },
32220                 {
32221                     tag: 'div',
32222                     cls: 'masonry-brick-paragraph',
32223                     cn: []
32224                 }
32225             ]
32226         };
32227         
32228         if(this.href.length){
32229             cfg.href = this.href;
32230         }
32231         
32232         var cn = cfg.cn[1].cn;
32233         
32234         if(this.title.length){
32235             cn.push({
32236                 tag: 'h4',
32237                 cls: 'masonry-brick-title',
32238                 html: this.title
32239             });
32240         }
32241         
32242         if(this.html.length){
32243             cn.push({
32244                 tag: 'p',
32245                 cls: 'masonry-brick-text',
32246                 html: this.html
32247             });
32248         }
32249         
32250         if (!this.title.length && !this.html.length) {
32251             cfg.cn[1].cls += ' hide';
32252         }
32253         
32254         if(this.bgimage.length){
32255             cfg.cn.push({
32256                 tag: 'img',
32257                 cls: 'masonry-brick-image-view',
32258                 src: this.bgimage
32259             });
32260         }
32261         
32262         if(this.videourl.length){
32263             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32264             // youtube support only?
32265             cfg.cn.push({
32266                 tag: 'iframe',
32267                 cls: 'masonry-brick-image-view',
32268                 src: vurl,
32269                 frameborder : 0,
32270                 allowfullscreen : true
32271             });
32272         }
32273         
32274         return cfg;
32275         
32276     },
32277     
32278     getSplitAutoCreate : function()
32279     {
32280         var cls = 'masonry-brick masonry-brick-split';
32281         
32282         if(this.href.length){
32283             cls += ' masonry-brick-link';
32284         }
32285         
32286         if(this.bgimage.length){
32287             cls += ' masonry-brick-image';
32288         }
32289         
32290         if(this.size){
32291             cls += ' masonry-' + this.size + '-brick';
32292         }
32293         
32294         switch (this.placetitle) {
32295             case 'center' :
32296                 cls += ' masonry-center-title';
32297                 break;
32298             case 'bottom' :
32299                 cls += ' masonry-bottom-title';
32300                 break;
32301             default:
32302                 if(!this.bgimage.length){
32303                     cls += ' masonry-center-title';
32304                 }
32305
32306                 if(this.bgimage.length){
32307                     cls += ' masonry-bottom-title';
32308                 }
32309                 break;
32310         }
32311         
32312         if(this.cls){
32313             cls += ' ' + this.cls;
32314         }
32315         
32316         var cfg = {
32317             tag: (this.href.length) ? 'a' : 'div',
32318             cls: cls,
32319             cn: [
32320                 {
32321                     tag: 'div',
32322                     cls: 'masonry-brick-split-head',
32323                     cn: [
32324                         {
32325                             tag: 'div',
32326                             cls: 'masonry-brick-paragraph',
32327                             cn: []
32328                         }
32329                     ]
32330                 },
32331                 {
32332                     tag: 'div',
32333                     cls: 'masonry-brick-split-body',
32334                     cn: []
32335                 }
32336             ]
32337         };
32338         
32339         if(this.href.length){
32340             cfg.href = this.href;
32341         }
32342         
32343         if(this.title.length){
32344             cfg.cn[0].cn[0].cn.push({
32345                 tag: 'h4',
32346                 cls: 'masonry-brick-title',
32347                 html: this.title
32348             });
32349         }
32350         
32351         if(this.html.length){
32352             cfg.cn[1].cn.push({
32353                 tag: 'p',
32354                 cls: 'masonry-brick-text',
32355                 html: this.html
32356             });
32357         }
32358
32359         if(this.bgimage.length){
32360             cfg.cn[0].cn.push({
32361                 tag: 'img',
32362                 cls: 'masonry-brick-image-view',
32363                 src: this.bgimage
32364             });
32365         }
32366         
32367         if(this.videourl.length){
32368             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32369             // youtube support only?
32370             cfg.cn[0].cn.cn.push({
32371                 tag: 'iframe',
32372                 cls: 'masonry-brick-image-view',
32373                 src: vurl,
32374                 frameborder : 0,
32375                 allowfullscreen : true
32376             });
32377         }
32378         
32379         return cfg;
32380     },
32381     
32382     initEvents: function() 
32383     {
32384         switch (this.size) {
32385             case 'xs' :
32386                 this.x = 1;
32387                 this.y = 1;
32388                 break;
32389             case 'sm' :
32390                 this.x = 2;
32391                 this.y = 2;
32392                 break;
32393             case 'md' :
32394             case 'md-left' :
32395             case 'md-right' :
32396                 this.x = 3;
32397                 this.y = 3;
32398                 break;
32399             case 'tall' :
32400                 this.x = 2;
32401                 this.y = 3;
32402                 break;
32403             case 'wide' :
32404                 this.x = 3;
32405                 this.y = 2;
32406                 break;
32407             case 'wide-thin' :
32408                 this.x = 3;
32409                 this.y = 1;
32410                 break;
32411                         
32412             default :
32413                 break;
32414         }
32415         
32416         if(Roo.isTouch){
32417             this.el.on('touchstart', this.onTouchStart, this);
32418             this.el.on('touchmove', this.onTouchMove, this);
32419             this.el.on('touchend', this.onTouchEnd, this);
32420             this.el.on('contextmenu', this.onContextMenu, this);
32421         } else {
32422             this.el.on('mouseenter'  ,this.enter, this);
32423             this.el.on('mouseleave', this.leave, this);
32424             this.el.on('click', this.onClick, this);
32425         }
32426         
32427         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32428             this.parent().bricks.push(this);   
32429         }
32430         
32431     },
32432     
32433     onClick: function(e, el)
32434     {
32435         var time = this.endTimer - this.startTimer;
32436         // Roo.log(e.preventDefault());
32437         if(Roo.isTouch){
32438             if(time > 1000){
32439                 e.preventDefault();
32440                 return;
32441             }
32442         }
32443         
32444         if(!this.preventDefault){
32445             return;
32446         }
32447         
32448         e.preventDefault();
32449         
32450         if (this.activcClass != '') {
32451             this.selectBrick();
32452         }
32453         
32454         this.fireEvent('click', this);
32455     },
32456     
32457     enter: function(e, el)
32458     {
32459         e.preventDefault();
32460         
32461         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32462             return;
32463         }
32464         
32465         if(this.bgimage.length && this.html.length){
32466             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32467         }
32468     },
32469     
32470     leave: function(e, el)
32471     {
32472         e.preventDefault();
32473         
32474         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32475             return;
32476         }
32477         
32478         if(this.bgimage.length && this.html.length){
32479             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32480         }
32481     },
32482     
32483     onTouchStart: function(e, el)
32484     {
32485 //        e.preventDefault();
32486         
32487         this.touchmoved = false;
32488         
32489         if(!this.isFitContainer){
32490             return;
32491         }
32492         
32493         if(!this.bgimage.length || !this.html.length){
32494             return;
32495         }
32496         
32497         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32498         
32499         this.timer = new Date().getTime();
32500         
32501     },
32502     
32503     onTouchMove: function(e, el)
32504     {
32505         this.touchmoved = true;
32506     },
32507     
32508     onContextMenu : function(e,el)
32509     {
32510         e.preventDefault();
32511         e.stopPropagation();
32512         return false;
32513     },
32514     
32515     onTouchEnd: function(e, el)
32516     {
32517 //        e.preventDefault();
32518         
32519         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32520         
32521             this.leave(e,el);
32522             
32523             return;
32524         }
32525         
32526         if(!this.bgimage.length || !this.html.length){
32527             
32528             if(this.href.length){
32529                 window.location.href = this.href;
32530             }
32531             
32532             return;
32533         }
32534         
32535         if(!this.isFitContainer){
32536             return;
32537         }
32538         
32539         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32540         
32541         window.location.href = this.href;
32542     },
32543     
32544     //selection on single brick only
32545     selectBrick : function() {
32546         
32547         if (!this.parentId) {
32548             return;
32549         }
32550         
32551         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32552         var index = m.selectedBrick.indexOf(this.id);
32553         
32554         if ( index > -1) {
32555             m.selectedBrick.splice(index,1);
32556             this.el.removeClass(this.activeClass);
32557             return;
32558         }
32559         
32560         for(var i = 0; i < m.selectedBrick.length; i++) {
32561             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32562             b.el.removeClass(b.activeClass);
32563         }
32564         
32565         m.selectedBrick = [];
32566         
32567         m.selectedBrick.push(this.id);
32568         this.el.addClass(this.activeClass);
32569         return;
32570     }
32571     
32572 });
32573
32574 Roo.apply(Roo.bootstrap.MasonryBrick, {
32575     
32576     //groups: {},
32577     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32578      /**
32579     * register a Masonry Brick
32580     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32581     */
32582     
32583     register : function(brick)
32584     {
32585         //this.groups[brick.id] = brick;
32586         this.groups.add(brick.id, brick);
32587     },
32588     /**
32589     * fetch a  masonry brick based on the masonry brick ID
32590     * @param {string} the masonry brick to add
32591     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32592     */
32593     
32594     get: function(brick_id) 
32595     {
32596         // if (typeof(this.groups[brick_id]) == 'undefined') {
32597         //     return false;
32598         // }
32599         // return this.groups[brick_id] ;
32600         
32601         if(this.groups.key(brick_id)) {
32602             return this.groups.key(brick_id);
32603         }
32604         
32605         return false;
32606     }
32607     
32608     
32609     
32610 });
32611
32612  /*
32613  * - LGPL
32614  *
32615  * element
32616  * 
32617  */
32618
32619 /**
32620  * @class Roo.bootstrap.Brick
32621  * @extends Roo.bootstrap.Component
32622  * Bootstrap Brick class
32623  * 
32624  * @constructor
32625  * Create a new Brick
32626  * @param {Object} config The config object
32627  */
32628
32629 Roo.bootstrap.Brick = function(config){
32630     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32631     
32632     this.addEvents({
32633         // raw events
32634         /**
32635          * @event click
32636          * When a Brick is click
32637          * @param {Roo.bootstrap.Brick} this
32638          * @param {Roo.EventObject} e
32639          */
32640         "click" : true
32641     });
32642 };
32643
32644 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32645     
32646     /**
32647      * @cfg {String} title
32648      */   
32649     title : '',
32650     /**
32651      * @cfg {String} html
32652      */   
32653     html : '',
32654     /**
32655      * @cfg {String} bgimage
32656      */   
32657     bgimage : '',
32658     /**
32659      * @cfg {String} cls
32660      */   
32661     cls : '',
32662     /**
32663      * @cfg {String} href
32664      */   
32665     href : '',
32666     /**
32667      * @cfg {String} video
32668      */   
32669     video : '',
32670     /**
32671      * @cfg {Boolean} square
32672      */   
32673     square : true,
32674     
32675     getAutoCreate : function()
32676     {
32677         var cls = 'roo-brick';
32678         
32679         if(this.href.length){
32680             cls += ' roo-brick-link';
32681         }
32682         
32683         if(this.bgimage.length){
32684             cls += ' roo-brick-image';
32685         }
32686         
32687         if(!this.html.length && !this.bgimage.length){
32688             cls += ' roo-brick-center-title';
32689         }
32690         
32691         if(!this.html.length && this.bgimage.length){
32692             cls += ' roo-brick-bottom-title';
32693         }
32694         
32695         if(this.cls){
32696             cls += ' ' + this.cls;
32697         }
32698         
32699         var cfg = {
32700             tag: (this.href.length) ? 'a' : 'div',
32701             cls: cls,
32702             cn: [
32703                 {
32704                     tag: 'div',
32705                     cls: 'roo-brick-paragraph',
32706                     cn: []
32707                 }
32708             ]
32709         };
32710         
32711         if(this.href.length){
32712             cfg.href = this.href;
32713         }
32714         
32715         var cn = cfg.cn[0].cn;
32716         
32717         if(this.title.length){
32718             cn.push({
32719                 tag: 'h4',
32720                 cls: 'roo-brick-title',
32721                 html: this.title
32722             });
32723         }
32724         
32725         if(this.html.length){
32726             cn.push({
32727                 tag: 'p',
32728                 cls: 'roo-brick-text',
32729                 html: this.html
32730             });
32731         } else {
32732             cn.cls += ' hide';
32733         }
32734         
32735         if(this.bgimage.length){
32736             cfg.cn.push({
32737                 tag: 'img',
32738                 cls: 'roo-brick-image-view',
32739                 src: this.bgimage
32740             });
32741         }
32742         
32743         return cfg;
32744     },
32745     
32746     initEvents: function() 
32747     {
32748         if(this.title.length || this.html.length){
32749             this.el.on('mouseenter'  ,this.enter, this);
32750             this.el.on('mouseleave', this.leave, this);
32751         }
32752         
32753         Roo.EventManager.onWindowResize(this.resize, this); 
32754         
32755         if(this.bgimage.length){
32756             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32757             this.imageEl.on('load', this.onImageLoad, this);
32758             return;
32759         }
32760         
32761         this.resize();
32762     },
32763     
32764     onImageLoad : function()
32765     {
32766         this.resize();
32767     },
32768     
32769     resize : function()
32770     {
32771         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32772         
32773         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32774         
32775         if(this.bgimage.length){
32776             var image = this.el.select('.roo-brick-image-view', true).first();
32777             
32778             image.setWidth(paragraph.getWidth());
32779             
32780             if(this.square){
32781                 image.setHeight(paragraph.getWidth());
32782             }
32783             
32784             this.el.setHeight(image.getHeight());
32785             paragraph.setHeight(image.getHeight());
32786             
32787         }
32788         
32789     },
32790     
32791     enter: function(e, el)
32792     {
32793         e.preventDefault();
32794         
32795         if(this.bgimage.length){
32796             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32797             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32798         }
32799     },
32800     
32801     leave: function(e, el)
32802     {
32803         e.preventDefault();
32804         
32805         if(this.bgimage.length){
32806             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32807             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32808         }
32809     }
32810     
32811 });
32812
32813  
32814
32815  /*
32816  * - LGPL
32817  *
32818  * Input
32819  * 
32820  */
32821
32822 /**
32823  * @class Roo.bootstrap.NumberField
32824  * @extends Roo.bootstrap.Input
32825  * Bootstrap NumberField class
32826  * 
32827  * 
32828  * 
32829  * 
32830  * @constructor
32831  * Create a new NumberField
32832  * @param {Object} config The config object
32833  */
32834
32835 Roo.bootstrap.NumberField = function(config){
32836     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32837 };
32838
32839 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32840     
32841     /**
32842      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32843      */
32844     allowDecimals : true,
32845     /**
32846      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32847      */
32848     decimalSeparator : ".",
32849     /**
32850      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32851      */
32852     decimalPrecision : 2,
32853     /**
32854      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32855      */
32856     allowNegative : true,
32857     /**
32858      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32859      */
32860     minValue : Number.NEGATIVE_INFINITY,
32861     /**
32862      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32863      */
32864     maxValue : Number.MAX_VALUE,
32865     /**
32866      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32867      */
32868     minText : "The minimum value for this field is {0}",
32869     /**
32870      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32871      */
32872     maxText : "The maximum value for this field is {0}",
32873     /**
32874      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32875      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32876      */
32877     nanText : "{0} is not a valid number",
32878     /**
32879      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32880      */
32881     castInt : true,
32882
32883     // private
32884     initEvents : function()
32885     {   
32886         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32887         
32888         var allowed = "0123456789";
32889         
32890         if(this.allowDecimals){
32891             allowed += this.decimalSeparator;
32892         }
32893         
32894         if(this.allowNegative){
32895             allowed += "-";
32896         }
32897         
32898         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32899         
32900         var keyPress = function(e){
32901             
32902             var k = e.getKey();
32903             
32904             var c = e.getCharCode();
32905             
32906             if(
32907                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32908                     allowed.indexOf(String.fromCharCode(c)) === -1
32909             ){
32910                 e.stopEvent();
32911                 return;
32912             }
32913             
32914             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32915                 return;
32916             }
32917             
32918             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32919                 e.stopEvent();
32920             }
32921         };
32922         
32923         this.el.on("keypress", keyPress, this);
32924     },
32925     
32926     validateValue : function(value)
32927     {
32928         
32929         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32930             return false;
32931         }
32932         
32933         var num = this.parseValue(value);
32934         
32935         if(isNaN(num)){
32936             this.markInvalid(String.format(this.nanText, value));
32937             return false;
32938         }
32939         
32940         if(num < this.minValue){
32941             this.markInvalid(String.format(this.minText, this.minValue));
32942             return false;
32943         }
32944         
32945         if(num > this.maxValue){
32946             this.markInvalid(String.format(this.maxText, this.maxValue));
32947             return false;
32948         }
32949         
32950         return true;
32951     },
32952
32953     getValue : function()
32954     {
32955         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32956     },
32957
32958     parseValue : function(value)
32959     {
32960         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32961         return isNaN(value) ? '' : value;
32962     },
32963
32964     fixPrecision : function(value)
32965     {
32966         var nan = isNaN(value);
32967         
32968         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32969             return nan ? '' : value;
32970         }
32971         return parseFloat(value).toFixed(this.decimalPrecision);
32972     },
32973
32974     setValue : function(v)
32975     {
32976         v = this.fixPrecision(v);
32977         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32978     },
32979
32980     decimalPrecisionFcn : function(v)
32981     {
32982         return Math.floor(v);
32983     },
32984
32985     beforeBlur : function()
32986     {
32987         if(!this.castInt){
32988             return;
32989         }
32990         
32991         var v = this.parseValue(this.getRawValue());
32992         if(v){
32993             this.setValue(v);
32994         }
32995     }
32996     
32997 });
32998
32999  
33000
33001 /*
33002 * Licence: LGPL
33003 */
33004
33005 /**
33006  * @class Roo.bootstrap.DocumentSlider
33007  * @extends Roo.bootstrap.Component
33008  * Bootstrap DocumentSlider class
33009  * 
33010  * @constructor
33011  * Create a new DocumentViewer
33012  * @param {Object} config The config object
33013  */
33014
33015 Roo.bootstrap.DocumentSlider = function(config){
33016     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33017     
33018     this.files = [];
33019     
33020     this.addEvents({
33021         /**
33022          * @event initial
33023          * Fire after initEvent
33024          * @param {Roo.bootstrap.DocumentSlider} this
33025          */
33026         "initial" : true,
33027         /**
33028          * @event update
33029          * Fire after update
33030          * @param {Roo.bootstrap.DocumentSlider} this
33031          */
33032         "update" : true,
33033         /**
33034          * @event click
33035          * Fire after click
33036          * @param {Roo.bootstrap.DocumentSlider} this
33037          */
33038         "click" : true
33039     });
33040 };
33041
33042 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33043     
33044     files : false,
33045     
33046     indicator : 0,
33047     
33048     getAutoCreate : function()
33049     {
33050         var cfg = {
33051             tag : 'div',
33052             cls : 'roo-document-slider',
33053             cn : [
33054                 {
33055                     tag : 'div',
33056                     cls : 'roo-document-slider-header',
33057                     cn : [
33058                         {
33059                             tag : 'div',
33060                             cls : 'roo-document-slider-header-title'
33061                         }
33062                     ]
33063                 },
33064                 {
33065                     tag : 'div',
33066                     cls : 'roo-document-slider-body',
33067                     cn : [
33068                         {
33069                             tag : 'div',
33070                             cls : 'roo-document-slider-prev',
33071                             cn : [
33072                                 {
33073                                     tag : 'i',
33074                                     cls : 'fa fa-chevron-left'
33075                                 }
33076                             ]
33077                         },
33078                         {
33079                             tag : 'div',
33080                             cls : 'roo-document-slider-thumb',
33081                             cn : [
33082                                 {
33083                                     tag : 'img',
33084                                     cls : 'roo-document-slider-image'
33085                                 }
33086                             ]
33087                         },
33088                         {
33089                             tag : 'div',
33090                             cls : 'roo-document-slider-next',
33091                             cn : [
33092                                 {
33093                                     tag : 'i',
33094                                     cls : 'fa fa-chevron-right'
33095                                 }
33096                             ]
33097                         }
33098                     ]
33099                 }
33100             ]
33101         };
33102         
33103         return cfg;
33104     },
33105     
33106     initEvents : function()
33107     {
33108         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33109         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33110         
33111         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33112         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33113         
33114         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33115         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33116         
33117         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33118         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33119         
33120         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33121         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33122         
33123         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33124         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33125         
33126         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33127         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33128         
33129         this.thumbEl.on('click', this.onClick, this);
33130         
33131         this.prevIndicator.on('click', this.prev, this);
33132         
33133         this.nextIndicator.on('click', this.next, this);
33134         
33135     },
33136     
33137     initial : function()
33138     {
33139         if(this.files.length){
33140             this.indicator = 1;
33141             this.update()
33142         }
33143         
33144         this.fireEvent('initial', this);
33145     },
33146     
33147     update : function()
33148     {
33149         this.imageEl.attr('src', this.files[this.indicator - 1]);
33150         
33151         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33152         
33153         this.prevIndicator.show();
33154         
33155         if(this.indicator == 1){
33156             this.prevIndicator.hide();
33157         }
33158         
33159         this.nextIndicator.show();
33160         
33161         if(this.indicator == this.files.length){
33162             this.nextIndicator.hide();
33163         }
33164         
33165         this.thumbEl.scrollTo('top');
33166         
33167         this.fireEvent('update', this);
33168     },
33169     
33170     onClick : function(e)
33171     {
33172         e.preventDefault();
33173         
33174         this.fireEvent('click', this);
33175     },
33176     
33177     prev : function(e)
33178     {
33179         e.preventDefault();
33180         
33181         this.indicator = Math.max(1, this.indicator - 1);
33182         
33183         this.update();
33184     },
33185     
33186     next : function(e)
33187     {
33188         e.preventDefault();
33189         
33190         this.indicator = Math.min(this.files.length, this.indicator + 1);
33191         
33192         this.update();
33193     }
33194 });
33195 /*
33196  * - LGPL
33197  *
33198  * RadioSet
33199  *
33200  *
33201  */
33202
33203 /**
33204  * @class Roo.bootstrap.RadioSet
33205  * @extends Roo.bootstrap.Input
33206  * Bootstrap RadioSet class
33207  * @cfg {String} indicatorpos (left|right) default left
33208  * @cfg {Boolean} inline (true|false) inline the element (default true)
33209  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33210  * @constructor
33211  * Create a new RadioSet
33212  * @param {Object} config The config object
33213  */
33214
33215 Roo.bootstrap.RadioSet = function(config){
33216     
33217     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33218     
33219     this.radioes = [];
33220     
33221     Roo.bootstrap.RadioSet.register(this);
33222     
33223     this.addEvents({
33224         /**
33225         * @event check
33226         * Fires when the element is checked or unchecked.
33227         * @param {Roo.bootstrap.RadioSet} this This radio
33228         * @param {Roo.bootstrap.Radio} item The checked item
33229         */
33230        check : true
33231     });
33232     
33233 };
33234
33235 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33236
33237     radioes : false,
33238     
33239     inline : true,
33240     
33241     weight : '',
33242     
33243     indicatorpos : 'left',
33244     
33245     getAutoCreate : function()
33246     {
33247         var label = {
33248             tag : 'label',
33249             cls : 'roo-radio-set-label',
33250             cn : [
33251                 {
33252                     tag : 'span',
33253                     html : this.fieldLabel
33254                 }
33255             ]
33256         };
33257         
33258         if(this.indicatorpos == 'left'){
33259             label.cn.unshift({
33260                 tag : 'i',
33261                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33262                 tooltip : 'This field is required'
33263             });
33264         } else {
33265             label.cn.push({
33266                 tag : 'i',
33267                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33268                 tooltip : 'This field is required'
33269             });
33270         }
33271         
33272         var items = {
33273             tag : 'div',
33274             cls : 'roo-radio-set-items'
33275         };
33276         
33277         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33278         
33279         if (align === 'left' && this.fieldLabel.length) {
33280             
33281             items = {
33282                 cls : "roo-radio-set-right", 
33283                 cn: [
33284                     items
33285                 ]
33286             };
33287             
33288             if(this.labelWidth > 12){
33289                 label.style = "width: " + this.labelWidth + 'px';
33290             }
33291             
33292             if(this.labelWidth < 13 && this.labelmd == 0){
33293                 this.labelmd = this.labelWidth;
33294             }
33295             
33296             if(this.labellg > 0){
33297                 label.cls += ' col-lg-' + this.labellg;
33298                 items.cls += ' col-lg-' + (12 - this.labellg);
33299             }
33300             
33301             if(this.labelmd > 0){
33302                 label.cls += ' col-md-' + this.labelmd;
33303                 items.cls += ' col-md-' + (12 - this.labelmd);
33304             }
33305             
33306             if(this.labelsm > 0){
33307                 label.cls += ' col-sm-' + this.labelsm;
33308                 items.cls += ' col-sm-' + (12 - this.labelsm);
33309             }
33310             
33311             if(this.labelxs > 0){
33312                 label.cls += ' col-xs-' + this.labelxs;
33313                 items.cls += ' col-xs-' + (12 - this.labelxs);
33314             }
33315         }
33316         
33317         var cfg = {
33318             tag : 'div',
33319             cls : 'roo-radio-set',
33320             cn : [
33321                 {
33322                     tag : 'input',
33323                     cls : 'roo-radio-set-input',
33324                     type : 'hidden',
33325                     name : this.name,
33326                     value : this.value ? this.value :  ''
33327                 },
33328                 label,
33329                 items
33330             ]
33331         };
33332         
33333         if(this.weight.length){
33334             cfg.cls += ' roo-radio-' + this.weight;
33335         }
33336         
33337         if(this.inline) {
33338             cfg.cls += ' roo-radio-set-inline';
33339         }
33340         
33341         var settings=this;
33342         ['xs','sm','md','lg'].map(function(size){
33343             if (settings[size]) {
33344                 cfg.cls += ' col-' + size + '-' + settings[size];
33345             }
33346         });
33347         
33348         return cfg;
33349         
33350     },
33351
33352     initEvents : function()
33353     {
33354         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33355         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33356         
33357         if(!this.fieldLabel.length){
33358             this.labelEl.hide();
33359         }
33360         
33361         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33362         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33363         
33364         this.indicatorEl().addClass('invisible');
33365         
33366         this.originalValue = this.getValue();
33367         
33368     },
33369     
33370     inputEl: function ()
33371     {
33372         return this.el.select('.roo-radio-set-input', true).first();
33373     },
33374     
33375     getChildContainer : function()
33376     {
33377         return this.itemsEl;
33378     },
33379     
33380     register : function(item)
33381     {
33382         this.radioes.push(item);
33383         
33384     },
33385     
33386     validate : function()
33387     {   
33388         var valid = false;
33389         
33390         Roo.each(this.radioes, function(i){
33391             if(!i.checked){
33392                 return;
33393             }
33394             
33395             valid = true;
33396             return false;
33397         });
33398         
33399         if(this.allowBlank) {
33400             return true;
33401         }
33402         
33403         if(this.disabled || valid){
33404             this.markValid();
33405             return true;
33406         }
33407         
33408         this.markInvalid();
33409         return false;
33410         
33411     },
33412     
33413     markValid : function()
33414     {
33415         if(this.labelEl.isVisible(true)){
33416             this.indicatorEl().removeClass('visible');
33417             this.indicatorEl().addClass('invisible');
33418         }
33419         
33420         this.el.removeClass([this.invalidClass, this.validClass]);
33421         this.el.addClass(this.validClass);
33422         
33423         this.fireEvent('valid', this);
33424     },
33425     
33426     markInvalid : function(msg)
33427     {
33428         if(this.allowBlank || this.disabled){
33429             return;
33430         }
33431         
33432         if(this.labelEl.isVisible(true)){
33433             this.indicatorEl().removeClass('invisible');
33434             this.indicatorEl().addClass('visible');
33435         }
33436         
33437         this.el.removeClass([this.invalidClass, this.validClass]);
33438         this.el.addClass(this.invalidClass);
33439         
33440         this.fireEvent('invalid', this, msg);
33441         
33442     },
33443     
33444     setValue : function(v, suppressEvent)
33445     {   
33446         this.value = v;
33447         if(this.rendered){
33448             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33449         }
33450         
33451         Roo.each(this.radioes, function(i){
33452             
33453             i.checked = false;
33454             i.el.removeClass('checked');
33455             
33456             if(i.value === v || i.value.toString() === v.toString()){
33457                 i.checked = true;
33458                 i.el.addClass('checked');
33459                 
33460                 if(suppressEvent !== true){
33461                     this.fireEvent('check', this, i);
33462                 }
33463             }
33464             
33465         }, this);
33466         
33467         this.validate();
33468     },
33469     
33470     clearInvalid : function(){
33471         
33472         if(!this.el || this.preventMark){
33473             return;
33474         }
33475         
33476         this.el.removeClass([this.invalidClass]);
33477         
33478         this.fireEvent('valid', this);
33479     }
33480     
33481 });
33482
33483 Roo.apply(Roo.bootstrap.RadioSet, {
33484     
33485     groups: {},
33486     
33487     register : function(set)
33488     {
33489         this.groups[set.name] = set;
33490     },
33491     
33492     get: function(name) 
33493     {
33494         if (typeof(this.groups[name]) == 'undefined') {
33495             return false;
33496         }
33497         
33498         return this.groups[name] ;
33499     }
33500     
33501 });
33502 /*
33503  * Based on:
33504  * Ext JS Library 1.1.1
33505  * Copyright(c) 2006-2007, Ext JS, LLC.
33506  *
33507  * Originally Released Under LGPL - original licence link has changed is not relivant.
33508  *
33509  * Fork - LGPL
33510  * <script type="text/javascript">
33511  */
33512
33513
33514 /**
33515  * @class Roo.bootstrap.SplitBar
33516  * @extends Roo.util.Observable
33517  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33518  * <br><br>
33519  * Usage:
33520  * <pre><code>
33521 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33522                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33523 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33524 split.minSize = 100;
33525 split.maxSize = 600;
33526 split.animate = true;
33527 split.on('moved', splitterMoved);
33528 </code></pre>
33529  * @constructor
33530  * Create a new SplitBar
33531  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33532  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33533  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33534  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33535                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33536                         position of the SplitBar).
33537  */
33538 Roo.bootstrap.SplitBar = function(cfg){
33539     
33540     /** @private */
33541     
33542     //{
33543     //  dragElement : elm
33544     //  resizingElement: el,
33545         // optional..
33546     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33547     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33548         // existingProxy ???
33549     //}
33550     
33551     this.el = Roo.get(cfg.dragElement, true);
33552     this.el.dom.unselectable = "on";
33553     /** @private */
33554     this.resizingEl = Roo.get(cfg.resizingElement, true);
33555
33556     /**
33557      * @private
33558      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33559      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33560      * @type Number
33561      */
33562     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33563     
33564     /**
33565      * The minimum size of the resizing element. (Defaults to 0)
33566      * @type Number
33567      */
33568     this.minSize = 0;
33569     
33570     /**
33571      * The maximum size of the resizing element. (Defaults to 2000)
33572      * @type Number
33573      */
33574     this.maxSize = 2000;
33575     
33576     /**
33577      * Whether to animate the transition to the new size
33578      * @type Boolean
33579      */
33580     this.animate = false;
33581     
33582     /**
33583      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33584      * @type Boolean
33585      */
33586     this.useShim = false;
33587     
33588     /** @private */
33589     this.shim = null;
33590     
33591     if(!cfg.existingProxy){
33592         /** @private */
33593         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33594     }else{
33595         this.proxy = Roo.get(cfg.existingProxy).dom;
33596     }
33597     /** @private */
33598     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33599     
33600     /** @private */
33601     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33602     
33603     /** @private */
33604     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33605     
33606     /** @private */
33607     this.dragSpecs = {};
33608     
33609     /**
33610      * @private The adapter to use to positon and resize elements
33611      */
33612     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33613     this.adapter.init(this);
33614     
33615     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33616         /** @private */
33617         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33618         this.el.addClass("roo-splitbar-h");
33619     }else{
33620         /** @private */
33621         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33622         this.el.addClass("roo-splitbar-v");
33623     }
33624     
33625     this.addEvents({
33626         /**
33627          * @event resize
33628          * Fires when the splitter is moved (alias for {@link #event-moved})
33629          * @param {Roo.bootstrap.SplitBar} this
33630          * @param {Number} newSize the new width or height
33631          */
33632         "resize" : true,
33633         /**
33634          * @event moved
33635          * Fires when the splitter is moved
33636          * @param {Roo.bootstrap.SplitBar} this
33637          * @param {Number} newSize the new width or height
33638          */
33639         "moved" : true,
33640         /**
33641          * @event beforeresize
33642          * Fires before the splitter is dragged
33643          * @param {Roo.bootstrap.SplitBar} this
33644          */
33645         "beforeresize" : true,
33646
33647         "beforeapply" : true
33648     });
33649
33650     Roo.util.Observable.call(this);
33651 };
33652
33653 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33654     onStartProxyDrag : function(x, y){
33655         this.fireEvent("beforeresize", this);
33656         if(!this.overlay){
33657             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33658             o.unselectable();
33659             o.enableDisplayMode("block");
33660             // all splitbars share the same overlay
33661             Roo.bootstrap.SplitBar.prototype.overlay = o;
33662         }
33663         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33664         this.overlay.show();
33665         Roo.get(this.proxy).setDisplayed("block");
33666         var size = this.adapter.getElementSize(this);
33667         this.activeMinSize = this.getMinimumSize();;
33668         this.activeMaxSize = this.getMaximumSize();;
33669         var c1 = size - this.activeMinSize;
33670         var c2 = Math.max(this.activeMaxSize - size, 0);
33671         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33672             this.dd.resetConstraints();
33673             this.dd.setXConstraint(
33674                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33675                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33676             );
33677             this.dd.setYConstraint(0, 0);
33678         }else{
33679             this.dd.resetConstraints();
33680             this.dd.setXConstraint(0, 0);
33681             this.dd.setYConstraint(
33682                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33683                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33684             );
33685          }
33686         this.dragSpecs.startSize = size;
33687         this.dragSpecs.startPoint = [x, y];
33688         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33689     },
33690     
33691     /** 
33692      * @private Called after the drag operation by the DDProxy
33693      */
33694     onEndProxyDrag : function(e){
33695         Roo.get(this.proxy).setDisplayed(false);
33696         var endPoint = Roo.lib.Event.getXY(e);
33697         if(this.overlay){
33698             this.overlay.hide();
33699         }
33700         var newSize;
33701         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33702             newSize = this.dragSpecs.startSize + 
33703                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33704                     endPoint[0] - this.dragSpecs.startPoint[0] :
33705                     this.dragSpecs.startPoint[0] - endPoint[0]
33706                 );
33707         }else{
33708             newSize = this.dragSpecs.startSize + 
33709                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33710                     endPoint[1] - this.dragSpecs.startPoint[1] :
33711                     this.dragSpecs.startPoint[1] - endPoint[1]
33712                 );
33713         }
33714         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33715         if(newSize != this.dragSpecs.startSize){
33716             if(this.fireEvent('beforeapply', this, newSize) !== false){
33717                 this.adapter.setElementSize(this, newSize);
33718                 this.fireEvent("moved", this, newSize);
33719                 this.fireEvent("resize", this, newSize);
33720             }
33721         }
33722     },
33723     
33724     /**
33725      * Get the adapter this SplitBar uses
33726      * @return The adapter object
33727      */
33728     getAdapter : function(){
33729         return this.adapter;
33730     },
33731     
33732     /**
33733      * Set the adapter this SplitBar uses
33734      * @param {Object} adapter A SplitBar adapter object
33735      */
33736     setAdapter : function(adapter){
33737         this.adapter = adapter;
33738         this.adapter.init(this);
33739     },
33740     
33741     /**
33742      * Gets the minimum size for the resizing element
33743      * @return {Number} The minimum size
33744      */
33745     getMinimumSize : function(){
33746         return this.minSize;
33747     },
33748     
33749     /**
33750      * Sets the minimum size for the resizing element
33751      * @param {Number} minSize The minimum size
33752      */
33753     setMinimumSize : function(minSize){
33754         this.minSize = minSize;
33755     },
33756     
33757     /**
33758      * Gets the maximum size for the resizing element
33759      * @return {Number} The maximum size
33760      */
33761     getMaximumSize : function(){
33762         return this.maxSize;
33763     },
33764     
33765     /**
33766      * Sets the maximum size for the resizing element
33767      * @param {Number} maxSize The maximum size
33768      */
33769     setMaximumSize : function(maxSize){
33770         this.maxSize = maxSize;
33771     },
33772     
33773     /**
33774      * Sets the initialize size for the resizing element
33775      * @param {Number} size The initial size
33776      */
33777     setCurrentSize : function(size){
33778         var oldAnimate = this.animate;
33779         this.animate = false;
33780         this.adapter.setElementSize(this, size);
33781         this.animate = oldAnimate;
33782     },
33783     
33784     /**
33785      * Destroy this splitbar. 
33786      * @param {Boolean} removeEl True to remove the element
33787      */
33788     destroy : function(removeEl){
33789         if(this.shim){
33790             this.shim.remove();
33791         }
33792         this.dd.unreg();
33793         this.proxy.parentNode.removeChild(this.proxy);
33794         if(removeEl){
33795             this.el.remove();
33796         }
33797     }
33798 });
33799
33800 /**
33801  * @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.
33802  */
33803 Roo.bootstrap.SplitBar.createProxy = function(dir){
33804     var proxy = new Roo.Element(document.createElement("div"));
33805     proxy.unselectable();
33806     var cls = 'roo-splitbar-proxy';
33807     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33808     document.body.appendChild(proxy.dom);
33809     return proxy.dom;
33810 };
33811
33812 /** 
33813  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33814  * Default Adapter. It assumes the splitter and resizing element are not positioned
33815  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33816  */
33817 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33818 };
33819
33820 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33821     // do nothing for now
33822     init : function(s){
33823     
33824     },
33825     /**
33826      * Called before drag operations to get the current size of the resizing element. 
33827      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33828      */
33829      getElementSize : function(s){
33830         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33831             return s.resizingEl.getWidth();
33832         }else{
33833             return s.resizingEl.getHeight();
33834         }
33835     },
33836     
33837     /**
33838      * Called after drag operations to set the size of the resizing element.
33839      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33840      * @param {Number} newSize The new size to set
33841      * @param {Function} onComplete A function to be invoked when resizing is complete
33842      */
33843     setElementSize : function(s, newSize, onComplete){
33844         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33845             if(!s.animate){
33846                 s.resizingEl.setWidth(newSize);
33847                 if(onComplete){
33848                     onComplete(s, newSize);
33849                 }
33850             }else{
33851                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33852             }
33853         }else{
33854             
33855             if(!s.animate){
33856                 s.resizingEl.setHeight(newSize);
33857                 if(onComplete){
33858                     onComplete(s, newSize);
33859                 }
33860             }else{
33861                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33862             }
33863         }
33864     }
33865 };
33866
33867 /** 
33868  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33869  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33870  * Adapter that  moves the splitter element to align with the resized sizing element. 
33871  * Used with an absolute positioned SplitBar.
33872  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33873  * document.body, make sure you assign an id to the body element.
33874  */
33875 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33876     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33877     this.container = Roo.get(container);
33878 };
33879
33880 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33881     init : function(s){
33882         this.basic.init(s);
33883     },
33884     
33885     getElementSize : function(s){
33886         return this.basic.getElementSize(s);
33887     },
33888     
33889     setElementSize : function(s, newSize, onComplete){
33890         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33891     },
33892     
33893     moveSplitter : function(s){
33894         var yes = Roo.bootstrap.SplitBar;
33895         switch(s.placement){
33896             case yes.LEFT:
33897                 s.el.setX(s.resizingEl.getRight());
33898                 break;
33899             case yes.RIGHT:
33900                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33901                 break;
33902             case yes.TOP:
33903                 s.el.setY(s.resizingEl.getBottom());
33904                 break;
33905             case yes.BOTTOM:
33906                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33907                 break;
33908         }
33909     }
33910 };
33911
33912 /**
33913  * Orientation constant - Create a vertical SplitBar
33914  * @static
33915  * @type Number
33916  */
33917 Roo.bootstrap.SplitBar.VERTICAL = 1;
33918
33919 /**
33920  * Orientation constant - Create a horizontal SplitBar
33921  * @static
33922  * @type Number
33923  */
33924 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33925
33926 /**
33927  * Placement constant - The resizing element is to the left of the splitter element
33928  * @static
33929  * @type Number
33930  */
33931 Roo.bootstrap.SplitBar.LEFT = 1;
33932
33933 /**
33934  * Placement constant - The resizing element is to the right of the splitter element
33935  * @static
33936  * @type Number
33937  */
33938 Roo.bootstrap.SplitBar.RIGHT = 2;
33939
33940 /**
33941  * Placement constant - The resizing element is positioned above the splitter element
33942  * @static
33943  * @type Number
33944  */
33945 Roo.bootstrap.SplitBar.TOP = 3;
33946
33947 /**
33948  * Placement constant - The resizing element is positioned under splitter element
33949  * @static
33950  * @type Number
33951  */
33952 Roo.bootstrap.SplitBar.BOTTOM = 4;
33953 Roo.namespace("Roo.bootstrap.layout");/*
33954  * Based on:
33955  * Ext JS Library 1.1.1
33956  * Copyright(c) 2006-2007, Ext JS, LLC.
33957  *
33958  * Originally Released Under LGPL - original licence link has changed is not relivant.
33959  *
33960  * Fork - LGPL
33961  * <script type="text/javascript">
33962  */
33963
33964 /**
33965  * @class Roo.bootstrap.layout.Manager
33966  * @extends Roo.bootstrap.Component
33967  * Base class for layout managers.
33968  */
33969 Roo.bootstrap.layout.Manager = function(config)
33970 {
33971     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33972
33973
33974
33975
33976
33977     /** false to disable window resize monitoring @type Boolean */
33978     this.monitorWindowResize = true;
33979     this.regions = {};
33980     this.addEvents({
33981         /**
33982          * @event layout
33983          * Fires when a layout is performed.
33984          * @param {Roo.LayoutManager} this
33985          */
33986         "layout" : true,
33987         /**
33988          * @event regionresized
33989          * Fires when the user resizes a region.
33990          * @param {Roo.LayoutRegion} region The resized region
33991          * @param {Number} newSize The new size (width for east/west, height for north/south)
33992          */
33993         "regionresized" : true,
33994         /**
33995          * @event regioncollapsed
33996          * Fires when a region is collapsed.
33997          * @param {Roo.LayoutRegion} region The collapsed region
33998          */
33999         "regioncollapsed" : true,
34000         /**
34001          * @event regionexpanded
34002          * Fires when a region is expanded.
34003          * @param {Roo.LayoutRegion} region The expanded region
34004          */
34005         "regionexpanded" : true
34006     });
34007     this.updating = false;
34008
34009     if (config.el) {
34010         this.el = Roo.get(config.el);
34011         this.initEvents();
34012     }
34013
34014 };
34015
34016 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34017
34018
34019     regions : null,
34020
34021     monitorWindowResize : true,
34022
34023
34024     updating : false,
34025
34026
34027     onRender : function(ct, position)
34028     {
34029         if(!this.el){
34030             this.el = Roo.get(ct);
34031             this.initEvents();
34032         }
34033         //this.fireEvent('render',this);
34034     },
34035
34036
34037     initEvents: function()
34038     {
34039
34040
34041         // ie scrollbar fix
34042         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34043             document.body.scroll = "no";
34044         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34045             this.el.position('relative');
34046         }
34047         this.id = this.el.id;
34048         this.el.addClass("roo-layout-container");
34049         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34050         if(this.el.dom != document.body ) {
34051             this.el.on('resize', this.layout,this);
34052             this.el.on('show', this.layout,this);
34053         }
34054
34055     },
34056
34057     /**
34058      * Returns true if this layout is currently being updated
34059      * @return {Boolean}
34060      */
34061     isUpdating : function(){
34062         return this.updating;
34063     },
34064
34065     /**
34066      * Suspend the LayoutManager from doing auto-layouts while
34067      * making multiple add or remove calls
34068      */
34069     beginUpdate : function(){
34070         this.updating = true;
34071     },
34072
34073     /**
34074      * Restore auto-layouts and optionally disable the manager from performing a layout
34075      * @param {Boolean} noLayout true to disable a layout update
34076      */
34077     endUpdate : function(noLayout){
34078         this.updating = false;
34079         if(!noLayout){
34080             this.layout();
34081         }
34082     },
34083
34084     layout: function(){
34085         // abstract...
34086     },
34087
34088     onRegionResized : function(region, newSize){
34089         this.fireEvent("regionresized", region, newSize);
34090         this.layout();
34091     },
34092
34093     onRegionCollapsed : function(region){
34094         this.fireEvent("regioncollapsed", region);
34095     },
34096
34097     onRegionExpanded : function(region){
34098         this.fireEvent("regionexpanded", region);
34099     },
34100
34101     /**
34102      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34103      * performs box-model adjustments.
34104      * @return {Object} The size as an object {width: (the width), height: (the height)}
34105      */
34106     getViewSize : function()
34107     {
34108         var size;
34109         if(this.el.dom != document.body){
34110             size = this.el.getSize();
34111         }else{
34112             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34113         }
34114         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34115         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34116         return size;
34117     },
34118
34119     /**
34120      * Returns the Element this layout is bound to.
34121      * @return {Roo.Element}
34122      */
34123     getEl : function(){
34124         return this.el;
34125     },
34126
34127     /**
34128      * Returns the specified region.
34129      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34130      * @return {Roo.LayoutRegion}
34131      */
34132     getRegion : function(target){
34133         return this.regions[target.toLowerCase()];
34134     },
34135
34136     onWindowResize : function(){
34137         if(this.monitorWindowResize){
34138             this.layout();
34139         }
34140     }
34141 });
34142 /*
34143  * Based on:
34144  * Ext JS Library 1.1.1
34145  * Copyright(c) 2006-2007, Ext JS, LLC.
34146  *
34147  * Originally Released Under LGPL - original licence link has changed is not relivant.
34148  *
34149  * Fork - LGPL
34150  * <script type="text/javascript">
34151  */
34152 /**
34153  * @class Roo.bootstrap.layout.Border
34154  * @extends Roo.bootstrap.layout.Manager
34155  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34156  * please see: examples/bootstrap/nested.html<br><br>
34157  
34158 <b>The container the layout is rendered into can be either the body element or any other element.
34159 If it is not the body element, the container needs to either be an absolute positioned element,
34160 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34161 the container size if it is not the body element.</b>
34162
34163 * @constructor
34164 * Create a new Border
34165 * @param {Object} config Configuration options
34166  */
34167 Roo.bootstrap.layout.Border = function(config){
34168     config = config || {};
34169     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34170     
34171     
34172     
34173     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34174         if(config[region]){
34175             config[region].region = region;
34176             this.addRegion(config[region]);
34177         }
34178     },this);
34179     
34180 };
34181
34182 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34183
34184 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34185     /**
34186      * Creates and adds a new region if it doesn't already exist.
34187      * @param {String} target The target region key (north, south, east, west or center).
34188      * @param {Object} config The regions config object
34189      * @return {BorderLayoutRegion} The new region
34190      */
34191     addRegion : function(config)
34192     {
34193         if(!this.regions[config.region]){
34194             var r = this.factory(config);
34195             this.bindRegion(r);
34196         }
34197         return this.regions[config.region];
34198     },
34199
34200     // private (kinda)
34201     bindRegion : function(r){
34202         this.regions[r.config.region] = r;
34203         
34204         r.on("visibilitychange",    this.layout, this);
34205         r.on("paneladded",          this.layout, this);
34206         r.on("panelremoved",        this.layout, this);
34207         r.on("invalidated",         this.layout, this);
34208         r.on("resized",             this.onRegionResized, this);
34209         r.on("collapsed",           this.onRegionCollapsed, this);
34210         r.on("expanded",            this.onRegionExpanded, this);
34211     },
34212
34213     /**
34214      * Performs a layout update.
34215      */
34216     layout : function()
34217     {
34218         if(this.updating) {
34219             return;
34220         }
34221         
34222         // render all the rebions if they have not been done alreayd?
34223         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34224             if(this.regions[region] && !this.regions[region].bodyEl){
34225                 this.regions[region].onRender(this.el)
34226             }
34227         },this);
34228         
34229         var size = this.getViewSize();
34230         var w = size.width;
34231         var h = size.height;
34232         var centerW = w;
34233         var centerH = h;
34234         var centerY = 0;
34235         var centerX = 0;
34236         //var x = 0, y = 0;
34237
34238         var rs = this.regions;
34239         var north = rs["north"];
34240         var south = rs["south"]; 
34241         var west = rs["west"];
34242         var east = rs["east"];
34243         var center = rs["center"];
34244         //if(this.hideOnLayout){ // not supported anymore
34245             //c.el.setStyle("display", "none");
34246         //}
34247         if(north && north.isVisible()){
34248             var b = north.getBox();
34249             var m = north.getMargins();
34250             b.width = w - (m.left+m.right);
34251             b.x = m.left;
34252             b.y = m.top;
34253             centerY = b.height + b.y + m.bottom;
34254             centerH -= centerY;
34255             north.updateBox(this.safeBox(b));
34256         }
34257         if(south && south.isVisible()){
34258             var b = south.getBox();
34259             var m = south.getMargins();
34260             b.width = w - (m.left+m.right);
34261             b.x = m.left;
34262             var totalHeight = (b.height + m.top + m.bottom);
34263             b.y = h - totalHeight + m.top;
34264             centerH -= totalHeight;
34265             south.updateBox(this.safeBox(b));
34266         }
34267         if(west && west.isVisible()){
34268             var b = west.getBox();
34269             var m = west.getMargins();
34270             b.height = centerH - (m.top+m.bottom);
34271             b.x = m.left;
34272             b.y = centerY + m.top;
34273             var totalWidth = (b.width + m.left + m.right);
34274             centerX += totalWidth;
34275             centerW -= totalWidth;
34276             west.updateBox(this.safeBox(b));
34277         }
34278         if(east && east.isVisible()){
34279             var b = east.getBox();
34280             var m = east.getMargins();
34281             b.height = centerH - (m.top+m.bottom);
34282             var totalWidth = (b.width + m.left + m.right);
34283             b.x = w - totalWidth + m.left;
34284             b.y = centerY + m.top;
34285             centerW -= totalWidth;
34286             east.updateBox(this.safeBox(b));
34287         }
34288         if(center){
34289             var m = center.getMargins();
34290             var centerBox = {
34291                 x: centerX + m.left,
34292                 y: centerY + m.top,
34293                 width: centerW - (m.left+m.right),
34294                 height: centerH - (m.top+m.bottom)
34295             };
34296             //if(this.hideOnLayout){
34297                 //center.el.setStyle("display", "block");
34298             //}
34299             center.updateBox(this.safeBox(centerBox));
34300         }
34301         this.el.repaint();
34302         this.fireEvent("layout", this);
34303     },
34304
34305     // private
34306     safeBox : function(box){
34307         box.width = Math.max(0, box.width);
34308         box.height = Math.max(0, box.height);
34309         return box;
34310     },
34311
34312     /**
34313      * Adds a ContentPanel (or subclass) to this layout.
34314      * @param {String} target The target region key (north, south, east, west or center).
34315      * @param {Roo.ContentPanel} panel The panel to add
34316      * @return {Roo.ContentPanel} The added panel
34317      */
34318     add : function(target, panel){
34319          
34320         target = target.toLowerCase();
34321         return this.regions[target].add(panel);
34322     },
34323
34324     /**
34325      * Remove a ContentPanel (or subclass) to this layout.
34326      * @param {String} target The target region key (north, south, east, west or center).
34327      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34328      * @return {Roo.ContentPanel} The removed panel
34329      */
34330     remove : function(target, panel){
34331         target = target.toLowerCase();
34332         return this.regions[target].remove(panel);
34333     },
34334
34335     /**
34336      * Searches all regions for a panel with the specified id
34337      * @param {String} panelId
34338      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34339      */
34340     findPanel : function(panelId){
34341         var rs = this.regions;
34342         for(var target in rs){
34343             if(typeof rs[target] != "function"){
34344                 var p = rs[target].getPanel(panelId);
34345                 if(p){
34346                     return p;
34347                 }
34348             }
34349         }
34350         return null;
34351     },
34352
34353     /**
34354      * Searches all regions for a panel with the specified id and activates (shows) it.
34355      * @param {String/ContentPanel} panelId The panels id or the panel itself
34356      * @return {Roo.ContentPanel} The shown panel or null
34357      */
34358     showPanel : function(panelId) {
34359       var rs = this.regions;
34360       for(var target in rs){
34361          var r = rs[target];
34362          if(typeof r != "function"){
34363             if(r.hasPanel(panelId)){
34364                return r.showPanel(panelId);
34365             }
34366          }
34367       }
34368       return null;
34369    },
34370
34371    /**
34372      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34373      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34374      */
34375    /*
34376     restoreState : function(provider){
34377         if(!provider){
34378             provider = Roo.state.Manager;
34379         }
34380         var sm = new Roo.LayoutStateManager();
34381         sm.init(this, provider);
34382     },
34383 */
34384  
34385  
34386     /**
34387      * Adds a xtype elements to the layout.
34388      * <pre><code>
34389
34390 layout.addxtype({
34391        xtype : 'ContentPanel',
34392        region: 'west',
34393        items: [ .... ]
34394    }
34395 );
34396
34397 layout.addxtype({
34398         xtype : 'NestedLayoutPanel',
34399         region: 'west',
34400         layout: {
34401            center: { },
34402            west: { }   
34403         },
34404         items : [ ... list of content panels or nested layout panels.. ]
34405    }
34406 );
34407 </code></pre>
34408      * @param {Object} cfg Xtype definition of item to add.
34409      */
34410     addxtype : function(cfg)
34411     {
34412         // basically accepts a pannel...
34413         // can accept a layout region..!?!?
34414         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34415         
34416         
34417         // theory?  children can only be panels??
34418         
34419         //if (!cfg.xtype.match(/Panel$/)) {
34420         //    return false;
34421         //}
34422         var ret = false;
34423         
34424         if (typeof(cfg.region) == 'undefined') {
34425             Roo.log("Failed to add Panel, region was not set");
34426             Roo.log(cfg);
34427             return false;
34428         }
34429         var region = cfg.region;
34430         delete cfg.region;
34431         
34432           
34433         var xitems = [];
34434         if (cfg.items) {
34435             xitems = cfg.items;
34436             delete cfg.items;
34437         }
34438         var nb = false;
34439         
34440         switch(cfg.xtype) 
34441         {
34442             case 'Content':  // ContentPanel (el, cfg)
34443             case 'Scroll':  // ContentPanel (el, cfg)
34444             case 'View': 
34445                 cfg.autoCreate = true;
34446                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34447                 //} else {
34448                 //    var el = this.el.createChild();
34449                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34450                 //}
34451                 
34452                 this.add(region, ret);
34453                 break;
34454             
34455             /*
34456             case 'TreePanel': // our new panel!
34457                 cfg.el = this.el.createChild();
34458                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34459                 this.add(region, ret);
34460                 break;
34461             */
34462             
34463             case 'Nest': 
34464                 // create a new Layout (which is  a Border Layout...
34465                 
34466                 var clayout = cfg.layout;
34467                 clayout.el  = this.el.createChild();
34468                 clayout.items   = clayout.items  || [];
34469                 
34470                 delete cfg.layout;
34471                 
34472                 // replace this exitems with the clayout ones..
34473                 xitems = clayout.items;
34474                  
34475                 // force background off if it's in center...
34476                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34477                     cfg.background = false;
34478                 }
34479                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34480                 
34481                 
34482                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34483                 //console.log('adding nested layout panel '  + cfg.toSource());
34484                 this.add(region, ret);
34485                 nb = {}; /// find first...
34486                 break;
34487             
34488             case 'Grid':
34489                 
34490                 // needs grid and region
34491                 
34492                 //var el = this.getRegion(region).el.createChild();
34493                 /*
34494                  *var el = this.el.createChild();
34495                 // create the grid first...
34496                 cfg.grid.container = el;
34497                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34498                 */
34499                 
34500                 if (region == 'center' && this.active ) {
34501                     cfg.background = false;
34502                 }
34503                 
34504                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34505                 
34506                 this.add(region, ret);
34507                 /*
34508                 if (cfg.background) {
34509                     // render grid on panel activation (if panel background)
34510                     ret.on('activate', function(gp) {
34511                         if (!gp.grid.rendered) {
34512                     //        gp.grid.render(el);
34513                         }
34514                     });
34515                 } else {
34516                   //  cfg.grid.render(el);
34517                 }
34518                 */
34519                 break;
34520            
34521            
34522             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34523                 // it was the old xcomponent building that caused this before.
34524                 // espeically if border is the top element in the tree.
34525                 ret = this;
34526                 break; 
34527                 
34528                     
34529                 
34530                 
34531                 
34532             default:
34533                 /*
34534                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34535                     
34536                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34537                     this.add(region, ret);
34538                 } else {
34539                 */
34540                     Roo.log(cfg);
34541                     throw "Can not add '" + cfg.xtype + "' to Border";
34542                     return null;
34543              
34544                                 
34545              
34546         }
34547         this.beginUpdate();
34548         // add children..
34549         var region = '';
34550         var abn = {};
34551         Roo.each(xitems, function(i)  {
34552             region = nb && i.region ? i.region : false;
34553             
34554             var add = ret.addxtype(i);
34555            
34556             if (region) {
34557                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34558                 if (!i.background) {
34559                     abn[region] = nb[region] ;
34560                 }
34561             }
34562             
34563         });
34564         this.endUpdate();
34565
34566         // make the last non-background panel active..
34567         //if (nb) { Roo.log(abn); }
34568         if (nb) {
34569             
34570             for(var r in abn) {
34571                 region = this.getRegion(r);
34572                 if (region) {
34573                     // tried using nb[r], but it does not work..
34574                      
34575                     region.showPanel(abn[r]);
34576                    
34577                 }
34578             }
34579         }
34580         return ret;
34581         
34582     },
34583     
34584     
34585 // private
34586     factory : function(cfg)
34587     {
34588         
34589         var validRegions = Roo.bootstrap.layout.Border.regions;
34590
34591         var target = cfg.region;
34592         cfg.mgr = this;
34593         
34594         var r = Roo.bootstrap.layout;
34595         Roo.log(target);
34596         switch(target){
34597             case "north":
34598                 return new r.North(cfg);
34599             case "south":
34600                 return new r.South(cfg);
34601             case "east":
34602                 return new r.East(cfg);
34603             case "west":
34604                 return new r.West(cfg);
34605             case "center":
34606                 return new r.Center(cfg);
34607         }
34608         throw 'Layout region "'+target+'" not supported.';
34609     }
34610     
34611     
34612 });
34613  /*
34614  * Based on:
34615  * Ext JS Library 1.1.1
34616  * Copyright(c) 2006-2007, Ext JS, LLC.
34617  *
34618  * Originally Released Under LGPL - original licence link has changed is not relivant.
34619  *
34620  * Fork - LGPL
34621  * <script type="text/javascript">
34622  */
34623  
34624 /**
34625  * @class Roo.bootstrap.layout.Basic
34626  * @extends Roo.util.Observable
34627  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34628  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34629  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34630  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34631  * @cfg {string}   region  the region that it inhabits..
34632  * @cfg {bool}   skipConfig skip config?
34633  * 
34634
34635  */
34636 Roo.bootstrap.layout.Basic = function(config){
34637     
34638     this.mgr = config.mgr;
34639     
34640     this.position = config.region;
34641     
34642     var skipConfig = config.skipConfig;
34643     
34644     this.events = {
34645         /**
34646          * @scope Roo.BasicLayoutRegion
34647          */
34648         
34649         /**
34650          * @event beforeremove
34651          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34652          * @param {Roo.LayoutRegion} this
34653          * @param {Roo.ContentPanel} panel The panel
34654          * @param {Object} e The cancel event object
34655          */
34656         "beforeremove" : true,
34657         /**
34658          * @event invalidated
34659          * Fires when the layout for this region is changed.
34660          * @param {Roo.LayoutRegion} this
34661          */
34662         "invalidated" : true,
34663         /**
34664          * @event visibilitychange
34665          * Fires when this region is shown or hidden 
34666          * @param {Roo.LayoutRegion} this
34667          * @param {Boolean} visibility true or false
34668          */
34669         "visibilitychange" : true,
34670         /**
34671          * @event paneladded
34672          * Fires when a panel is added. 
34673          * @param {Roo.LayoutRegion} this
34674          * @param {Roo.ContentPanel} panel The panel
34675          */
34676         "paneladded" : true,
34677         /**
34678          * @event panelremoved
34679          * Fires when a panel is removed. 
34680          * @param {Roo.LayoutRegion} this
34681          * @param {Roo.ContentPanel} panel The panel
34682          */
34683         "panelremoved" : true,
34684         /**
34685          * @event beforecollapse
34686          * Fires when this region before collapse.
34687          * @param {Roo.LayoutRegion} this
34688          */
34689         "beforecollapse" : true,
34690         /**
34691          * @event collapsed
34692          * Fires when this region is collapsed.
34693          * @param {Roo.LayoutRegion} this
34694          */
34695         "collapsed" : true,
34696         /**
34697          * @event expanded
34698          * Fires when this region is expanded.
34699          * @param {Roo.LayoutRegion} this
34700          */
34701         "expanded" : true,
34702         /**
34703          * @event slideshow
34704          * Fires when this region is slid into view.
34705          * @param {Roo.LayoutRegion} this
34706          */
34707         "slideshow" : true,
34708         /**
34709          * @event slidehide
34710          * Fires when this region slides out of view. 
34711          * @param {Roo.LayoutRegion} this
34712          */
34713         "slidehide" : true,
34714         /**
34715          * @event panelactivated
34716          * Fires when a panel is activated. 
34717          * @param {Roo.LayoutRegion} this
34718          * @param {Roo.ContentPanel} panel The activated panel
34719          */
34720         "panelactivated" : true,
34721         /**
34722          * @event resized
34723          * Fires when the user resizes this region. 
34724          * @param {Roo.LayoutRegion} this
34725          * @param {Number} newSize The new size (width for east/west, height for north/south)
34726          */
34727         "resized" : true
34728     };
34729     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34730     this.panels = new Roo.util.MixedCollection();
34731     this.panels.getKey = this.getPanelId.createDelegate(this);
34732     this.box = null;
34733     this.activePanel = null;
34734     // ensure listeners are added...
34735     
34736     if (config.listeners || config.events) {
34737         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34738             listeners : config.listeners || {},
34739             events : config.events || {}
34740         });
34741     }
34742     
34743     if(skipConfig !== true){
34744         this.applyConfig(config);
34745     }
34746 };
34747
34748 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34749 {
34750     getPanelId : function(p){
34751         return p.getId();
34752     },
34753     
34754     applyConfig : function(config){
34755         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34756         this.config = config;
34757         
34758     },
34759     
34760     /**
34761      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34762      * the width, for horizontal (north, south) the height.
34763      * @param {Number} newSize The new width or height
34764      */
34765     resizeTo : function(newSize){
34766         var el = this.el ? this.el :
34767                  (this.activePanel ? this.activePanel.getEl() : null);
34768         if(el){
34769             switch(this.position){
34770                 case "east":
34771                 case "west":
34772                     el.setWidth(newSize);
34773                     this.fireEvent("resized", this, newSize);
34774                 break;
34775                 case "north":
34776                 case "south":
34777                     el.setHeight(newSize);
34778                     this.fireEvent("resized", this, newSize);
34779                 break;                
34780             }
34781         }
34782     },
34783     
34784     getBox : function(){
34785         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34786     },
34787     
34788     getMargins : function(){
34789         return this.margins;
34790     },
34791     
34792     updateBox : function(box){
34793         this.box = box;
34794         var el = this.activePanel.getEl();
34795         el.dom.style.left = box.x + "px";
34796         el.dom.style.top = box.y + "px";
34797         this.activePanel.setSize(box.width, box.height);
34798     },
34799     
34800     /**
34801      * Returns the container element for this region.
34802      * @return {Roo.Element}
34803      */
34804     getEl : function(){
34805         return this.activePanel;
34806     },
34807     
34808     /**
34809      * Returns true if this region is currently visible.
34810      * @return {Boolean}
34811      */
34812     isVisible : function(){
34813         return this.activePanel ? true : false;
34814     },
34815     
34816     setActivePanel : function(panel){
34817         panel = this.getPanel(panel);
34818         if(this.activePanel && this.activePanel != panel){
34819             this.activePanel.setActiveState(false);
34820             this.activePanel.getEl().setLeftTop(-10000,-10000);
34821         }
34822         this.activePanel = panel;
34823         panel.setActiveState(true);
34824         if(this.box){
34825             panel.setSize(this.box.width, this.box.height);
34826         }
34827         this.fireEvent("panelactivated", this, panel);
34828         this.fireEvent("invalidated");
34829     },
34830     
34831     /**
34832      * Show the specified panel.
34833      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34834      * @return {Roo.ContentPanel} The shown panel or null
34835      */
34836     showPanel : function(panel){
34837         panel = this.getPanel(panel);
34838         if(panel){
34839             this.setActivePanel(panel);
34840         }
34841         return panel;
34842     },
34843     
34844     /**
34845      * Get the active panel for this region.
34846      * @return {Roo.ContentPanel} The active panel or null
34847      */
34848     getActivePanel : function(){
34849         return this.activePanel;
34850     },
34851     
34852     /**
34853      * Add the passed ContentPanel(s)
34854      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34855      * @return {Roo.ContentPanel} The panel added (if only one was added)
34856      */
34857     add : function(panel){
34858         if(arguments.length > 1){
34859             for(var i = 0, len = arguments.length; i < len; i++) {
34860                 this.add(arguments[i]);
34861             }
34862             return null;
34863         }
34864         if(this.hasPanel(panel)){
34865             this.showPanel(panel);
34866             return panel;
34867         }
34868         var el = panel.getEl();
34869         if(el.dom.parentNode != this.mgr.el.dom){
34870             this.mgr.el.dom.appendChild(el.dom);
34871         }
34872         if(panel.setRegion){
34873             panel.setRegion(this);
34874         }
34875         this.panels.add(panel);
34876         el.setStyle("position", "absolute");
34877         if(!panel.background){
34878             this.setActivePanel(panel);
34879             if(this.config.initialSize && this.panels.getCount()==1){
34880                 this.resizeTo(this.config.initialSize);
34881             }
34882         }
34883         this.fireEvent("paneladded", this, panel);
34884         return panel;
34885     },
34886     
34887     /**
34888      * Returns true if the panel is in this region.
34889      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34890      * @return {Boolean}
34891      */
34892     hasPanel : function(panel){
34893         if(typeof panel == "object"){ // must be panel obj
34894             panel = panel.getId();
34895         }
34896         return this.getPanel(panel) ? true : false;
34897     },
34898     
34899     /**
34900      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34901      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34902      * @param {Boolean} preservePanel Overrides the config preservePanel option
34903      * @return {Roo.ContentPanel} The panel that was removed
34904      */
34905     remove : function(panel, preservePanel){
34906         panel = this.getPanel(panel);
34907         if(!panel){
34908             return null;
34909         }
34910         var e = {};
34911         this.fireEvent("beforeremove", this, panel, e);
34912         if(e.cancel === true){
34913             return null;
34914         }
34915         var panelId = panel.getId();
34916         this.panels.removeKey(panelId);
34917         return panel;
34918     },
34919     
34920     /**
34921      * Returns the panel specified or null if it's not in this region.
34922      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34923      * @return {Roo.ContentPanel}
34924      */
34925     getPanel : function(id){
34926         if(typeof id == "object"){ // must be panel obj
34927             return id;
34928         }
34929         return this.panels.get(id);
34930     },
34931     
34932     /**
34933      * Returns this regions position (north/south/east/west/center).
34934      * @return {String} 
34935      */
34936     getPosition: function(){
34937         return this.position;    
34938     }
34939 });/*
34940  * Based on:
34941  * Ext JS Library 1.1.1
34942  * Copyright(c) 2006-2007, Ext JS, LLC.
34943  *
34944  * Originally Released Under LGPL - original licence link has changed is not relivant.
34945  *
34946  * Fork - LGPL
34947  * <script type="text/javascript">
34948  */
34949  
34950 /**
34951  * @class Roo.bootstrap.layout.Region
34952  * @extends Roo.bootstrap.layout.Basic
34953  * This class represents a region in a layout manager.
34954  
34955  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34956  * @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})
34957  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34958  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34959  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34960  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34961  * @cfg {String}    title           The title for the region (overrides panel titles)
34962  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34963  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34964  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34965  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34966  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34967  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34968  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34969  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34970  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34971  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34972
34973  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34974  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34975  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34976  * @cfg {Number}    width           For East/West panels
34977  * @cfg {Number}    height          For North/South panels
34978  * @cfg {Boolean}   split           To show the splitter
34979  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34980  * 
34981  * @cfg {string}   cls             Extra CSS classes to add to region
34982  * 
34983  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34984  * @cfg {string}   region  the region that it inhabits..
34985  *
34986
34987  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34988  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34989
34990  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34991  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34992  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34993  */
34994 Roo.bootstrap.layout.Region = function(config)
34995 {
34996     this.applyConfig(config);
34997
34998     var mgr = config.mgr;
34999     var pos = config.region;
35000     config.skipConfig = true;
35001     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35002     
35003     if (mgr.el) {
35004         this.onRender(mgr.el);   
35005     }
35006      
35007     this.visible = true;
35008     this.collapsed = false;
35009     this.unrendered_panels = [];
35010 };
35011
35012 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35013
35014     position: '', // set by wrapper (eg. north/south etc..)
35015     unrendered_panels : null,  // unrendered panels.
35016     createBody : function(){
35017         /** This region's body element 
35018         * @type Roo.Element */
35019         this.bodyEl = this.el.createChild({
35020                 tag: "div",
35021                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35022         });
35023     },
35024
35025     onRender: function(ctr, pos)
35026     {
35027         var dh = Roo.DomHelper;
35028         /** This region's container element 
35029         * @type Roo.Element */
35030         this.el = dh.append(ctr.dom, {
35031                 tag: "div",
35032                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35033             }, true);
35034         /** This region's title element 
35035         * @type Roo.Element */
35036     
35037         this.titleEl = dh.append(this.el.dom,
35038             {
35039                     tag: "div",
35040                     unselectable: "on",
35041                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35042                     children:[
35043                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35044                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35045                     ]}, true);
35046         
35047         this.titleEl.enableDisplayMode();
35048         /** This region's title text element 
35049         * @type HTMLElement */
35050         this.titleTextEl = this.titleEl.dom.firstChild;
35051         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35052         /*
35053         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35054         this.closeBtn.enableDisplayMode();
35055         this.closeBtn.on("click", this.closeClicked, this);
35056         this.closeBtn.hide();
35057     */
35058         this.createBody(this.config);
35059         if(this.config.hideWhenEmpty){
35060             this.hide();
35061             this.on("paneladded", this.validateVisibility, this);
35062             this.on("panelremoved", this.validateVisibility, this);
35063         }
35064         if(this.autoScroll){
35065             this.bodyEl.setStyle("overflow", "auto");
35066         }else{
35067             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35068         }
35069         //if(c.titlebar !== false){
35070             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35071                 this.titleEl.hide();
35072             }else{
35073                 this.titleEl.show();
35074                 if(this.config.title){
35075                     this.titleTextEl.innerHTML = this.config.title;
35076                 }
35077             }
35078         //}
35079         if(this.config.collapsed){
35080             this.collapse(true);
35081         }
35082         if(this.config.hidden){
35083             this.hide();
35084         }
35085         
35086         if (this.unrendered_panels && this.unrendered_panels.length) {
35087             for (var i =0;i< this.unrendered_panels.length; i++) {
35088                 this.add(this.unrendered_panels[i]);
35089             }
35090             this.unrendered_panels = null;
35091             
35092         }
35093         
35094     },
35095     
35096     applyConfig : function(c)
35097     {
35098         /*
35099          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35100             var dh = Roo.DomHelper;
35101             if(c.titlebar !== false){
35102                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35103                 this.collapseBtn.on("click", this.collapse, this);
35104                 this.collapseBtn.enableDisplayMode();
35105                 /*
35106                 if(c.showPin === true || this.showPin){
35107                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35108                     this.stickBtn.enableDisplayMode();
35109                     this.stickBtn.on("click", this.expand, this);
35110                     this.stickBtn.hide();
35111                 }
35112                 
35113             }
35114             */
35115             /** This region's collapsed element
35116             * @type Roo.Element */
35117             /*
35118              *
35119             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35120                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35121             ]}, true);
35122             
35123             if(c.floatable !== false){
35124                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35125                this.collapsedEl.on("click", this.collapseClick, this);
35126             }
35127
35128             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35129                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35130                    id: "message", unselectable: "on", style:{"float":"left"}});
35131                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35132              }
35133             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35134             this.expandBtn.on("click", this.expand, this);
35135             
35136         }
35137         
35138         if(this.collapseBtn){
35139             this.collapseBtn.setVisible(c.collapsible == true);
35140         }
35141         
35142         this.cmargins = c.cmargins || this.cmargins ||
35143                          (this.position == "west" || this.position == "east" ?
35144                              {top: 0, left: 2, right:2, bottom: 0} :
35145                              {top: 2, left: 0, right:0, bottom: 2});
35146         */
35147         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35148         
35149         
35150         this.bottomTabs = c.tabPosition != "top";
35151         
35152         this.autoScroll = c.autoScroll || false;
35153         
35154         
35155        
35156         
35157         this.duration = c.duration || .30;
35158         this.slideDuration = c.slideDuration || .45;
35159         this.config = c;
35160        
35161     },
35162     /**
35163      * Returns true if this region is currently visible.
35164      * @return {Boolean}
35165      */
35166     isVisible : function(){
35167         return this.visible;
35168     },
35169
35170     /**
35171      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35172      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35173      */
35174     //setCollapsedTitle : function(title){
35175     //    title = title || "&#160;";
35176      //   if(this.collapsedTitleTextEl){
35177       //      this.collapsedTitleTextEl.innerHTML = title;
35178        // }
35179     //},
35180
35181     getBox : function(){
35182         var b;
35183       //  if(!this.collapsed){
35184             b = this.el.getBox(false, true);
35185        // }else{
35186           //  b = this.collapsedEl.getBox(false, true);
35187         //}
35188         return b;
35189     },
35190
35191     getMargins : function(){
35192         return this.margins;
35193         //return this.collapsed ? this.cmargins : this.margins;
35194     },
35195 /*
35196     highlight : function(){
35197         this.el.addClass("x-layout-panel-dragover");
35198     },
35199
35200     unhighlight : function(){
35201         this.el.removeClass("x-layout-panel-dragover");
35202     },
35203 */
35204     updateBox : function(box)
35205     {
35206         if (!this.bodyEl) {
35207             return; // not rendered yet..
35208         }
35209         
35210         this.box = box;
35211         if(!this.collapsed){
35212             this.el.dom.style.left = box.x + "px";
35213             this.el.dom.style.top = box.y + "px";
35214             this.updateBody(box.width, box.height);
35215         }else{
35216             this.collapsedEl.dom.style.left = box.x + "px";
35217             this.collapsedEl.dom.style.top = box.y + "px";
35218             this.collapsedEl.setSize(box.width, box.height);
35219         }
35220         if(this.tabs){
35221             this.tabs.autoSizeTabs();
35222         }
35223     },
35224
35225     updateBody : function(w, h)
35226     {
35227         if(w !== null){
35228             this.el.setWidth(w);
35229             w -= this.el.getBorderWidth("rl");
35230             if(this.config.adjustments){
35231                 w += this.config.adjustments[0];
35232             }
35233         }
35234         if(h !== null && h > 0){
35235             this.el.setHeight(h);
35236             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35237             h -= this.el.getBorderWidth("tb");
35238             if(this.config.adjustments){
35239                 h += this.config.adjustments[1];
35240             }
35241             this.bodyEl.setHeight(h);
35242             if(this.tabs){
35243                 h = this.tabs.syncHeight(h);
35244             }
35245         }
35246         if(this.panelSize){
35247             w = w !== null ? w : this.panelSize.width;
35248             h = h !== null ? h : this.panelSize.height;
35249         }
35250         if(this.activePanel){
35251             var el = this.activePanel.getEl();
35252             w = w !== null ? w : el.getWidth();
35253             h = h !== null ? h : el.getHeight();
35254             this.panelSize = {width: w, height: h};
35255             this.activePanel.setSize(w, h);
35256         }
35257         if(Roo.isIE && this.tabs){
35258             this.tabs.el.repaint();
35259         }
35260     },
35261
35262     /**
35263      * Returns the container element for this region.
35264      * @return {Roo.Element}
35265      */
35266     getEl : function(){
35267         return this.el;
35268     },
35269
35270     /**
35271      * Hides this region.
35272      */
35273     hide : function(){
35274         //if(!this.collapsed){
35275             this.el.dom.style.left = "-2000px";
35276             this.el.hide();
35277         //}else{
35278          //   this.collapsedEl.dom.style.left = "-2000px";
35279          //   this.collapsedEl.hide();
35280        // }
35281         this.visible = false;
35282         this.fireEvent("visibilitychange", this, false);
35283     },
35284
35285     /**
35286      * Shows this region if it was previously hidden.
35287      */
35288     show : function(){
35289         //if(!this.collapsed){
35290             this.el.show();
35291         //}else{
35292         //    this.collapsedEl.show();
35293        // }
35294         this.visible = true;
35295         this.fireEvent("visibilitychange", this, true);
35296     },
35297 /*
35298     closeClicked : function(){
35299         if(this.activePanel){
35300             this.remove(this.activePanel);
35301         }
35302     },
35303
35304     collapseClick : function(e){
35305         if(this.isSlid){
35306            e.stopPropagation();
35307            this.slideIn();
35308         }else{
35309            e.stopPropagation();
35310            this.slideOut();
35311         }
35312     },
35313 */
35314     /**
35315      * Collapses this region.
35316      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35317      */
35318     /*
35319     collapse : function(skipAnim, skipCheck = false){
35320         if(this.collapsed) {
35321             return;
35322         }
35323         
35324         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35325             
35326             this.collapsed = true;
35327             if(this.split){
35328                 this.split.el.hide();
35329             }
35330             if(this.config.animate && skipAnim !== true){
35331                 this.fireEvent("invalidated", this);
35332                 this.animateCollapse();
35333             }else{
35334                 this.el.setLocation(-20000,-20000);
35335                 this.el.hide();
35336                 this.collapsedEl.show();
35337                 this.fireEvent("collapsed", this);
35338                 this.fireEvent("invalidated", this);
35339             }
35340         }
35341         
35342     },
35343 */
35344     animateCollapse : function(){
35345         // overridden
35346     },
35347
35348     /**
35349      * Expands this region if it was previously collapsed.
35350      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35351      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35352      */
35353     /*
35354     expand : function(e, skipAnim){
35355         if(e) {
35356             e.stopPropagation();
35357         }
35358         if(!this.collapsed || this.el.hasActiveFx()) {
35359             return;
35360         }
35361         if(this.isSlid){
35362             this.afterSlideIn();
35363             skipAnim = true;
35364         }
35365         this.collapsed = false;
35366         if(this.config.animate && skipAnim !== true){
35367             this.animateExpand();
35368         }else{
35369             this.el.show();
35370             if(this.split){
35371                 this.split.el.show();
35372             }
35373             this.collapsedEl.setLocation(-2000,-2000);
35374             this.collapsedEl.hide();
35375             this.fireEvent("invalidated", this);
35376             this.fireEvent("expanded", this);
35377         }
35378     },
35379 */
35380     animateExpand : function(){
35381         // overridden
35382     },
35383
35384     initTabs : function()
35385     {
35386         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35387         
35388         var ts = new Roo.bootstrap.panel.Tabs({
35389                 el: this.bodyEl.dom,
35390                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35391                 disableTooltips: this.config.disableTabTips,
35392                 toolbar : this.config.toolbar
35393             });
35394         
35395         if(this.config.hideTabs){
35396             ts.stripWrap.setDisplayed(false);
35397         }
35398         this.tabs = ts;
35399         ts.resizeTabs = this.config.resizeTabs === true;
35400         ts.minTabWidth = this.config.minTabWidth || 40;
35401         ts.maxTabWidth = this.config.maxTabWidth || 250;
35402         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35403         ts.monitorResize = false;
35404         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35405         ts.bodyEl.addClass('roo-layout-tabs-body');
35406         this.panels.each(this.initPanelAsTab, this);
35407     },
35408
35409     initPanelAsTab : function(panel){
35410         var ti = this.tabs.addTab(
35411             panel.getEl().id,
35412             panel.getTitle(),
35413             null,
35414             this.config.closeOnTab && panel.isClosable(),
35415             panel.tpl
35416         );
35417         if(panel.tabTip !== undefined){
35418             ti.setTooltip(panel.tabTip);
35419         }
35420         ti.on("activate", function(){
35421               this.setActivePanel(panel);
35422         }, this);
35423         
35424         if(this.config.closeOnTab){
35425             ti.on("beforeclose", function(t, e){
35426                 e.cancel = true;
35427                 this.remove(panel);
35428             }, this);
35429         }
35430         
35431         panel.tabItem = ti;
35432         
35433         return ti;
35434     },
35435
35436     updatePanelTitle : function(panel, title)
35437     {
35438         if(this.activePanel == panel){
35439             this.updateTitle(title);
35440         }
35441         if(this.tabs){
35442             var ti = this.tabs.getTab(panel.getEl().id);
35443             ti.setText(title);
35444             if(panel.tabTip !== undefined){
35445                 ti.setTooltip(panel.tabTip);
35446             }
35447         }
35448     },
35449
35450     updateTitle : function(title){
35451         if(this.titleTextEl && !this.config.title){
35452             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35453         }
35454     },
35455
35456     setActivePanel : function(panel)
35457     {
35458         panel = this.getPanel(panel);
35459         if(this.activePanel && this.activePanel != panel){
35460             if(this.activePanel.setActiveState(false) === false){
35461                 return;
35462             }
35463         }
35464         this.activePanel = panel;
35465         panel.setActiveState(true);
35466         if(this.panelSize){
35467             panel.setSize(this.panelSize.width, this.panelSize.height);
35468         }
35469         if(this.closeBtn){
35470             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35471         }
35472         this.updateTitle(panel.getTitle());
35473         if(this.tabs){
35474             this.fireEvent("invalidated", this);
35475         }
35476         this.fireEvent("panelactivated", this, panel);
35477     },
35478
35479     /**
35480      * Shows the specified panel.
35481      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35482      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35483      */
35484     showPanel : function(panel)
35485     {
35486         panel = this.getPanel(panel);
35487         if(panel){
35488             if(this.tabs){
35489                 var tab = this.tabs.getTab(panel.getEl().id);
35490                 if(tab.isHidden()){
35491                     this.tabs.unhideTab(tab.id);
35492                 }
35493                 tab.activate();
35494             }else{
35495                 this.setActivePanel(panel);
35496             }
35497         }
35498         return panel;
35499     },
35500
35501     /**
35502      * Get the active panel for this region.
35503      * @return {Roo.ContentPanel} The active panel or null
35504      */
35505     getActivePanel : function(){
35506         return this.activePanel;
35507     },
35508
35509     validateVisibility : function(){
35510         if(this.panels.getCount() < 1){
35511             this.updateTitle("&#160;");
35512             this.closeBtn.hide();
35513             this.hide();
35514         }else{
35515             if(!this.isVisible()){
35516                 this.show();
35517             }
35518         }
35519     },
35520
35521     /**
35522      * Adds the passed ContentPanel(s) to this region.
35523      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35524      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35525      */
35526     add : function(panel)
35527     {
35528         if(arguments.length > 1){
35529             for(var i = 0, len = arguments.length; i < len; i++) {
35530                 this.add(arguments[i]);
35531             }
35532             return null;
35533         }
35534         
35535         // if we have not been rendered yet, then we can not really do much of this..
35536         if (!this.bodyEl) {
35537             this.unrendered_panels.push(panel);
35538             return panel;
35539         }
35540         
35541         
35542         
35543         
35544         if(this.hasPanel(panel)){
35545             this.showPanel(panel);
35546             return panel;
35547         }
35548         panel.setRegion(this);
35549         this.panels.add(panel);
35550        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35551             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35552             // and hide them... ???
35553             this.bodyEl.dom.appendChild(panel.getEl().dom);
35554             if(panel.background !== true){
35555                 this.setActivePanel(panel);
35556             }
35557             this.fireEvent("paneladded", this, panel);
35558             return panel;
35559         }
35560         */
35561         if(!this.tabs){
35562             this.initTabs();
35563         }else{
35564             this.initPanelAsTab(panel);
35565         }
35566         
35567         
35568         if(panel.background !== true){
35569             this.tabs.activate(panel.getEl().id);
35570         }
35571         this.fireEvent("paneladded", this, panel);
35572         return panel;
35573     },
35574
35575     /**
35576      * Hides the tab for the specified panel.
35577      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35578      */
35579     hidePanel : function(panel){
35580         if(this.tabs && (panel = this.getPanel(panel))){
35581             this.tabs.hideTab(panel.getEl().id);
35582         }
35583     },
35584
35585     /**
35586      * Unhides the tab for a previously hidden panel.
35587      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35588      */
35589     unhidePanel : function(panel){
35590         if(this.tabs && (panel = this.getPanel(panel))){
35591             this.tabs.unhideTab(panel.getEl().id);
35592         }
35593     },
35594
35595     clearPanels : function(){
35596         while(this.panels.getCount() > 0){
35597              this.remove(this.panels.first());
35598         }
35599     },
35600
35601     /**
35602      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35603      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35604      * @param {Boolean} preservePanel Overrides the config preservePanel option
35605      * @return {Roo.ContentPanel} The panel that was removed
35606      */
35607     remove : function(panel, preservePanel)
35608     {
35609         panel = this.getPanel(panel);
35610         if(!panel){
35611             return null;
35612         }
35613         var e = {};
35614         this.fireEvent("beforeremove", this, panel, e);
35615         if(e.cancel === true){
35616             return null;
35617         }
35618         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35619         var panelId = panel.getId();
35620         this.panels.removeKey(panelId);
35621         if(preservePanel){
35622             document.body.appendChild(panel.getEl().dom);
35623         }
35624         if(this.tabs){
35625             this.tabs.removeTab(panel.getEl().id);
35626         }else if (!preservePanel){
35627             this.bodyEl.dom.removeChild(panel.getEl().dom);
35628         }
35629         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35630             var p = this.panels.first();
35631             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35632             tempEl.appendChild(p.getEl().dom);
35633             this.bodyEl.update("");
35634             this.bodyEl.dom.appendChild(p.getEl().dom);
35635             tempEl = null;
35636             this.updateTitle(p.getTitle());
35637             this.tabs = null;
35638             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35639             this.setActivePanel(p);
35640         }
35641         panel.setRegion(null);
35642         if(this.activePanel == panel){
35643             this.activePanel = null;
35644         }
35645         if(this.config.autoDestroy !== false && preservePanel !== true){
35646             try{panel.destroy();}catch(e){}
35647         }
35648         this.fireEvent("panelremoved", this, panel);
35649         return panel;
35650     },
35651
35652     /**
35653      * Returns the TabPanel component used by this region
35654      * @return {Roo.TabPanel}
35655      */
35656     getTabs : function(){
35657         return this.tabs;
35658     },
35659
35660     createTool : function(parentEl, className){
35661         var btn = Roo.DomHelper.append(parentEl, {
35662             tag: "div",
35663             cls: "x-layout-tools-button",
35664             children: [ {
35665                 tag: "div",
35666                 cls: "roo-layout-tools-button-inner " + className,
35667                 html: "&#160;"
35668             }]
35669         }, true);
35670         btn.addClassOnOver("roo-layout-tools-button-over");
35671         return btn;
35672     }
35673 });/*
35674  * Based on:
35675  * Ext JS Library 1.1.1
35676  * Copyright(c) 2006-2007, Ext JS, LLC.
35677  *
35678  * Originally Released Under LGPL - original licence link has changed is not relivant.
35679  *
35680  * Fork - LGPL
35681  * <script type="text/javascript">
35682  */
35683  
35684
35685
35686 /**
35687  * @class Roo.SplitLayoutRegion
35688  * @extends Roo.LayoutRegion
35689  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35690  */
35691 Roo.bootstrap.layout.Split = function(config){
35692     this.cursor = config.cursor;
35693     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35694 };
35695
35696 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35697 {
35698     splitTip : "Drag to resize.",
35699     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35700     useSplitTips : false,
35701
35702     applyConfig : function(config){
35703         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35704     },
35705     
35706     onRender : function(ctr,pos) {
35707         
35708         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35709         if(!this.config.split){
35710             return;
35711         }
35712         if(!this.split){
35713             
35714             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35715                             tag: "div",
35716                             id: this.el.id + "-split",
35717                             cls: "roo-layout-split roo-layout-split-"+this.position,
35718                             html: "&#160;"
35719             });
35720             /** The SplitBar for this region 
35721             * @type Roo.SplitBar */
35722             // does not exist yet...
35723             Roo.log([this.position, this.orientation]);
35724             
35725             this.split = new Roo.bootstrap.SplitBar({
35726                 dragElement : splitEl,
35727                 resizingElement: this.el,
35728                 orientation : this.orientation
35729             });
35730             
35731             this.split.on("moved", this.onSplitMove, this);
35732             this.split.useShim = this.config.useShim === true;
35733             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35734             if(this.useSplitTips){
35735                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35736             }
35737             //if(config.collapsible){
35738             //    this.split.el.on("dblclick", this.collapse,  this);
35739             //}
35740         }
35741         if(typeof this.config.minSize != "undefined"){
35742             this.split.minSize = this.config.minSize;
35743         }
35744         if(typeof this.config.maxSize != "undefined"){
35745             this.split.maxSize = this.config.maxSize;
35746         }
35747         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35748             this.hideSplitter();
35749         }
35750         
35751     },
35752
35753     getHMaxSize : function(){
35754          var cmax = this.config.maxSize || 10000;
35755          var center = this.mgr.getRegion("center");
35756          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35757     },
35758
35759     getVMaxSize : function(){
35760          var cmax = this.config.maxSize || 10000;
35761          var center = this.mgr.getRegion("center");
35762          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35763     },
35764
35765     onSplitMove : function(split, newSize){
35766         this.fireEvent("resized", this, newSize);
35767     },
35768     
35769     /** 
35770      * Returns the {@link Roo.SplitBar} for this region.
35771      * @return {Roo.SplitBar}
35772      */
35773     getSplitBar : function(){
35774         return this.split;
35775     },
35776     
35777     hide : function(){
35778         this.hideSplitter();
35779         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35780     },
35781
35782     hideSplitter : function(){
35783         if(this.split){
35784             this.split.el.setLocation(-2000,-2000);
35785             this.split.el.hide();
35786         }
35787     },
35788
35789     show : function(){
35790         if(this.split){
35791             this.split.el.show();
35792         }
35793         Roo.bootstrap.layout.Split.superclass.show.call(this);
35794     },
35795     
35796     beforeSlide: function(){
35797         if(Roo.isGecko){// firefox overflow auto bug workaround
35798             this.bodyEl.clip();
35799             if(this.tabs) {
35800                 this.tabs.bodyEl.clip();
35801             }
35802             if(this.activePanel){
35803                 this.activePanel.getEl().clip();
35804                 
35805                 if(this.activePanel.beforeSlide){
35806                     this.activePanel.beforeSlide();
35807                 }
35808             }
35809         }
35810     },
35811     
35812     afterSlide : function(){
35813         if(Roo.isGecko){// firefox overflow auto bug workaround
35814             this.bodyEl.unclip();
35815             if(this.tabs) {
35816                 this.tabs.bodyEl.unclip();
35817             }
35818             if(this.activePanel){
35819                 this.activePanel.getEl().unclip();
35820                 if(this.activePanel.afterSlide){
35821                     this.activePanel.afterSlide();
35822                 }
35823             }
35824         }
35825     },
35826
35827     initAutoHide : function(){
35828         if(this.autoHide !== false){
35829             if(!this.autoHideHd){
35830                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35831                 this.autoHideHd = {
35832                     "mouseout": function(e){
35833                         if(!e.within(this.el, true)){
35834                             st.delay(500);
35835                         }
35836                     },
35837                     "mouseover" : function(e){
35838                         st.cancel();
35839                     },
35840                     scope : this
35841                 };
35842             }
35843             this.el.on(this.autoHideHd);
35844         }
35845     },
35846
35847     clearAutoHide : function(){
35848         if(this.autoHide !== false){
35849             this.el.un("mouseout", this.autoHideHd.mouseout);
35850             this.el.un("mouseover", this.autoHideHd.mouseover);
35851         }
35852     },
35853
35854     clearMonitor : function(){
35855         Roo.get(document).un("click", this.slideInIf, this);
35856     },
35857
35858     // these names are backwards but not changed for compat
35859     slideOut : function(){
35860         if(this.isSlid || this.el.hasActiveFx()){
35861             return;
35862         }
35863         this.isSlid = true;
35864         if(this.collapseBtn){
35865             this.collapseBtn.hide();
35866         }
35867         this.closeBtnState = this.closeBtn.getStyle('display');
35868         this.closeBtn.hide();
35869         if(this.stickBtn){
35870             this.stickBtn.show();
35871         }
35872         this.el.show();
35873         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35874         this.beforeSlide();
35875         this.el.setStyle("z-index", 10001);
35876         this.el.slideIn(this.getSlideAnchor(), {
35877             callback: function(){
35878                 this.afterSlide();
35879                 this.initAutoHide();
35880                 Roo.get(document).on("click", this.slideInIf, this);
35881                 this.fireEvent("slideshow", this);
35882             },
35883             scope: this,
35884             block: true
35885         });
35886     },
35887
35888     afterSlideIn : function(){
35889         this.clearAutoHide();
35890         this.isSlid = false;
35891         this.clearMonitor();
35892         this.el.setStyle("z-index", "");
35893         if(this.collapseBtn){
35894             this.collapseBtn.show();
35895         }
35896         this.closeBtn.setStyle('display', this.closeBtnState);
35897         if(this.stickBtn){
35898             this.stickBtn.hide();
35899         }
35900         this.fireEvent("slidehide", this);
35901     },
35902
35903     slideIn : function(cb){
35904         if(!this.isSlid || this.el.hasActiveFx()){
35905             Roo.callback(cb);
35906             return;
35907         }
35908         this.isSlid = false;
35909         this.beforeSlide();
35910         this.el.slideOut(this.getSlideAnchor(), {
35911             callback: function(){
35912                 this.el.setLeftTop(-10000, -10000);
35913                 this.afterSlide();
35914                 this.afterSlideIn();
35915                 Roo.callback(cb);
35916             },
35917             scope: this,
35918             block: true
35919         });
35920     },
35921     
35922     slideInIf : function(e){
35923         if(!e.within(this.el)){
35924             this.slideIn();
35925         }
35926     },
35927
35928     animateCollapse : function(){
35929         this.beforeSlide();
35930         this.el.setStyle("z-index", 20000);
35931         var anchor = this.getSlideAnchor();
35932         this.el.slideOut(anchor, {
35933             callback : function(){
35934                 this.el.setStyle("z-index", "");
35935                 this.collapsedEl.slideIn(anchor, {duration:.3});
35936                 this.afterSlide();
35937                 this.el.setLocation(-10000,-10000);
35938                 this.el.hide();
35939                 this.fireEvent("collapsed", this);
35940             },
35941             scope: this,
35942             block: true
35943         });
35944     },
35945
35946     animateExpand : function(){
35947         this.beforeSlide();
35948         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35949         this.el.setStyle("z-index", 20000);
35950         this.collapsedEl.hide({
35951             duration:.1
35952         });
35953         this.el.slideIn(this.getSlideAnchor(), {
35954             callback : function(){
35955                 this.el.setStyle("z-index", "");
35956                 this.afterSlide();
35957                 if(this.split){
35958                     this.split.el.show();
35959                 }
35960                 this.fireEvent("invalidated", this);
35961                 this.fireEvent("expanded", this);
35962             },
35963             scope: this,
35964             block: true
35965         });
35966     },
35967
35968     anchors : {
35969         "west" : "left",
35970         "east" : "right",
35971         "north" : "top",
35972         "south" : "bottom"
35973     },
35974
35975     sanchors : {
35976         "west" : "l",
35977         "east" : "r",
35978         "north" : "t",
35979         "south" : "b"
35980     },
35981
35982     canchors : {
35983         "west" : "tl-tr",
35984         "east" : "tr-tl",
35985         "north" : "tl-bl",
35986         "south" : "bl-tl"
35987     },
35988
35989     getAnchor : function(){
35990         return this.anchors[this.position];
35991     },
35992
35993     getCollapseAnchor : function(){
35994         return this.canchors[this.position];
35995     },
35996
35997     getSlideAnchor : function(){
35998         return this.sanchors[this.position];
35999     },
36000
36001     getAlignAdj : function(){
36002         var cm = this.cmargins;
36003         switch(this.position){
36004             case "west":
36005                 return [0, 0];
36006             break;
36007             case "east":
36008                 return [0, 0];
36009             break;
36010             case "north":
36011                 return [0, 0];
36012             break;
36013             case "south":
36014                 return [0, 0];
36015             break;
36016         }
36017     },
36018
36019     getExpandAdj : function(){
36020         var c = this.collapsedEl, cm = this.cmargins;
36021         switch(this.position){
36022             case "west":
36023                 return [-(cm.right+c.getWidth()+cm.left), 0];
36024             break;
36025             case "east":
36026                 return [cm.right+c.getWidth()+cm.left, 0];
36027             break;
36028             case "north":
36029                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36030             break;
36031             case "south":
36032                 return [0, cm.top+cm.bottom+c.getHeight()];
36033             break;
36034         }
36035     }
36036 });/*
36037  * Based on:
36038  * Ext JS Library 1.1.1
36039  * Copyright(c) 2006-2007, Ext JS, LLC.
36040  *
36041  * Originally Released Under LGPL - original licence link has changed is not relivant.
36042  *
36043  * Fork - LGPL
36044  * <script type="text/javascript">
36045  */
36046 /*
36047  * These classes are private internal classes
36048  */
36049 Roo.bootstrap.layout.Center = function(config){
36050     config.region = "center";
36051     Roo.bootstrap.layout.Region.call(this, config);
36052     this.visible = true;
36053     this.minWidth = config.minWidth || 20;
36054     this.minHeight = config.minHeight || 20;
36055 };
36056
36057 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36058     hide : function(){
36059         // center panel can't be hidden
36060     },
36061     
36062     show : function(){
36063         // center panel can't be hidden
36064     },
36065     
36066     getMinWidth: function(){
36067         return this.minWidth;
36068     },
36069     
36070     getMinHeight: function(){
36071         return this.minHeight;
36072     }
36073 });
36074
36075
36076
36077
36078  
36079
36080
36081
36082
36083
36084 Roo.bootstrap.layout.North = function(config)
36085 {
36086     config.region = 'north';
36087     config.cursor = 'n-resize';
36088     
36089     Roo.bootstrap.layout.Split.call(this, config);
36090     
36091     
36092     if(this.split){
36093         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36094         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36095         this.split.el.addClass("roo-layout-split-v");
36096     }
36097     var size = config.initialSize || config.height;
36098     if(typeof size != "undefined"){
36099         this.el.setHeight(size);
36100     }
36101 };
36102 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36103 {
36104     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36105     
36106     
36107     
36108     getBox : function(){
36109         if(this.collapsed){
36110             return this.collapsedEl.getBox();
36111         }
36112         var box = this.el.getBox();
36113         if(this.split){
36114             box.height += this.split.el.getHeight();
36115         }
36116         return box;
36117     },
36118     
36119     updateBox : function(box){
36120         if(this.split && !this.collapsed){
36121             box.height -= this.split.el.getHeight();
36122             this.split.el.setLeft(box.x);
36123             this.split.el.setTop(box.y+box.height);
36124             this.split.el.setWidth(box.width);
36125         }
36126         if(this.collapsed){
36127             this.updateBody(box.width, null);
36128         }
36129         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36130     }
36131 });
36132
36133
36134
36135
36136
36137 Roo.bootstrap.layout.South = function(config){
36138     config.region = 'south';
36139     config.cursor = 's-resize';
36140     Roo.bootstrap.layout.Split.call(this, config);
36141     if(this.split){
36142         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36143         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36144         this.split.el.addClass("roo-layout-split-v");
36145     }
36146     var size = config.initialSize || config.height;
36147     if(typeof size != "undefined"){
36148         this.el.setHeight(size);
36149     }
36150 };
36151
36152 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36153     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36154     getBox : function(){
36155         if(this.collapsed){
36156             return this.collapsedEl.getBox();
36157         }
36158         var box = this.el.getBox();
36159         if(this.split){
36160             var sh = this.split.el.getHeight();
36161             box.height += sh;
36162             box.y -= sh;
36163         }
36164         return box;
36165     },
36166     
36167     updateBox : function(box){
36168         if(this.split && !this.collapsed){
36169             var sh = this.split.el.getHeight();
36170             box.height -= sh;
36171             box.y += sh;
36172             this.split.el.setLeft(box.x);
36173             this.split.el.setTop(box.y-sh);
36174             this.split.el.setWidth(box.width);
36175         }
36176         if(this.collapsed){
36177             this.updateBody(box.width, null);
36178         }
36179         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36180     }
36181 });
36182
36183 Roo.bootstrap.layout.East = function(config){
36184     config.region = "east";
36185     config.cursor = "e-resize";
36186     Roo.bootstrap.layout.Split.call(this, config);
36187     if(this.split){
36188         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36189         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36190         this.split.el.addClass("roo-layout-split-h");
36191     }
36192     var size = config.initialSize || config.width;
36193     if(typeof size != "undefined"){
36194         this.el.setWidth(size);
36195     }
36196 };
36197 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36198     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36199     getBox : function(){
36200         if(this.collapsed){
36201             return this.collapsedEl.getBox();
36202         }
36203         var box = this.el.getBox();
36204         if(this.split){
36205             var sw = this.split.el.getWidth();
36206             box.width += sw;
36207             box.x -= sw;
36208         }
36209         return box;
36210     },
36211
36212     updateBox : function(box){
36213         if(this.split && !this.collapsed){
36214             var sw = this.split.el.getWidth();
36215             box.width -= sw;
36216             this.split.el.setLeft(box.x);
36217             this.split.el.setTop(box.y);
36218             this.split.el.setHeight(box.height);
36219             box.x += sw;
36220         }
36221         if(this.collapsed){
36222             this.updateBody(null, box.height);
36223         }
36224         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36225     }
36226 });
36227
36228 Roo.bootstrap.layout.West = function(config){
36229     config.region = "west";
36230     config.cursor = "w-resize";
36231     
36232     Roo.bootstrap.layout.Split.call(this, config);
36233     if(this.split){
36234         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36235         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36236         this.split.el.addClass("roo-layout-split-h");
36237     }
36238     
36239 };
36240 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36241     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36242     
36243     onRender: function(ctr, pos)
36244     {
36245         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36246         var size = this.config.initialSize || this.config.width;
36247         if(typeof size != "undefined"){
36248             this.el.setWidth(size);
36249         }
36250     },
36251     
36252     getBox : function(){
36253         if(this.collapsed){
36254             return this.collapsedEl.getBox();
36255         }
36256         var box = this.el.getBox();
36257         if(this.split){
36258             box.width += this.split.el.getWidth();
36259         }
36260         return box;
36261     },
36262     
36263     updateBox : function(box){
36264         if(this.split && !this.collapsed){
36265             var sw = this.split.el.getWidth();
36266             box.width -= sw;
36267             this.split.el.setLeft(box.x+box.width);
36268             this.split.el.setTop(box.y);
36269             this.split.el.setHeight(box.height);
36270         }
36271         if(this.collapsed){
36272             this.updateBody(null, box.height);
36273         }
36274         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36275     }
36276 });
36277 Roo.namespace("Roo.bootstrap.panel");/*
36278  * Based on:
36279  * Ext JS Library 1.1.1
36280  * Copyright(c) 2006-2007, Ext JS, LLC.
36281  *
36282  * Originally Released Under LGPL - original licence link has changed is not relivant.
36283  *
36284  * Fork - LGPL
36285  * <script type="text/javascript">
36286  */
36287 /**
36288  * @class Roo.ContentPanel
36289  * @extends Roo.util.Observable
36290  * A basic ContentPanel element.
36291  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36292  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36293  * @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
36294  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36295  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36296  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36297  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36298  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36299  * @cfg {String} title          The title for this panel
36300  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36301  * @cfg {String} url            Calls {@link #setUrl} with this value
36302  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36303  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36304  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36305  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36306  * @cfg {Boolean} badges render the badges
36307
36308  * @constructor
36309  * Create a new ContentPanel.
36310  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36311  * @param {String/Object} config A string to set only the title or a config object
36312  * @param {String} content (optional) Set the HTML content for this panel
36313  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36314  */
36315 Roo.bootstrap.panel.Content = function( config){
36316     
36317     this.tpl = config.tpl || false;
36318     
36319     var el = config.el;
36320     var content = config.content;
36321
36322     if(config.autoCreate){ // xtype is available if this is called from factory
36323         el = Roo.id();
36324     }
36325     this.el = Roo.get(el);
36326     if(!this.el && config && config.autoCreate){
36327         if(typeof config.autoCreate == "object"){
36328             if(!config.autoCreate.id){
36329                 config.autoCreate.id = config.id||el;
36330             }
36331             this.el = Roo.DomHelper.append(document.body,
36332                         config.autoCreate, true);
36333         }else{
36334             var elcfg =  {   tag: "div",
36335                             cls: "roo-layout-inactive-content",
36336                             id: config.id||el
36337                             };
36338             if (config.html) {
36339                 elcfg.html = config.html;
36340                 
36341             }
36342                         
36343             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36344         }
36345     } 
36346     this.closable = false;
36347     this.loaded = false;
36348     this.active = false;
36349    
36350       
36351     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36352         
36353         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36354         
36355         this.wrapEl = this.el; //this.el.wrap();
36356         var ti = [];
36357         if (config.toolbar.items) {
36358             ti = config.toolbar.items ;
36359             delete config.toolbar.items ;
36360         }
36361         
36362         var nitems = [];
36363         this.toolbar.render(this.wrapEl, 'before');
36364         for(var i =0;i < ti.length;i++) {
36365           //  Roo.log(['add child', items[i]]);
36366             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36367         }
36368         this.toolbar.items = nitems;
36369         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36370         delete config.toolbar;
36371         
36372     }
36373     /*
36374     // xtype created footer. - not sure if will work as we normally have to render first..
36375     if (this.footer && !this.footer.el && this.footer.xtype) {
36376         if (!this.wrapEl) {
36377             this.wrapEl = this.el.wrap();
36378         }
36379     
36380         this.footer.container = this.wrapEl.createChild();
36381          
36382         this.footer = Roo.factory(this.footer, Roo);
36383         
36384     }
36385     */
36386     
36387      if(typeof config == "string"){
36388         this.title = config;
36389     }else{
36390         Roo.apply(this, config);
36391     }
36392     
36393     if(this.resizeEl){
36394         this.resizeEl = Roo.get(this.resizeEl, true);
36395     }else{
36396         this.resizeEl = this.el;
36397     }
36398     // handle view.xtype
36399     
36400  
36401     
36402     
36403     this.addEvents({
36404         /**
36405          * @event activate
36406          * Fires when this panel is activated. 
36407          * @param {Roo.ContentPanel} this
36408          */
36409         "activate" : true,
36410         /**
36411          * @event deactivate
36412          * Fires when this panel is activated. 
36413          * @param {Roo.ContentPanel} this
36414          */
36415         "deactivate" : true,
36416
36417         /**
36418          * @event resize
36419          * Fires when this panel is resized if fitToFrame is true.
36420          * @param {Roo.ContentPanel} this
36421          * @param {Number} width The width after any component adjustments
36422          * @param {Number} height The height after any component adjustments
36423          */
36424         "resize" : true,
36425         
36426          /**
36427          * @event render
36428          * Fires when this tab is created
36429          * @param {Roo.ContentPanel} this
36430          */
36431         "render" : true
36432         
36433         
36434         
36435     });
36436     
36437
36438     
36439     
36440     if(this.autoScroll){
36441         this.resizeEl.setStyle("overflow", "auto");
36442     } else {
36443         // fix randome scrolling
36444         //this.el.on('scroll', function() {
36445         //    Roo.log('fix random scolling');
36446         //    this.scrollTo('top',0); 
36447         //});
36448     }
36449     content = content || this.content;
36450     if(content){
36451         this.setContent(content);
36452     }
36453     if(config && config.url){
36454         this.setUrl(this.url, this.params, this.loadOnce);
36455     }
36456     
36457     
36458     
36459     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36460     
36461     if (this.view && typeof(this.view.xtype) != 'undefined') {
36462         this.view.el = this.el.appendChild(document.createElement("div"));
36463         this.view = Roo.factory(this.view); 
36464         this.view.render  &&  this.view.render(false, '');  
36465     }
36466     
36467     
36468     this.fireEvent('render', this);
36469 };
36470
36471 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36472     
36473     tabTip : '',
36474     
36475     setRegion : function(region){
36476         this.region = region;
36477         this.setActiveClass(region && !this.background);
36478     },
36479     
36480     
36481     setActiveClass: function(state)
36482     {
36483         if(state){
36484            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36485            this.el.setStyle('position','relative');
36486         }else{
36487            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36488            this.el.setStyle('position', 'absolute');
36489         } 
36490     },
36491     
36492     /**
36493      * Returns the toolbar for this Panel if one was configured. 
36494      * @return {Roo.Toolbar} 
36495      */
36496     getToolbar : function(){
36497         return this.toolbar;
36498     },
36499     
36500     setActiveState : function(active)
36501     {
36502         this.active = active;
36503         this.setActiveClass(active);
36504         if(!active){
36505             if(this.fireEvent("deactivate", this) === false){
36506                 return false;
36507             }
36508             return true;
36509         }
36510         this.fireEvent("activate", this);
36511         return true;
36512     },
36513     /**
36514      * Updates this panel's element
36515      * @param {String} content The new content
36516      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36517     */
36518     setContent : function(content, loadScripts){
36519         this.el.update(content, loadScripts);
36520     },
36521
36522     ignoreResize : function(w, h){
36523         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36524             return true;
36525         }else{
36526             this.lastSize = {width: w, height: h};
36527             return false;
36528         }
36529     },
36530     /**
36531      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36532      * @return {Roo.UpdateManager} The UpdateManager
36533      */
36534     getUpdateManager : function(){
36535         return this.el.getUpdateManager();
36536     },
36537      /**
36538      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36539      * @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:
36540 <pre><code>
36541 panel.load({
36542     url: "your-url.php",
36543     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36544     callback: yourFunction,
36545     scope: yourObject, //(optional scope)
36546     discardUrl: false,
36547     nocache: false,
36548     text: "Loading...",
36549     timeout: 30,
36550     scripts: false
36551 });
36552 </code></pre>
36553      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36554      * 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.
36555      * @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}
36556      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36557      * @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.
36558      * @return {Roo.ContentPanel} this
36559      */
36560     load : function(){
36561         var um = this.el.getUpdateManager();
36562         um.update.apply(um, arguments);
36563         return this;
36564     },
36565
36566
36567     /**
36568      * 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.
36569      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36570      * @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)
36571      * @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)
36572      * @return {Roo.UpdateManager} The UpdateManager
36573      */
36574     setUrl : function(url, params, loadOnce){
36575         if(this.refreshDelegate){
36576             this.removeListener("activate", this.refreshDelegate);
36577         }
36578         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36579         this.on("activate", this.refreshDelegate);
36580         return this.el.getUpdateManager();
36581     },
36582     
36583     _handleRefresh : function(url, params, loadOnce){
36584         if(!loadOnce || !this.loaded){
36585             var updater = this.el.getUpdateManager();
36586             updater.update(url, params, this._setLoaded.createDelegate(this));
36587         }
36588     },
36589     
36590     _setLoaded : function(){
36591         this.loaded = true;
36592     }, 
36593     
36594     /**
36595      * Returns this panel's id
36596      * @return {String} 
36597      */
36598     getId : function(){
36599         return this.el.id;
36600     },
36601     
36602     /** 
36603      * Returns this panel's element - used by regiosn to add.
36604      * @return {Roo.Element} 
36605      */
36606     getEl : function(){
36607         return this.wrapEl || this.el;
36608     },
36609     
36610    
36611     
36612     adjustForComponents : function(width, height)
36613     {
36614         //Roo.log('adjustForComponents ');
36615         if(this.resizeEl != this.el){
36616             width -= this.el.getFrameWidth('lr');
36617             height -= this.el.getFrameWidth('tb');
36618         }
36619         if(this.toolbar){
36620             var te = this.toolbar.getEl();
36621             te.setWidth(width);
36622             height -= te.getHeight();
36623         }
36624         if(this.footer){
36625             var te = this.footer.getEl();
36626             te.setWidth(width);
36627             height -= te.getHeight();
36628         }
36629         
36630         
36631         if(this.adjustments){
36632             width += this.adjustments[0];
36633             height += this.adjustments[1];
36634         }
36635         return {"width": width, "height": height};
36636     },
36637     
36638     setSize : function(width, height){
36639         if(this.fitToFrame && !this.ignoreResize(width, height)){
36640             if(this.fitContainer && this.resizeEl != this.el){
36641                 this.el.setSize(width, height);
36642             }
36643             var size = this.adjustForComponents(width, height);
36644             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36645             this.fireEvent('resize', this, size.width, size.height);
36646         }
36647     },
36648     
36649     /**
36650      * Returns this panel's title
36651      * @return {String} 
36652      */
36653     getTitle : function(){
36654         
36655         if (typeof(this.title) != 'object') {
36656             return this.title;
36657         }
36658         
36659         var t = '';
36660         for (var k in this.title) {
36661             if (!this.title.hasOwnProperty(k)) {
36662                 continue;
36663             }
36664             
36665             if (k.indexOf('-') >= 0) {
36666                 var s = k.split('-');
36667                 for (var i = 0; i<s.length; i++) {
36668                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36669                 }
36670             } else {
36671                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36672             }
36673         }
36674         return t;
36675     },
36676     
36677     /**
36678      * Set this panel's title
36679      * @param {String} title
36680      */
36681     setTitle : function(title){
36682         this.title = title;
36683         if(this.region){
36684             this.region.updatePanelTitle(this, title);
36685         }
36686     },
36687     
36688     /**
36689      * Returns true is this panel was configured to be closable
36690      * @return {Boolean} 
36691      */
36692     isClosable : function(){
36693         return this.closable;
36694     },
36695     
36696     beforeSlide : function(){
36697         this.el.clip();
36698         this.resizeEl.clip();
36699     },
36700     
36701     afterSlide : function(){
36702         this.el.unclip();
36703         this.resizeEl.unclip();
36704     },
36705     
36706     /**
36707      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36708      *   Will fail silently if the {@link #setUrl} method has not been called.
36709      *   This does not activate the panel, just updates its content.
36710      */
36711     refresh : function(){
36712         if(this.refreshDelegate){
36713            this.loaded = false;
36714            this.refreshDelegate();
36715         }
36716     },
36717     
36718     /**
36719      * Destroys this panel
36720      */
36721     destroy : function(){
36722         this.el.removeAllListeners();
36723         var tempEl = document.createElement("span");
36724         tempEl.appendChild(this.el.dom);
36725         tempEl.innerHTML = "";
36726         this.el.remove();
36727         this.el = null;
36728     },
36729     
36730     /**
36731      * form - if the content panel contains a form - this is a reference to it.
36732      * @type {Roo.form.Form}
36733      */
36734     form : false,
36735     /**
36736      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36737      *    This contains a reference to it.
36738      * @type {Roo.View}
36739      */
36740     view : false,
36741     
36742       /**
36743      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36744      * <pre><code>
36745
36746 layout.addxtype({
36747        xtype : 'Form',
36748        items: [ .... ]
36749    }
36750 );
36751
36752 </code></pre>
36753      * @param {Object} cfg Xtype definition of item to add.
36754      */
36755     
36756     
36757     getChildContainer: function () {
36758         return this.getEl();
36759     }
36760     
36761     
36762     /*
36763         var  ret = new Roo.factory(cfg);
36764         return ret;
36765         
36766         
36767         // add form..
36768         if (cfg.xtype.match(/^Form$/)) {
36769             
36770             var el;
36771             //if (this.footer) {
36772             //    el = this.footer.container.insertSibling(false, 'before');
36773             //} else {
36774                 el = this.el.createChild();
36775             //}
36776
36777             this.form = new  Roo.form.Form(cfg);
36778             
36779             
36780             if ( this.form.allItems.length) {
36781                 this.form.render(el.dom);
36782             }
36783             return this.form;
36784         }
36785         // should only have one of theses..
36786         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36787             // views.. should not be just added - used named prop 'view''
36788             
36789             cfg.el = this.el.appendChild(document.createElement("div"));
36790             // factory?
36791             
36792             var ret = new Roo.factory(cfg);
36793              
36794              ret.render && ret.render(false, ''); // render blank..
36795             this.view = ret;
36796             return ret;
36797         }
36798         return false;
36799     }
36800     \*/
36801 });
36802  
36803 /**
36804  * @class Roo.bootstrap.panel.Grid
36805  * @extends Roo.bootstrap.panel.Content
36806  * @constructor
36807  * Create a new GridPanel.
36808  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36809  * @param {Object} config A the config object
36810   
36811  */
36812
36813
36814
36815 Roo.bootstrap.panel.Grid = function(config)
36816 {
36817     
36818       
36819     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36820         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36821
36822     config.el = this.wrapper;
36823     //this.el = this.wrapper;
36824     
36825       if (config.container) {
36826         // ctor'ed from a Border/panel.grid
36827         
36828         
36829         this.wrapper.setStyle("overflow", "hidden");
36830         this.wrapper.addClass('roo-grid-container');
36831
36832     }
36833     
36834     
36835     if(config.toolbar){
36836         var tool_el = this.wrapper.createChild();    
36837         this.toolbar = Roo.factory(config.toolbar);
36838         var ti = [];
36839         if (config.toolbar.items) {
36840             ti = config.toolbar.items ;
36841             delete config.toolbar.items ;
36842         }
36843         
36844         var nitems = [];
36845         this.toolbar.render(tool_el);
36846         for(var i =0;i < ti.length;i++) {
36847           //  Roo.log(['add child', items[i]]);
36848             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36849         }
36850         this.toolbar.items = nitems;
36851         
36852         delete config.toolbar;
36853     }
36854     
36855     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36856     config.grid.scrollBody = true;;
36857     config.grid.monitorWindowResize = false; // turn off autosizing
36858     config.grid.autoHeight = false;
36859     config.grid.autoWidth = false;
36860     
36861     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36862     
36863     if (config.background) {
36864         // render grid on panel activation (if panel background)
36865         this.on('activate', function(gp) {
36866             if (!gp.grid.rendered) {
36867                 gp.grid.render(this.wrapper);
36868                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36869             }
36870         });
36871             
36872     } else {
36873         this.grid.render(this.wrapper);
36874         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36875
36876     }
36877     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36878     // ??? needed ??? config.el = this.wrapper;
36879     
36880     
36881     
36882   
36883     // xtype created footer. - not sure if will work as we normally have to render first..
36884     if (this.footer && !this.footer.el && this.footer.xtype) {
36885         
36886         var ctr = this.grid.getView().getFooterPanel(true);
36887         this.footer.dataSource = this.grid.dataSource;
36888         this.footer = Roo.factory(this.footer, Roo);
36889         this.footer.render(ctr);
36890         
36891     }
36892     
36893     
36894     
36895     
36896      
36897 };
36898
36899 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36900     getId : function(){
36901         return this.grid.id;
36902     },
36903     
36904     /**
36905      * Returns the grid for this panel
36906      * @return {Roo.bootstrap.Table} 
36907      */
36908     getGrid : function(){
36909         return this.grid;    
36910     },
36911     
36912     setSize : function(width, height){
36913         if(!this.ignoreResize(width, height)){
36914             var grid = this.grid;
36915             var size = this.adjustForComponents(width, height);
36916             var gridel = grid.getGridEl();
36917             gridel.setSize(size.width, size.height);
36918             /*
36919             var thd = grid.getGridEl().select('thead',true).first();
36920             var tbd = grid.getGridEl().select('tbody', true).first();
36921             if (tbd) {
36922                 tbd.setSize(width, height - thd.getHeight());
36923             }
36924             */
36925             grid.autoSize();
36926         }
36927     },
36928      
36929     
36930     
36931     beforeSlide : function(){
36932         this.grid.getView().scroller.clip();
36933     },
36934     
36935     afterSlide : function(){
36936         this.grid.getView().scroller.unclip();
36937     },
36938     
36939     destroy : function(){
36940         this.grid.destroy();
36941         delete this.grid;
36942         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36943     }
36944 });
36945
36946 /**
36947  * @class Roo.bootstrap.panel.Nest
36948  * @extends Roo.bootstrap.panel.Content
36949  * @constructor
36950  * Create a new Panel, that can contain a layout.Border.
36951  * 
36952  * 
36953  * @param {Roo.BorderLayout} layout The layout for this panel
36954  * @param {String/Object} config A string to set only the title or a config object
36955  */
36956 Roo.bootstrap.panel.Nest = function(config)
36957 {
36958     // construct with only one argument..
36959     /* FIXME - implement nicer consturctors
36960     if (layout.layout) {
36961         config = layout;
36962         layout = config.layout;
36963         delete config.layout;
36964     }
36965     if (layout.xtype && !layout.getEl) {
36966         // then layout needs constructing..
36967         layout = Roo.factory(layout, Roo);
36968     }
36969     */
36970     
36971     config.el =  config.layout.getEl();
36972     
36973     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36974     
36975     config.layout.monitorWindowResize = false; // turn off autosizing
36976     this.layout = config.layout;
36977     this.layout.getEl().addClass("roo-layout-nested-layout");
36978     
36979     
36980     
36981     
36982 };
36983
36984 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36985
36986     setSize : function(width, height){
36987         if(!this.ignoreResize(width, height)){
36988             var size = this.adjustForComponents(width, height);
36989             var el = this.layout.getEl();
36990             if (size.height < 1) {
36991                 el.setWidth(size.width);   
36992             } else {
36993                 el.setSize(size.width, size.height);
36994             }
36995             var touch = el.dom.offsetWidth;
36996             this.layout.layout();
36997             // ie requires a double layout on the first pass
36998             if(Roo.isIE && !this.initialized){
36999                 this.initialized = true;
37000                 this.layout.layout();
37001             }
37002         }
37003     },
37004     
37005     // activate all subpanels if not currently active..
37006     
37007     setActiveState : function(active){
37008         this.active = active;
37009         this.setActiveClass(active);
37010         
37011         if(!active){
37012             this.fireEvent("deactivate", this);
37013             return;
37014         }
37015         
37016         this.fireEvent("activate", this);
37017         // not sure if this should happen before or after..
37018         if (!this.layout) {
37019             return; // should not happen..
37020         }
37021         var reg = false;
37022         for (var r in this.layout.regions) {
37023             reg = this.layout.getRegion(r);
37024             if (reg.getActivePanel()) {
37025                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37026                 reg.setActivePanel(reg.getActivePanel());
37027                 continue;
37028             }
37029             if (!reg.panels.length) {
37030                 continue;
37031             }
37032             reg.showPanel(reg.getPanel(0));
37033         }
37034         
37035         
37036         
37037         
37038     },
37039     
37040     /**
37041      * Returns the nested BorderLayout for this panel
37042      * @return {Roo.BorderLayout} 
37043      */
37044     getLayout : function(){
37045         return this.layout;
37046     },
37047     
37048      /**
37049      * Adds a xtype elements to the layout of the nested panel
37050      * <pre><code>
37051
37052 panel.addxtype({
37053        xtype : 'ContentPanel',
37054        region: 'west',
37055        items: [ .... ]
37056    }
37057 );
37058
37059 panel.addxtype({
37060         xtype : 'NestedLayoutPanel',
37061         region: 'west',
37062         layout: {
37063            center: { },
37064            west: { }   
37065         },
37066         items : [ ... list of content panels or nested layout panels.. ]
37067    }
37068 );
37069 </code></pre>
37070      * @param {Object} cfg Xtype definition of item to add.
37071      */
37072     addxtype : function(cfg) {
37073         return this.layout.addxtype(cfg);
37074     
37075     }
37076 });        /*
37077  * Based on:
37078  * Ext JS Library 1.1.1
37079  * Copyright(c) 2006-2007, Ext JS, LLC.
37080  *
37081  * Originally Released Under LGPL - original licence link has changed is not relivant.
37082  *
37083  * Fork - LGPL
37084  * <script type="text/javascript">
37085  */
37086 /**
37087  * @class Roo.TabPanel
37088  * @extends Roo.util.Observable
37089  * A lightweight tab container.
37090  * <br><br>
37091  * Usage:
37092  * <pre><code>
37093 // basic tabs 1, built from existing content
37094 var tabs = new Roo.TabPanel("tabs1");
37095 tabs.addTab("script", "View Script");
37096 tabs.addTab("markup", "View Markup");
37097 tabs.activate("script");
37098
37099 // more advanced tabs, built from javascript
37100 var jtabs = new Roo.TabPanel("jtabs");
37101 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37102
37103 // set up the UpdateManager
37104 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37105 var updater = tab2.getUpdateManager();
37106 updater.setDefaultUrl("ajax1.htm");
37107 tab2.on('activate', updater.refresh, updater, true);
37108
37109 // Use setUrl for Ajax loading
37110 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37111 tab3.setUrl("ajax2.htm", null, true);
37112
37113 // Disabled tab
37114 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37115 tab4.disable();
37116
37117 jtabs.activate("jtabs-1");
37118  * </code></pre>
37119  * @constructor
37120  * Create a new TabPanel.
37121  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37122  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37123  */
37124 Roo.bootstrap.panel.Tabs = function(config){
37125     /**
37126     * The container element for this TabPanel.
37127     * @type Roo.Element
37128     */
37129     this.el = Roo.get(config.el);
37130     delete config.el;
37131     if(config){
37132         if(typeof config == "boolean"){
37133             this.tabPosition = config ? "bottom" : "top";
37134         }else{
37135             Roo.apply(this, config);
37136         }
37137     }
37138     
37139     if(this.tabPosition == "bottom"){
37140         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37141         this.el.addClass("roo-tabs-bottom");
37142     }
37143     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37144     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37145     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37146     if(Roo.isIE){
37147         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37148     }
37149     if(this.tabPosition != "bottom"){
37150         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37151          * @type Roo.Element
37152          */
37153         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37154         this.el.addClass("roo-tabs-top");
37155     }
37156     this.items = [];
37157
37158     this.bodyEl.setStyle("position", "relative");
37159
37160     this.active = null;
37161     this.activateDelegate = this.activate.createDelegate(this);
37162
37163     this.addEvents({
37164         /**
37165          * @event tabchange
37166          * Fires when the active tab changes
37167          * @param {Roo.TabPanel} this
37168          * @param {Roo.TabPanelItem} activePanel The new active tab
37169          */
37170         "tabchange": true,
37171         /**
37172          * @event beforetabchange
37173          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37174          * @param {Roo.TabPanel} this
37175          * @param {Object} e Set cancel to true on this object to cancel the tab change
37176          * @param {Roo.TabPanelItem} tab The tab being changed to
37177          */
37178         "beforetabchange" : true
37179     });
37180
37181     Roo.EventManager.onWindowResize(this.onResize, this);
37182     this.cpad = this.el.getPadding("lr");
37183     this.hiddenCount = 0;
37184
37185
37186     // toolbar on the tabbar support...
37187     if (this.toolbar) {
37188         alert("no toolbar support yet");
37189         this.toolbar  = false;
37190         /*
37191         var tcfg = this.toolbar;
37192         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37193         this.toolbar = new Roo.Toolbar(tcfg);
37194         if (Roo.isSafari) {
37195             var tbl = tcfg.container.child('table', true);
37196             tbl.setAttribute('width', '100%');
37197         }
37198         */
37199         
37200     }
37201    
37202
37203
37204     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37205 };
37206
37207 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37208     /*
37209      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37210      */
37211     tabPosition : "top",
37212     /*
37213      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37214      */
37215     currentTabWidth : 0,
37216     /*
37217      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37218      */
37219     minTabWidth : 40,
37220     /*
37221      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37222      */
37223     maxTabWidth : 250,
37224     /*
37225      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37226      */
37227     preferredTabWidth : 175,
37228     /*
37229      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37230      */
37231     resizeTabs : false,
37232     /*
37233      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37234      */
37235     monitorResize : true,
37236     /*
37237      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37238      */
37239     toolbar : false,
37240
37241     /**
37242      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37243      * @param {String} id The id of the div to use <b>or create</b>
37244      * @param {String} text The text for the tab
37245      * @param {String} content (optional) Content to put in the TabPanelItem body
37246      * @param {Boolean} closable (optional) True to create a close icon on the tab
37247      * @return {Roo.TabPanelItem} The created TabPanelItem
37248      */
37249     addTab : function(id, text, content, closable, tpl)
37250     {
37251         var item = new Roo.bootstrap.panel.TabItem({
37252             panel: this,
37253             id : id,
37254             text : text,
37255             closable : closable,
37256             tpl : tpl
37257         });
37258         this.addTabItem(item);
37259         if(content){
37260             item.setContent(content);
37261         }
37262         return item;
37263     },
37264
37265     /**
37266      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37267      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37268      * @return {Roo.TabPanelItem}
37269      */
37270     getTab : function(id){
37271         return this.items[id];
37272     },
37273
37274     /**
37275      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37276      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37277      */
37278     hideTab : function(id){
37279         var t = this.items[id];
37280         if(!t.isHidden()){
37281            t.setHidden(true);
37282            this.hiddenCount++;
37283            this.autoSizeTabs();
37284         }
37285     },
37286
37287     /**
37288      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37289      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37290      */
37291     unhideTab : function(id){
37292         var t = this.items[id];
37293         if(t.isHidden()){
37294            t.setHidden(false);
37295            this.hiddenCount--;
37296            this.autoSizeTabs();
37297         }
37298     },
37299
37300     /**
37301      * Adds an existing {@link Roo.TabPanelItem}.
37302      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37303      */
37304     addTabItem : function(item){
37305         this.items[item.id] = item;
37306         this.items.push(item);
37307       //  if(this.resizeTabs){
37308     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37309   //         this.autoSizeTabs();
37310 //        }else{
37311 //            item.autoSize();
37312        // }
37313     },
37314
37315     /**
37316      * Removes a {@link Roo.TabPanelItem}.
37317      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37318      */
37319     removeTab : function(id){
37320         var items = this.items;
37321         var tab = items[id];
37322         if(!tab) { return; }
37323         var index = items.indexOf(tab);
37324         if(this.active == tab && items.length > 1){
37325             var newTab = this.getNextAvailable(index);
37326             if(newTab) {
37327                 newTab.activate();
37328             }
37329         }
37330         this.stripEl.dom.removeChild(tab.pnode.dom);
37331         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37332             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37333         }
37334         items.splice(index, 1);
37335         delete this.items[tab.id];
37336         tab.fireEvent("close", tab);
37337         tab.purgeListeners();
37338         this.autoSizeTabs();
37339     },
37340
37341     getNextAvailable : function(start){
37342         var items = this.items;
37343         var index = start;
37344         // look for a next tab that will slide over to
37345         // replace the one being removed
37346         while(index < items.length){
37347             var item = items[++index];
37348             if(item && !item.isHidden()){
37349                 return item;
37350             }
37351         }
37352         // if one isn't found select the previous tab (on the left)
37353         index = start;
37354         while(index >= 0){
37355             var item = items[--index];
37356             if(item && !item.isHidden()){
37357                 return item;
37358             }
37359         }
37360         return null;
37361     },
37362
37363     /**
37364      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37365      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37366      */
37367     disableTab : function(id){
37368         var tab = this.items[id];
37369         if(tab && this.active != tab){
37370             tab.disable();
37371         }
37372     },
37373
37374     /**
37375      * Enables a {@link Roo.TabPanelItem} that is disabled.
37376      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37377      */
37378     enableTab : function(id){
37379         var tab = this.items[id];
37380         tab.enable();
37381     },
37382
37383     /**
37384      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37385      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37386      * @return {Roo.TabPanelItem} The TabPanelItem.
37387      */
37388     activate : function(id){
37389         var tab = this.items[id];
37390         if(!tab){
37391             return null;
37392         }
37393         if(tab == this.active || tab.disabled){
37394             return tab;
37395         }
37396         var e = {};
37397         this.fireEvent("beforetabchange", this, e, tab);
37398         if(e.cancel !== true && !tab.disabled){
37399             if(this.active){
37400                 this.active.hide();
37401             }
37402             this.active = this.items[id];
37403             this.active.show();
37404             this.fireEvent("tabchange", this, this.active);
37405         }
37406         return tab;
37407     },
37408
37409     /**
37410      * Gets the active {@link Roo.TabPanelItem}.
37411      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37412      */
37413     getActiveTab : function(){
37414         return this.active;
37415     },
37416
37417     /**
37418      * Updates the tab body element to fit the height of the container element
37419      * for overflow scrolling
37420      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37421      */
37422     syncHeight : function(targetHeight){
37423         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37424         var bm = this.bodyEl.getMargins();
37425         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37426         this.bodyEl.setHeight(newHeight);
37427         return newHeight;
37428     },
37429
37430     onResize : function(){
37431         if(this.monitorResize){
37432             this.autoSizeTabs();
37433         }
37434     },
37435
37436     /**
37437      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37438      */
37439     beginUpdate : function(){
37440         this.updating = true;
37441     },
37442
37443     /**
37444      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37445      */
37446     endUpdate : function(){
37447         this.updating = false;
37448         this.autoSizeTabs();
37449     },
37450
37451     /**
37452      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37453      */
37454     autoSizeTabs : function(){
37455         var count = this.items.length;
37456         var vcount = count - this.hiddenCount;
37457         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37458             return;
37459         }
37460         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37461         var availWidth = Math.floor(w / vcount);
37462         var b = this.stripBody;
37463         if(b.getWidth() > w){
37464             var tabs = this.items;
37465             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37466             if(availWidth < this.minTabWidth){
37467                 /*if(!this.sleft){    // incomplete scrolling code
37468                     this.createScrollButtons();
37469                 }
37470                 this.showScroll();
37471                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37472             }
37473         }else{
37474             if(this.currentTabWidth < this.preferredTabWidth){
37475                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37476             }
37477         }
37478     },
37479
37480     /**
37481      * Returns the number of tabs in this TabPanel.
37482      * @return {Number}
37483      */
37484      getCount : function(){
37485          return this.items.length;
37486      },
37487
37488     /**
37489      * Resizes all the tabs to the passed width
37490      * @param {Number} The new width
37491      */
37492     setTabWidth : function(width){
37493         this.currentTabWidth = width;
37494         for(var i = 0, len = this.items.length; i < len; i++) {
37495                 if(!this.items[i].isHidden()) {
37496                 this.items[i].setWidth(width);
37497             }
37498         }
37499     },
37500
37501     /**
37502      * Destroys this TabPanel
37503      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37504      */
37505     destroy : function(removeEl){
37506         Roo.EventManager.removeResizeListener(this.onResize, this);
37507         for(var i = 0, len = this.items.length; i < len; i++){
37508             this.items[i].purgeListeners();
37509         }
37510         if(removeEl === true){
37511             this.el.update("");
37512             this.el.remove();
37513         }
37514     },
37515     
37516     createStrip : function(container)
37517     {
37518         var strip = document.createElement("nav");
37519         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37520         container.appendChild(strip);
37521         return strip;
37522     },
37523     
37524     createStripList : function(strip)
37525     {
37526         // div wrapper for retard IE
37527         // returns the "tr" element.
37528         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37529         //'<div class="x-tabs-strip-wrap">'+
37530           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37531           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37532         return strip.firstChild; //.firstChild.firstChild.firstChild;
37533     },
37534     createBody : function(container)
37535     {
37536         var body = document.createElement("div");
37537         Roo.id(body, "tab-body");
37538         //Roo.fly(body).addClass("x-tabs-body");
37539         Roo.fly(body).addClass("tab-content");
37540         container.appendChild(body);
37541         return body;
37542     },
37543     createItemBody :function(bodyEl, id){
37544         var body = Roo.getDom(id);
37545         if(!body){
37546             body = document.createElement("div");
37547             body.id = id;
37548         }
37549         //Roo.fly(body).addClass("x-tabs-item-body");
37550         Roo.fly(body).addClass("tab-pane");
37551          bodyEl.insertBefore(body, bodyEl.firstChild);
37552         return body;
37553     },
37554     /** @private */
37555     createStripElements :  function(stripEl, text, closable, tpl)
37556     {
37557         var td = document.createElement("li"); // was td..
37558         
37559         
37560         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37561         
37562         
37563         stripEl.appendChild(td);
37564         /*if(closable){
37565             td.className = "x-tabs-closable";
37566             if(!this.closeTpl){
37567                 this.closeTpl = new Roo.Template(
37568                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37569                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37570                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37571                 );
37572             }
37573             var el = this.closeTpl.overwrite(td, {"text": text});
37574             var close = el.getElementsByTagName("div")[0];
37575             var inner = el.getElementsByTagName("em")[0];
37576             return {"el": el, "close": close, "inner": inner};
37577         } else {
37578         */
37579         // not sure what this is..
37580 //            if(!this.tabTpl){
37581                 //this.tabTpl = new Roo.Template(
37582                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37583                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37584                 //);
37585 //                this.tabTpl = new Roo.Template(
37586 //                   '<a href="#">' +
37587 //                   '<span unselectable="on"' +
37588 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37589 //                            ' >{text}</span></a>'
37590 //                );
37591 //                
37592 //            }
37593
37594
37595             var template = tpl || this.tabTpl || false;
37596             
37597             if(!template){
37598                 
37599                 template = new Roo.Template(
37600                    '<a href="#">' +
37601                    '<span unselectable="on"' +
37602                             (this.disableTooltips ? '' : ' title="{text}"') +
37603                             ' >{text}</span></a>'
37604                 );
37605             }
37606             
37607             switch (typeof(template)) {
37608                 case 'object' :
37609                     break;
37610                 case 'string' :
37611                     template = new Roo.Template(template);
37612                     break;
37613                 default :
37614                     break;
37615             }
37616             
37617             var el = template.overwrite(td, {"text": text});
37618             
37619             var inner = el.getElementsByTagName("span")[0];
37620             
37621             return {"el": el, "inner": inner};
37622             
37623     }
37624         
37625     
37626 });
37627
37628 /**
37629  * @class Roo.TabPanelItem
37630  * @extends Roo.util.Observable
37631  * Represents an individual item (tab plus body) in a TabPanel.
37632  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37633  * @param {String} id The id of this TabPanelItem
37634  * @param {String} text The text for the tab of this TabPanelItem
37635  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37636  */
37637 Roo.bootstrap.panel.TabItem = function(config){
37638     /**
37639      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37640      * @type Roo.TabPanel
37641      */
37642     this.tabPanel = config.panel;
37643     /**
37644      * The id for this TabPanelItem
37645      * @type String
37646      */
37647     this.id = config.id;
37648     /** @private */
37649     this.disabled = false;
37650     /** @private */
37651     this.text = config.text;
37652     /** @private */
37653     this.loaded = false;
37654     this.closable = config.closable;
37655
37656     /**
37657      * The body element for this TabPanelItem.
37658      * @type Roo.Element
37659      */
37660     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37661     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37662     this.bodyEl.setStyle("display", "block");
37663     this.bodyEl.setStyle("zoom", "1");
37664     //this.hideAction();
37665
37666     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37667     /** @private */
37668     this.el = Roo.get(els.el);
37669     this.inner = Roo.get(els.inner, true);
37670     this.textEl = Roo.get(this.el.dom.firstChild, true);
37671     this.pnode = Roo.get(els.el.parentNode, true);
37672 //    this.el.on("mousedown", this.onTabMouseDown, this);
37673     this.el.on("click", this.onTabClick, this);
37674     /** @private */
37675     if(config.closable){
37676         var c = Roo.get(els.close, true);
37677         c.dom.title = this.closeText;
37678         c.addClassOnOver("close-over");
37679         c.on("click", this.closeClick, this);
37680      }
37681
37682     this.addEvents({
37683          /**
37684          * @event activate
37685          * Fires when this tab becomes the active tab.
37686          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37687          * @param {Roo.TabPanelItem} this
37688          */
37689         "activate": true,
37690         /**
37691          * @event beforeclose
37692          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37693          * @param {Roo.TabPanelItem} this
37694          * @param {Object} e Set cancel to true on this object to cancel the close.
37695          */
37696         "beforeclose": true,
37697         /**
37698          * @event close
37699          * Fires when this tab is closed.
37700          * @param {Roo.TabPanelItem} this
37701          */
37702          "close": true,
37703         /**
37704          * @event deactivate
37705          * Fires when this tab is no longer the active tab.
37706          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37707          * @param {Roo.TabPanelItem} this
37708          */
37709          "deactivate" : true
37710     });
37711     this.hidden = false;
37712
37713     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37714 };
37715
37716 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37717            {
37718     purgeListeners : function(){
37719        Roo.util.Observable.prototype.purgeListeners.call(this);
37720        this.el.removeAllListeners();
37721     },
37722     /**
37723      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37724      */
37725     show : function(){
37726         this.pnode.addClass("active");
37727         this.showAction();
37728         if(Roo.isOpera){
37729             this.tabPanel.stripWrap.repaint();
37730         }
37731         this.fireEvent("activate", this.tabPanel, this);
37732     },
37733
37734     /**
37735      * Returns true if this tab is the active tab.
37736      * @return {Boolean}
37737      */
37738     isActive : function(){
37739         return this.tabPanel.getActiveTab() == this;
37740     },
37741
37742     /**
37743      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37744      */
37745     hide : function(){
37746         this.pnode.removeClass("active");
37747         this.hideAction();
37748         this.fireEvent("deactivate", this.tabPanel, this);
37749     },
37750
37751     hideAction : function(){
37752         this.bodyEl.hide();
37753         this.bodyEl.setStyle("position", "absolute");
37754         this.bodyEl.setLeft("-20000px");
37755         this.bodyEl.setTop("-20000px");
37756     },
37757
37758     showAction : function(){
37759         this.bodyEl.setStyle("position", "relative");
37760         this.bodyEl.setTop("");
37761         this.bodyEl.setLeft("");
37762         this.bodyEl.show();
37763     },
37764
37765     /**
37766      * Set the tooltip for the tab.
37767      * @param {String} tooltip The tab's tooltip
37768      */
37769     setTooltip : function(text){
37770         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37771             this.textEl.dom.qtip = text;
37772             this.textEl.dom.removeAttribute('title');
37773         }else{
37774             this.textEl.dom.title = text;
37775         }
37776     },
37777
37778     onTabClick : function(e){
37779         e.preventDefault();
37780         this.tabPanel.activate(this.id);
37781     },
37782
37783     onTabMouseDown : function(e){
37784         e.preventDefault();
37785         this.tabPanel.activate(this.id);
37786     },
37787 /*
37788     getWidth : function(){
37789         return this.inner.getWidth();
37790     },
37791
37792     setWidth : function(width){
37793         var iwidth = width - this.pnode.getPadding("lr");
37794         this.inner.setWidth(iwidth);
37795         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37796         this.pnode.setWidth(width);
37797     },
37798 */
37799     /**
37800      * Show or hide the tab
37801      * @param {Boolean} hidden True to hide or false to show.
37802      */
37803     setHidden : function(hidden){
37804         this.hidden = hidden;
37805         this.pnode.setStyle("display", hidden ? "none" : "");
37806     },
37807
37808     /**
37809      * Returns true if this tab is "hidden"
37810      * @return {Boolean}
37811      */
37812     isHidden : function(){
37813         return this.hidden;
37814     },
37815
37816     /**
37817      * Returns the text for this tab
37818      * @return {String}
37819      */
37820     getText : function(){
37821         return this.text;
37822     },
37823     /*
37824     autoSize : function(){
37825         //this.el.beginMeasure();
37826         this.textEl.setWidth(1);
37827         /*
37828          *  #2804 [new] Tabs in Roojs
37829          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37830          */
37831         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37832         //this.el.endMeasure();
37833     //},
37834
37835     /**
37836      * Sets the text for the tab (Note: this also sets the tooltip text)
37837      * @param {String} text The tab's text and tooltip
37838      */
37839     setText : function(text){
37840         this.text = text;
37841         this.textEl.update(text);
37842         this.setTooltip(text);
37843         //if(!this.tabPanel.resizeTabs){
37844         //    this.autoSize();
37845         //}
37846     },
37847     /**
37848      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37849      */
37850     activate : function(){
37851         this.tabPanel.activate(this.id);
37852     },
37853
37854     /**
37855      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37856      */
37857     disable : function(){
37858         if(this.tabPanel.active != this){
37859             this.disabled = true;
37860             this.pnode.addClass("disabled");
37861         }
37862     },
37863
37864     /**
37865      * Enables this TabPanelItem if it was previously disabled.
37866      */
37867     enable : function(){
37868         this.disabled = false;
37869         this.pnode.removeClass("disabled");
37870     },
37871
37872     /**
37873      * Sets the content for this TabPanelItem.
37874      * @param {String} content The content
37875      * @param {Boolean} loadScripts true to look for and load scripts
37876      */
37877     setContent : function(content, loadScripts){
37878         this.bodyEl.update(content, loadScripts);
37879     },
37880
37881     /**
37882      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37883      * @return {Roo.UpdateManager} The UpdateManager
37884      */
37885     getUpdateManager : function(){
37886         return this.bodyEl.getUpdateManager();
37887     },
37888
37889     /**
37890      * Set a URL to be used to load the content for this TabPanelItem.
37891      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37892      * @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)
37893      * @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)
37894      * @return {Roo.UpdateManager} The UpdateManager
37895      */
37896     setUrl : function(url, params, loadOnce){
37897         if(this.refreshDelegate){
37898             this.un('activate', this.refreshDelegate);
37899         }
37900         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37901         this.on("activate", this.refreshDelegate);
37902         return this.bodyEl.getUpdateManager();
37903     },
37904
37905     /** @private */
37906     _handleRefresh : function(url, params, loadOnce){
37907         if(!loadOnce || !this.loaded){
37908             var updater = this.bodyEl.getUpdateManager();
37909             updater.update(url, params, this._setLoaded.createDelegate(this));
37910         }
37911     },
37912
37913     /**
37914      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37915      *   Will fail silently if the setUrl method has not been called.
37916      *   This does not activate the panel, just updates its content.
37917      */
37918     refresh : function(){
37919         if(this.refreshDelegate){
37920            this.loaded = false;
37921            this.refreshDelegate();
37922         }
37923     },
37924
37925     /** @private */
37926     _setLoaded : function(){
37927         this.loaded = true;
37928     },
37929
37930     /** @private */
37931     closeClick : function(e){
37932         var o = {};
37933         e.stopEvent();
37934         this.fireEvent("beforeclose", this, o);
37935         if(o.cancel !== true){
37936             this.tabPanel.removeTab(this.id);
37937         }
37938     },
37939     /**
37940      * The text displayed in the tooltip for the close icon.
37941      * @type String
37942      */
37943     closeText : "Close this tab"
37944 });
37945 /**
37946 *    This script refer to:
37947 *    Title: International Telephone Input
37948 *    Author: Jack O'Connor
37949 *    Code version:  v12.1.12
37950 *    Availability: https://github.com/jackocnr/intl-tel-input.git
37951 **/
37952
37953 Roo.bootstrap.PhoneInputData = function() {
37954     var d = [
37955       [
37956         "Afghanistan (‫افغانستان‬‎)",
37957         "af",
37958         "93"
37959       ],
37960       [
37961         "Albania (Shqipëri)",
37962         "al",
37963         "355"
37964       ],
37965       [
37966         "Algeria (‫الجزائر‬‎)",
37967         "dz",
37968         "213"
37969       ],
37970       [
37971         "American Samoa",
37972         "as",
37973         "1684"
37974       ],
37975       [
37976         "Andorra",
37977         "ad",
37978         "376"
37979       ],
37980       [
37981         "Angola",
37982         "ao",
37983         "244"
37984       ],
37985       [
37986         "Anguilla",
37987         "ai",
37988         "1264"
37989       ],
37990       [
37991         "Antigua and Barbuda",
37992         "ag",
37993         "1268"
37994       ],
37995       [
37996         "Argentina",
37997         "ar",
37998         "54"
37999       ],
38000       [
38001         "Armenia (Հայաստան)",
38002         "am",
38003         "374"
38004       ],
38005       [
38006         "Aruba",
38007         "aw",
38008         "297"
38009       ],
38010       [
38011         "Australia",
38012         "au",
38013         "61",
38014         0
38015       ],
38016       [
38017         "Austria (Österreich)",
38018         "at",
38019         "43"
38020       ],
38021       [
38022         "Azerbaijan (Azərbaycan)",
38023         "az",
38024         "994"
38025       ],
38026       [
38027         "Bahamas",
38028         "bs",
38029         "1242"
38030       ],
38031       [
38032         "Bahrain (‫البحرين‬‎)",
38033         "bh",
38034         "973"
38035       ],
38036       [
38037         "Bangladesh (বাংলাদেশ)",
38038         "bd",
38039         "880"
38040       ],
38041       [
38042         "Barbados",
38043         "bb",
38044         "1246"
38045       ],
38046       [
38047         "Belarus (Беларусь)",
38048         "by",
38049         "375"
38050       ],
38051       [
38052         "Belgium (België)",
38053         "be",
38054         "32"
38055       ],
38056       [
38057         "Belize",
38058         "bz",
38059         "501"
38060       ],
38061       [
38062         "Benin (Bénin)",
38063         "bj",
38064         "229"
38065       ],
38066       [
38067         "Bermuda",
38068         "bm",
38069         "1441"
38070       ],
38071       [
38072         "Bhutan (འབྲུག)",
38073         "bt",
38074         "975"
38075       ],
38076       [
38077         "Bolivia",
38078         "bo",
38079         "591"
38080       ],
38081       [
38082         "Bosnia and Herzegovina (Босна и Херцеговина)",
38083         "ba",
38084         "387"
38085       ],
38086       [
38087         "Botswana",
38088         "bw",
38089         "267"
38090       ],
38091       [
38092         "Brazil (Brasil)",
38093         "br",
38094         "55"
38095       ],
38096       [
38097         "British Indian Ocean Territory",
38098         "io",
38099         "246"
38100       ],
38101       [
38102         "British Virgin Islands",
38103         "vg",
38104         "1284"
38105       ],
38106       [
38107         "Brunei",
38108         "bn",
38109         "673"
38110       ],
38111       [
38112         "Bulgaria (България)",
38113         "bg",
38114         "359"
38115       ],
38116       [
38117         "Burkina Faso",
38118         "bf",
38119         "226"
38120       ],
38121       [
38122         "Burundi (Uburundi)",
38123         "bi",
38124         "257"
38125       ],
38126       [
38127         "Cambodia (កម្ពុជា)",
38128         "kh",
38129         "855"
38130       ],
38131       [
38132         "Cameroon (Cameroun)",
38133         "cm",
38134         "237"
38135       ],
38136       [
38137         "Canada",
38138         "ca",
38139         "1",
38140         1,
38141         ["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"]
38142       ],
38143       [
38144         "Cape Verde (Kabu Verdi)",
38145         "cv",
38146         "238"
38147       ],
38148       [
38149         "Caribbean Netherlands",
38150         "bq",
38151         "599",
38152         1
38153       ],
38154       [
38155         "Cayman Islands",
38156         "ky",
38157         "1345"
38158       ],
38159       [
38160         "Central African Republic (République centrafricaine)",
38161         "cf",
38162         "236"
38163       ],
38164       [
38165         "Chad (Tchad)",
38166         "td",
38167         "235"
38168       ],
38169       [
38170         "Chile",
38171         "cl",
38172         "56"
38173       ],
38174       [
38175         "China (中国)",
38176         "cn",
38177         "86"
38178       ],
38179       [
38180         "Christmas Island",
38181         "cx",
38182         "61",
38183         2
38184       ],
38185       [
38186         "Cocos (Keeling) Islands",
38187         "cc",
38188         "61",
38189         1
38190       ],
38191       [
38192         "Colombia",
38193         "co",
38194         "57"
38195       ],
38196       [
38197         "Comoros (‫جزر القمر‬‎)",
38198         "km",
38199         "269"
38200       ],
38201       [
38202         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38203         "cd",
38204         "243"
38205       ],
38206       [
38207         "Congo (Republic) (Congo-Brazzaville)",
38208         "cg",
38209         "242"
38210       ],
38211       [
38212         "Cook Islands",
38213         "ck",
38214         "682"
38215       ],
38216       [
38217         "Costa Rica",
38218         "cr",
38219         "506"
38220       ],
38221       [
38222         "Côte d’Ivoire",
38223         "ci",
38224         "225"
38225       ],
38226       [
38227         "Croatia (Hrvatska)",
38228         "hr",
38229         "385"
38230       ],
38231       [
38232         "Cuba",
38233         "cu",
38234         "53"
38235       ],
38236       [
38237         "Curaçao",
38238         "cw",
38239         "599",
38240         0
38241       ],
38242       [
38243         "Cyprus (Κύπρος)",
38244         "cy",
38245         "357"
38246       ],
38247       [
38248         "Czech Republic (Česká republika)",
38249         "cz",
38250         "420"
38251       ],
38252       [
38253         "Denmark (Danmark)",
38254         "dk",
38255         "45"
38256       ],
38257       [
38258         "Djibouti",
38259         "dj",
38260         "253"
38261       ],
38262       [
38263         "Dominica",
38264         "dm",
38265         "1767"
38266       ],
38267       [
38268         "Dominican Republic (República Dominicana)",
38269         "do",
38270         "1",
38271         2,
38272         ["809", "829", "849"]
38273       ],
38274       [
38275         "Ecuador",
38276         "ec",
38277         "593"
38278       ],
38279       [
38280         "Egypt (‫مصر‬‎)",
38281         "eg",
38282         "20"
38283       ],
38284       [
38285         "El Salvador",
38286         "sv",
38287         "503"
38288       ],
38289       [
38290         "Equatorial Guinea (Guinea Ecuatorial)",
38291         "gq",
38292         "240"
38293       ],
38294       [
38295         "Eritrea",
38296         "er",
38297         "291"
38298       ],
38299       [
38300         "Estonia (Eesti)",
38301         "ee",
38302         "372"
38303       ],
38304       [
38305         "Ethiopia",
38306         "et",
38307         "251"
38308       ],
38309       [
38310         "Falkland Islands (Islas Malvinas)",
38311         "fk",
38312         "500"
38313       ],
38314       [
38315         "Faroe Islands (Føroyar)",
38316         "fo",
38317         "298"
38318       ],
38319       [
38320         "Fiji",
38321         "fj",
38322         "679"
38323       ],
38324       [
38325         "Finland (Suomi)",
38326         "fi",
38327         "358",
38328         0
38329       ],
38330       [
38331         "France",
38332         "fr",
38333         "33"
38334       ],
38335       [
38336         "French Guiana (Guyane française)",
38337         "gf",
38338         "594"
38339       ],
38340       [
38341         "French Polynesia (Polynésie française)",
38342         "pf",
38343         "689"
38344       ],
38345       [
38346         "Gabon",
38347         "ga",
38348         "241"
38349       ],
38350       [
38351         "Gambia",
38352         "gm",
38353         "220"
38354       ],
38355       [
38356         "Georgia (საქართველო)",
38357         "ge",
38358         "995"
38359       ],
38360       [
38361         "Germany (Deutschland)",
38362         "de",
38363         "49"
38364       ],
38365       [
38366         "Ghana (Gaana)",
38367         "gh",
38368         "233"
38369       ],
38370       [
38371         "Gibraltar",
38372         "gi",
38373         "350"
38374       ],
38375       [
38376         "Greece (Ελλάδα)",
38377         "gr",
38378         "30"
38379       ],
38380       [
38381         "Greenland (Kalaallit Nunaat)",
38382         "gl",
38383         "299"
38384       ],
38385       [
38386         "Grenada",
38387         "gd",
38388         "1473"
38389       ],
38390       [
38391         "Guadeloupe",
38392         "gp",
38393         "590",
38394         0
38395       ],
38396       [
38397         "Guam",
38398         "gu",
38399         "1671"
38400       ],
38401       [
38402         "Guatemala",
38403         "gt",
38404         "502"
38405       ],
38406       [
38407         "Guernsey",
38408         "gg",
38409         "44",
38410         1
38411       ],
38412       [
38413         "Guinea (Guinée)",
38414         "gn",
38415         "224"
38416       ],
38417       [
38418         "Guinea-Bissau (Guiné Bissau)",
38419         "gw",
38420         "245"
38421       ],
38422       [
38423         "Guyana",
38424         "gy",
38425         "592"
38426       ],
38427       [
38428         "Haiti",
38429         "ht",
38430         "509"
38431       ],
38432       [
38433         "Honduras",
38434         "hn",
38435         "504"
38436       ],
38437       [
38438         "Hong Kong (香港)",
38439         "hk",
38440         "852"
38441       ],
38442       [
38443         "Hungary (Magyarország)",
38444         "hu",
38445         "36"
38446       ],
38447       [
38448         "Iceland (Ísland)",
38449         "is",
38450         "354"
38451       ],
38452       [
38453         "India (भारत)",
38454         "in",
38455         "91"
38456       ],
38457       [
38458         "Indonesia",
38459         "id",
38460         "62"
38461       ],
38462       [
38463         "Iran (‫ایران‬‎)",
38464         "ir",
38465         "98"
38466       ],
38467       [
38468         "Iraq (‫العراق‬‎)",
38469         "iq",
38470         "964"
38471       ],
38472       [
38473         "Ireland",
38474         "ie",
38475         "353"
38476       ],
38477       [
38478         "Isle of Man",
38479         "im",
38480         "44",
38481         2
38482       ],
38483       [
38484         "Israel (‫ישראל‬‎)",
38485         "il",
38486         "972"
38487       ],
38488       [
38489         "Italy (Italia)",
38490         "it",
38491         "39",
38492         0
38493       ],
38494       [
38495         "Jamaica",
38496         "jm",
38497         "1876"
38498       ],
38499       [
38500         "Japan (日本)",
38501         "jp",
38502         "81"
38503       ],
38504       [
38505         "Jersey",
38506         "je",
38507         "44",
38508         3
38509       ],
38510       [
38511         "Jordan (‫الأردن‬‎)",
38512         "jo",
38513         "962"
38514       ],
38515       [
38516         "Kazakhstan (Казахстан)",
38517         "kz",
38518         "7",
38519         1
38520       ],
38521       [
38522         "Kenya",
38523         "ke",
38524         "254"
38525       ],
38526       [
38527         "Kiribati",
38528         "ki",
38529         "686"
38530       ],
38531       [
38532         "Kosovo",
38533         "xk",
38534         "383"
38535       ],
38536       [
38537         "Kuwait (‫الكويت‬‎)",
38538         "kw",
38539         "965"
38540       ],
38541       [
38542         "Kyrgyzstan (Кыргызстан)",
38543         "kg",
38544         "996"
38545       ],
38546       [
38547         "Laos (ລາວ)",
38548         "la",
38549         "856"
38550       ],
38551       [
38552         "Latvia (Latvija)",
38553         "lv",
38554         "371"
38555       ],
38556       [
38557         "Lebanon (‫لبنان‬‎)",
38558         "lb",
38559         "961"
38560       ],
38561       [
38562         "Lesotho",
38563         "ls",
38564         "266"
38565       ],
38566       [
38567         "Liberia",
38568         "lr",
38569         "231"
38570       ],
38571       [
38572         "Libya (‫ليبيا‬‎)",
38573         "ly",
38574         "218"
38575       ],
38576       [
38577         "Liechtenstein",
38578         "li",
38579         "423"
38580       ],
38581       [
38582         "Lithuania (Lietuva)",
38583         "lt",
38584         "370"
38585       ],
38586       [
38587         "Luxembourg",
38588         "lu",
38589         "352"
38590       ],
38591       [
38592         "Macau (澳門)",
38593         "mo",
38594         "853"
38595       ],
38596       [
38597         "Macedonia (FYROM) (Македонија)",
38598         "mk",
38599         "389"
38600       ],
38601       [
38602         "Madagascar (Madagasikara)",
38603         "mg",
38604         "261"
38605       ],
38606       [
38607         "Malawi",
38608         "mw",
38609         "265"
38610       ],
38611       [
38612         "Malaysia",
38613         "my",
38614         "60"
38615       ],
38616       [
38617         "Maldives",
38618         "mv",
38619         "960"
38620       ],
38621       [
38622         "Mali",
38623         "ml",
38624         "223"
38625       ],
38626       [
38627         "Malta",
38628         "mt",
38629         "356"
38630       ],
38631       [
38632         "Marshall Islands",
38633         "mh",
38634         "692"
38635       ],
38636       [
38637         "Martinique",
38638         "mq",
38639         "596"
38640       ],
38641       [
38642         "Mauritania (‫موريتانيا‬‎)",
38643         "mr",
38644         "222"
38645       ],
38646       [
38647         "Mauritius (Moris)",
38648         "mu",
38649         "230"
38650       ],
38651       [
38652         "Mayotte",
38653         "yt",
38654         "262",
38655         1
38656       ],
38657       [
38658         "Mexico (México)",
38659         "mx",
38660         "52"
38661       ],
38662       [
38663         "Micronesia",
38664         "fm",
38665         "691"
38666       ],
38667       [
38668         "Moldova (Republica Moldova)",
38669         "md",
38670         "373"
38671       ],
38672       [
38673         "Monaco",
38674         "mc",
38675         "377"
38676       ],
38677       [
38678         "Mongolia (Монгол)",
38679         "mn",
38680         "976"
38681       ],
38682       [
38683         "Montenegro (Crna Gora)",
38684         "me",
38685         "382"
38686       ],
38687       [
38688         "Montserrat",
38689         "ms",
38690         "1664"
38691       ],
38692       [
38693         "Morocco (‫المغرب‬‎)",
38694         "ma",
38695         "212",
38696         0
38697       ],
38698       [
38699         "Mozambique (Moçambique)",
38700         "mz",
38701         "258"
38702       ],
38703       [
38704         "Myanmar (Burma) (မြန်မာ)",
38705         "mm",
38706         "95"
38707       ],
38708       [
38709         "Namibia (Namibië)",
38710         "na",
38711         "264"
38712       ],
38713       [
38714         "Nauru",
38715         "nr",
38716         "674"
38717       ],
38718       [
38719         "Nepal (नेपाल)",
38720         "np",
38721         "977"
38722       ],
38723       [
38724         "Netherlands (Nederland)",
38725         "nl",
38726         "31"
38727       ],
38728       [
38729         "New Caledonia (Nouvelle-Calédonie)",
38730         "nc",
38731         "687"
38732       ],
38733       [
38734         "New Zealand",
38735         "nz",
38736         "64"
38737       ],
38738       [
38739         "Nicaragua",
38740         "ni",
38741         "505"
38742       ],
38743       [
38744         "Niger (Nijar)",
38745         "ne",
38746         "227"
38747       ],
38748       [
38749         "Nigeria",
38750         "ng",
38751         "234"
38752       ],
38753       [
38754         "Niue",
38755         "nu",
38756         "683"
38757       ],
38758       [
38759         "Norfolk Island",
38760         "nf",
38761         "672"
38762       ],
38763       [
38764         "North Korea (조선 민주주의 인민 공화국)",
38765         "kp",
38766         "850"
38767       ],
38768       [
38769         "Northern Mariana Islands",
38770         "mp",
38771         "1670"
38772       ],
38773       [
38774         "Norway (Norge)",
38775         "no",
38776         "47",
38777         0
38778       ],
38779       [
38780         "Oman (‫عُمان‬‎)",
38781         "om",
38782         "968"
38783       ],
38784       [
38785         "Pakistan (‫پاکستان‬‎)",
38786         "pk",
38787         "92"
38788       ],
38789       [
38790         "Palau",
38791         "pw",
38792         "680"
38793       ],
38794       [
38795         "Palestine (‫فلسطين‬‎)",
38796         "ps",
38797         "970"
38798       ],
38799       [
38800         "Panama (Panamá)",
38801         "pa",
38802         "507"
38803       ],
38804       [
38805         "Papua New Guinea",
38806         "pg",
38807         "675"
38808       ],
38809       [
38810         "Paraguay",
38811         "py",
38812         "595"
38813       ],
38814       [
38815         "Peru (Perú)",
38816         "pe",
38817         "51"
38818       ],
38819       [
38820         "Philippines",
38821         "ph",
38822         "63"
38823       ],
38824       [
38825         "Poland (Polska)",
38826         "pl",
38827         "48"
38828       ],
38829       [
38830         "Portugal",
38831         "pt",
38832         "351"
38833       ],
38834       [
38835         "Puerto Rico",
38836         "pr",
38837         "1",
38838         3,
38839         ["787", "939"]
38840       ],
38841       [
38842         "Qatar (‫قطر‬‎)",
38843         "qa",
38844         "974"
38845       ],
38846       [
38847         "Réunion (La Réunion)",
38848         "re",
38849         "262",
38850         0
38851       ],
38852       [
38853         "Romania (România)",
38854         "ro",
38855         "40"
38856       ],
38857       [
38858         "Russia (Россия)",
38859         "ru",
38860         "7",
38861         0
38862       ],
38863       [
38864         "Rwanda",
38865         "rw",
38866         "250"
38867       ],
38868       [
38869         "Saint Barthélemy",
38870         "bl",
38871         "590",
38872         1
38873       ],
38874       [
38875         "Saint Helena",
38876         "sh",
38877         "290"
38878       ],
38879       [
38880         "Saint Kitts and Nevis",
38881         "kn",
38882         "1869"
38883       ],
38884       [
38885         "Saint Lucia",
38886         "lc",
38887         "1758"
38888       ],
38889       [
38890         "Saint Martin (Saint-Martin (partie française))",
38891         "mf",
38892         "590",
38893         2
38894       ],
38895       [
38896         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38897         "pm",
38898         "508"
38899       ],
38900       [
38901         "Saint Vincent and the Grenadines",
38902         "vc",
38903         "1784"
38904       ],
38905       [
38906         "Samoa",
38907         "ws",
38908         "685"
38909       ],
38910       [
38911         "San Marino",
38912         "sm",
38913         "378"
38914       ],
38915       [
38916         "São Tomé and Príncipe (São Tomé e Príncipe)",
38917         "st",
38918         "239"
38919       ],
38920       [
38921         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
38922         "sa",
38923         "966"
38924       ],
38925       [
38926         "Senegal (Sénégal)",
38927         "sn",
38928         "221"
38929       ],
38930       [
38931         "Serbia (Србија)",
38932         "rs",
38933         "381"
38934       ],
38935       [
38936         "Seychelles",
38937         "sc",
38938         "248"
38939       ],
38940       [
38941         "Sierra Leone",
38942         "sl",
38943         "232"
38944       ],
38945       [
38946         "Singapore",
38947         "sg",
38948         "65"
38949       ],
38950       [
38951         "Sint Maarten",
38952         "sx",
38953         "1721"
38954       ],
38955       [
38956         "Slovakia (Slovensko)",
38957         "sk",
38958         "421"
38959       ],
38960       [
38961         "Slovenia (Slovenija)",
38962         "si",
38963         "386"
38964       ],
38965       [
38966         "Solomon Islands",
38967         "sb",
38968         "677"
38969       ],
38970       [
38971         "Somalia (Soomaaliya)",
38972         "so",
38973         "252"
38974       ],
38975       [
38976         "South Africa",
38977         "za",
38978         "27"
38979       ],
38980       [
38981         "South Korea (대한민국)",
38982         "kr",
38983         "82"
38984       ],
38985       [
38986         "South Sudan (‫جنوب السودان‬‎)",
38987         "ss",
38988         "211"
38989       ],
38990       [
38991         "Spain (España)",
38992         "es",
38993         "34"
38994       ],
38995       [
38996         "Sri Lanka (ශ්‍රී ලංකාව)",
38997         "lk",
38998         "94"
38999       ],
39000       [
39001         "Sudan (‫السودان‬‎)",
39002         "sd",
39003         "249"
39004       ],
39005       [
39006         "Suriname",
39007         "sr",
39008         "597"
39009       ],
39010       [
39011         "Svalbard and Jan Mayen",
39012         "sj",
39013         "47",
39014         1
39015       ],
39016       [
39017         "Swaziland",
39018         "sz",
39019         "268"
39020       ],
39021       [
39022         "Sweden (Sverige)",
39023         "se",
39024         "46"
39025       ],
39026       [
39027         "Switzerland (Schweiz)",
39028         "ch",
39029         "41"
39030       ],
39031       [
39032         "Syria (‫سوريا‬‎)",
39033         "sy",
39034         "963"
39035       ],
39036       [
39037         "Taiwan (台灣)",
39038         "tw",
39039         "886"
39040       ],
39041       [
39042         "Tajikistan",
39043         "tj",
39044         "992"
39045       ],
39046       [
39047         "Tanzania",
39048         "tz",
39049         "255"
39050       ],
39051       [
39052         "Thailand (ไทย)",
39053         "th",
39054         "66"
39055       ],
39056       [
39057         "Timor-Leste",
39058         "tl",
39059         "670"
39060       ],
39061       [
39062         "Togo",
39063         "tg",
39064         "228"
39065       ],
39066       [
39067         "Tokelau",
39068         "tk",
39069         "690"
39070       ],
39071       [
39072         "Tonga",
39073         "to",
39074         "676"
39075       ],
39076       [
39077         "Trinidad and Tobago",
39078         "tt",
39079         "1868"
39080       ],
39081       [
39082         "Tunisia (‫تونس‬‎)",
39083         "tn",
39084         "216"
39085       ],
39086       [
39087         "Turkey (Türkiye)",
39088         "tr",
39089         "90"
39090       ],
39091       [
39092         "Turkmenistan",
39093         "tm",
39094         "993"
39095       ],
39096       [
39097         "Turks and Caicos Islands",
39098         "tc",
39099         "1649"
39100       ],
39101       [
39102         "Tuvalu",
39103         "tv",
39104         "688"
39105       ],
39106       [
39107         "U.S. Virgin Islands",
39108         "vi",
39109         "1340"
39110       ],
39111       [
39112         "Uganda",
39113         "ug",
39114         "256"
39115       ],
39116       [
39117         "Ukraine (Україна)",
39118         "ua",
39119         "380"
39120       ],
39121       [
39122         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39123         "ae",
39124         "971"
39125       ],
39126       [
39127         "United Kingdom",
39128         "gb",
39129         "44",
39130         0
39131       ],
39132       [
39133         "United States",
39134         "us",
39135         "1",
39136         0
39137       ],
39138       [
39139         "Uruguay",
39140         "uy",
39141         "598"
39142       ],
39143       [
39144         "Uzbekistan (Oʻzbekiston)",
39145         "uz",
39146         "998"
39147       ],
39148       [
39149         "Vanuatu",
39150         "vu",
39151         "678"
39152       ],
39153       [
39154         "Vatican City (Città del Vaticano)",
39155         "va",
39156         "39",
39157         1
39158       ],
39159       [
39160         "Venezuela",
39161         "ve",
39162         "58"
39163       ],
39164       [
39165         "Vietnam (Việt Nam)",
39166         "vn",
39167         "84"
39168       ],
39169       [
39170         "Wallis and Futuna (Wallis-et-Futuna)",
39171         "wf",
39172         "681"
39173       ],
39174       [
39175         "Western Sahara (‫الصحراء الغربية‬‎)",
39176         "eh",
39177         "212",
39178         1
39179       ],
39180       [
39181         "Yemen (‫اليمن‬‎)",
39182         "ye",
39183         "967"
39184       ],
39185       [
39186         "Zambia",
39187         "zm",
39188         "260"
39189       ],
39190       [
39191         "Zimbabwe",
39192         "zw",
39193         "263"
39194       ],
39195       [
39196         "Åland Islands",
39197         "ax",
39198         "358",
39199         1
39200       ]
39201   ];
39202   
39203   return d;
39204 }/**
39205 *    This script refer to:
39206 *    Title: International Telephone Input
39207 *    Author: Jack O'Connor
39208 *    Code version:  v12.1.12
39209 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39210 **/
39211
39212 /**
39213  * @class Roo.bootstrap.PhoneInput
39214  * @extends Roo.bootstrap.TriggerField
39215  * An input with International dial-code selection
39216  
39217  * @cfg {String} defaultDialCode default '+852'
39218  * @cfg {Array} preferedCountries default []
39219   
39220  * @constructor
39221  * Create a new PhoneInput.
39222  * @param {Object} config Configuration options
39223  */
39224
39225 Roo.bootstrap.PhoneInput = function(config) {
39226     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39227 };
39228
39229 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39230         
39231         listWidth: undefined,
39232         
39233         selectedClass: 'active',
39234         
39235         invalidClass : "has-warning",
39236         
39237         validClass: 'has-success',
39238         
39239         allowed: '0123456789',
39240         
39241         /**
39242          * @cfg {String} defaultDialCode The default dial code when initializing the input
39243          */
39244         defaultDialCode: '+852',
39245         
39246         /**
39247          * @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
39248          */
39249         preferedCountries: false,
39250         
39251         getAutoCreate : function()
39252         {
39253             var data = Roo.bootstrap.PhoneInputData();
39254             var align = this.labelAlign || this.parentLabelAlign();
39255             var id = Roo.id();
39256             
39257             this.allCountries = [];
39258             this.dialCodeMapping = [];
39259             
39260             for (var i = 0; i < data.length; i++) {
39261               var c = data[i];
39262               this.allCountries[i] = {
39263                 name: c[0],
39264                 iso2: c[1],
39265                 dialCode: c[2],
39266                 priority: c[3] || 0,
39267                 areaCodes: c[4] || null
39268               };
39269               this.dialCodeMapping[c[2]] = {
39270                   name: c[0],
39271                   iso2: c[1],
39272                   priority: c[3] || 0,
39273                   areaCodes: c[4] || null
39274               };
39275             }
39276             
39277             var cfg = {
39278                 cls: 'form-group',
39279                 cn: []
39280             };
39281             
39282             var input =  {
39283                 tag: 'input',
39284                 id : id,
39285                 cls : 'form-control tel-input',
39286                 autocomplete: 'new-password'
39287             };
39288             
39289             var hiddenInput = {
39290                 tag: 'input',
39291                 type: 'hidden',
39292                 cls: 'hidden-tel-input'
39293             };
39294             
39295             if (this.name) {
39296                 hiddenInput.name = this.name;
39297             }
39298             
39299             if (this.disabled) {
39300                 input.disabled = true;
39301             }
39302             
39303             var flag_container = {
39304                 tag: 'div',
39305                 cls: 'flag-box',
39306                 cn: [
39307                     {
39308                         tag: 'div',
39309                         cls: 'flag'
39310                     },
39311                     {
39312                         tag: 'div',
39313                         cls: 'caret'
39314                     }
39315                 ]
39316             };
39317             
39318             var box = {
39319                 tag: 'div',
39320                 cls: this.hasFeedback ? 'has-feedback' : '',
39321                 cn: [
39322                     hiddenInput,
39323                     input,
39324                     {
39325                         tag: 'input',
39326                         cls: 'dial-code-holder',
39327                         disabled: true
39328                     }
39329                 ]
39330             };
39331             
39332             var container = {
39333                 cls: 'roo-select2-container input-group',
39334                 cn: [
39335                     flag_container,
39336                     box
39337                 ]
39338             };
39339             
39340             if (this.fieldLabel.length) {
39341                 var indicator = {
39342                     tag: 'i',
39343                     tooltip: 'This field is required'
39344                 };
39345                 
39346                 var label = {
39347                     tag: 'label',
39348                     'for':  id,
39349                     cls: 'control-label',
39350                     cn: []
39351                 };
39352                 
39353                 var label_text = {
39354                     tag: 'span',
39355                     html: this.fieldLabel
39356                 };
39357                 
39358                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39359                 label.cn = [
39360                     indicator,
39361                     label_text
39362                 ];
39363                 
39364                 if(this.indicatorpos == 'right') {
39365                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39366                     label.cn = [
39367                         label_text,
39368                         indicator
39369                     ];
39370                 }
39371                 
39372                 if(align == 'left') {
39373                     container = {
39374                         tag: 'div',
39375                         cn: [
39376                             container
39377                         ]
39378                     };
39379                     
39380                     if(this.labelWidth > 12){
39381                         label.style = "width: " + this.labelWidth + 'px';
39382                     }
39383                     if(this.labelWidth < 13 && this.labelmd == 0){
39384                         this.labelmd = this.labelWidth;
39385                     }
39386                     if(this.labellg > 0){
39387                         label.cls += ' col-lg-' + this.labellg;
39388                         input.cls += ' col-lg-' + (12 - this.labellg);
39389                     }
39390                     if(this.labelmd > 0){
39391                         label.cls += ' col-md-' + this.labelmd;
39392                         container.cls += ' col-md-' + (12 - this.labelmd);
39393                     }
39394                     if(this.labelsm > 0){
39395                         label.cls += ' col-sm-' + this.labelsm;
39396                         container.cls += ' col-sm-' + (12 - this.labelsm);
39397                     }
39398                     if(this.labelxs > 0){
39399                         label.cls += ' col-xs-' + this.labelxs;
39400                         container.cls += ' col-xs-' + (12 - this.labelxs);
39401                     }
39402                 }
39403             }
39404             
39405             cfg.cn = [
39406                 label,
39407                 container
39408             ];
39409             
39410             var settings = this;
39411             
39412             ['xs','sm','md','lg'].map(function(size){
39413                 if (settings[size]) {
39414                     cfg.cls += ' col-' + size + '-' + settings[size];
39415                 }
39416             });
39417             
39418             this.store = new Roo.data.Store({
39419                 proxy : new Roo.data.MemoryProxy({}),
39420                 reader : new Roo.data.JsonReader({
39421                     fields : [
39422                         {
39423                             'name' : 'name',
39424                             'type' : 'string'
39425                         },
39426                         {
39427                             'name' : 'iso2',
39428                             'type' : 'string'
39429                         },
39430                         {
39431                             'name' : 'dialCode',
39432                             'type' : 'string'
39433                         },
39434                         {
39435                             'name' : 'priority',
39436                             'type' : 'string'
39437                         },
39438                         {
39439                             'name' : 'areaCodes',
39440                             'type' : 'string'
39441                         }
39442                     ]
39443                 })
39444             });
39445             
39446             if(!this.preferedCountries) {
39447                 this.preferedCountries = [
39448                     'hk',
39449                     'gb',
39450                     'us'
39451                 ];
39452             }
39453             
39454             var p = this.preferedCountries.reverse();
39455             
39456             if(p) {
39457                 for (var i = 0; i < p.length; i++) {
39458                     for (var j = 0; j < this.allCountries.length; j++) {
39459                         if(this.allCountries[j].iso2 == p[i]) {
39460                             var t = this.allCountries[j];
39461                             this.allCountries.splice(j,1);
39462                             this.allCountries.unshift(t);
39463                         }
39464                     } 
39465                 }
39466             }
39467             
39468             this.store.proxy.data = {
39469                 success: true,
39470                 data: this.allCountries
39471             };
39472             
39473             return cfg;
39474         },
39475         
39476         initEvents : function()
39477         {
39478             this.createList();
39479             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39480             
39481             this.indicator = this.indicatorEl();
39482             this.flag = this.flagEl();
39483             this.dialCodeHolder = this.dialCodeHolderEl();
39484             
39485             this.trigger = this.el.select('div.flag-box',true).first();
39486             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39487             
39488             var _this = this;
39489             
39490             (function(){
39491                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39492                 _this.list.setWidth(lw);
39493             }).defer(100);
39494             
39495             this.list.on('mouseover', this.onViewOver, this);
39496             this.list.on('mousemove', this.onViewMove, this);
39497             this.inputEl().on("keyup", this.onKeyUp, this);
39498             
39499             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39500
39501             this.view = new Roo.View(this.list, this.tpl, {
39502                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39503             });
39504             
39505             this.view.on('click', this.onViewClick, this);
39506             this.setValue(this.defaultDialCode);
39507         },
39508         
39509         onTriggerClick : function(e)
39510         {
39511             Roo.log('trigger click');
39512             if(this.disabled){
39513                 return;
39514             }
39515             
39516             if(this.isExpanded()){
39517                 this.collapse();
39518                 this.hasFocus = false;
39519             }else {
39520                 this.store.load({});
39521                 this.hasFocus = true;
39522                 this.expand();
39523             }
39524         },
39525         
39526         isExpanded : function()
39527         {
39528             return this.list.isVisible();
39529         },
39530         
39531         collapse : function()
39532         {
39533             if(!this.isExpanded()){
39534                 return;
39535             }
39536             this.list.hide();
39537             Roo.get(document).un('mousedown', this.collapseIf, this);
39538             Roo.get(document).un('mousewheel', this.collapseIf, this);
39539             this.fireEvent('collapse', this);
39540             this.validate();
39541         },
39542         
39543         expand : function()
39544         {
39545             Roo.log('expand');
39546
39547             if(this.isExpanded() || !this.hasFocus){
39548                 return;
39549             }
39550             
39551             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39552             this.list.setWidth(lw);
39553             
39554             this.list.show();
39555             this.restrictHeight();
39556             
39557             Roo.get(document).on('mousedown', this.collapseIf, this);
39558             Roo.get(document).on('mousewheel', this.collapseIf, this);
39559             
39560             this.fireEvent('expand', this);
39561         },
39562         
39563         restrictHeight : function()
39564         {
39565             this.list.alignTo(this.inputEl(), this.listAlign);
39566             this.list.alignTo(this.inputEl(), this.listAlign);
39567         },
39568         
39569         onViewOver : function(e, t)
39570         {
39571             if(this.inKeyMode){
39572                 return;
39573             }
39574             var item = this.view.findItemFromChild(t);
39575             
39576             if(item){
39577                 var index = this.view.indexOf(item);
39578                 this.select(index, false);
39579             }
39580         },
39581
39582         // private
39583         onViewClick : function(view, doFocus, el, e)
39584         {
39585             var index = this.view.getSelectedIndexes()[0];
39586             
39587             var r = this.store.getAt(index);
39588             
39589             if(r){
39590                 this.onSelect(r, index);
39591             }
39592             if(doFocus !== false && !this.blockFocus){
39593                 this.inputEl().focus();
39594             }
39595         },
39596         
39597         onViewMove : function(e, t)
39598         {
39599             this.inKeyMode = false;
39600         },
39601         
39602         select : function(index, scrollIntoView)
39603         {
39604             this.selectedIndex = index;
39605             this.view.select(index);
39606             if(scrollIntoView !== false){
39607                 var el = this.view.getNode(index);
39608                 if(el){
39609                     this.list.scrollChildIntoView(el, false);
39610                 }
39611             }
39612         },
39613         
39614         createList : function()
39615         {
39616             this.list = Roo.get(document.body).createChild({
39617                 tag: 'ul',
39618                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39619                 style: 'display:none'
39620             });
39621             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39622         },
39623         
39624         collapseIf : function(e)
39625         {
39626             var in_combo  = e.within(this.el);
39627             var in_list =  e.within(this.list);
39628             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39629             
39630             if (in_combo || in_list || is_list) {
39631                 return;
39632             }
39633             this.collapse();
39634         },
39635         
39636         onSelect : function(record, index)
39637         {
39638             if(this.fireEvent('beforeselect', this, record, index) !== false){
39639                 
39640                 this.setFlagClass(record.data.iso2);
39641                 this.setDialCode(record.data.dialCode);
39642                 this.hasFocus = false;
39643                 this.collapse();
39644                 this.fireEvent('select', this, record, index);
39645             }
39646         },
39647         
39648         flagEl : function()
39649         {
39650             var flag = this.el.select('div.flag',true).first();
39651             if(!flag){
39652                 return false;
39653             }
39654             return flag;
39655         },
39656         
39657         dialCodeHolderEl : function()
39658         {
39659             var d = this.el.select('input.dial-code-holder',true).first();
39660             if(!d){
39661                 return false;
39662             }
39663             return d;
39664         },
39665         
39666         setDialCode : function(v)
39667         {
39668             this.dialCodeHolder.dom.value = '+'+v;
39669         },
39670         
39671         setFlagClass : function(n)
39672         {
39673             this.flag.dom.className = 'flag '+n;
39674         },
39675         
39676         getValue : function()
39677         {
39678             var v = this.inputEl().getValue();
39679             if(this.dialCodeHolder) {
39680                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39681             }
39682             return v;
39683         },
39684         
39685         setValue : function(v)
39686         {
39687             var d = this.getDialCode(v);
39688             
39689             //invalid dial code
39690             if(v.length == 0 || !d || d.length == 0) {
39691                 if(this.rendered){
39692                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39693                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39694                 }
39695                 return;
39696             }
39697             
39698             //valid dial code
39699             this.setFlagClass(this.dialCodeMapping[d].iso2);
39700             this.setDialCode(d);
39701             this.inputEl().dom.value = v.replace('+'+d,'');
39702             this.hiddenEl().dom.value = this.getValue();
39703             
39704             this.validate();
39705         },
39706         
39707         getDialCode : function(v = '')
39708         {
39709             if (v.length == 0) {
39710                 return this.dialCodeHolder.dom.value;
39711             }
39712             
39713             var dialCode = "";
39714             if (v.charAt(0) != "+") {
39715                 return false;
39716             }
39717             var numericChars = "";
39718             for (var i = 1; i < v.length; i++) {
39719               var c = v.charAt(i);
39720               if (!isNaN(c)) {
39721                 numericChars += c;
39722                 if (this.dialCodeMapping[numericChars]) {
39723                   dialCode = v.substr(1, i);
39724                 }
39725                 if (numericChars.length == 4) {
39726                   break;
39727                 }
39728               }
39729             }
39730             return dialCode;
39731         },
39732         
39733         reset : function()
39734         {
39735             this.setValue(this.defaultDialCode);
39736             this.validate();
39737         },
39738         
39739         hiddenEl : function()
39740         {
39741             return this.el.select('input.hidden-tel-input',true).first();
39742         },
39743         
39744         onKeyUp : function(e){
39745             
39746             var k = e.getKey();
39747             var c = e.getCharCode();
39748             
39749             if(
39750                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39751                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39752             ){
39753                 e.stopEvent();
39754             }
39755             
39756             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39757             //     return;
39758             // }
39759             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39760                 e.stopEvent();
39761             }
39762             
39763             this.setValue(this.getValue());
39764         }
39765         
39766 });
39767 /**
39768  * @class Roo.bootstrap.MoneyField
39769  * @extends Roo.bootstrap.ComboBox
39770  * Bootstrap MoneyField class
39771  * 
39772  * @constructor
39773  * Create a new MoneyField.
39774  * @param {Object} config Configuration options
39775  */
39776
39777 Roo.bootstrap.MoneyField = function(config) {
39778     
39779     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39780     
39781 };
39782
39783 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39784     
39785     /**
39786      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39787      */
39788     decimalSeparator : ".",
39789     
39790     inputlg : 9,
39791     inputmd : 9,
39792     inputsm : 9,
39793     inputxs : 6,
39794     
39795     store : false,
39796     
39797     getAutoCreate : function()
39798     {
39799         var align = this.labelAlign || this.parentLabelAlign();
39800         
39801         var id = Roo.id();
39802
39803         var cfg = {
39804             cls: 'form-group',
39805             cn: []
39806         };
39807
39808         var input =  {
39809             tag: 'input',
39810             id : id,
39811             cls : 'form-control roo-money-amount-input',
39812             autocomplete: 'new-password'
39813         };
39814         
39815         if (this.name) {
39816             input.name = this.name;
39817         }
39818
39819         if (this.disabled) {
39820             input.disabled = true;
39821         }
39822
39823         var clg = 12 - this.inputlg;
39824         var cmd = 12 - this.inputmd;
39825         var csm = 12 - this.inputsm;
39826         var cxs = 12 - this.inputxs;
39827         
39828         var container = {
39829             tag : 'div',
39830             cls : 'row roo-money-field',
39831             cn : [
39832                 {
39833                     tag : 'div',
39834                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
39835                     cn : [
39836                         {
39837                             tag : 'div',
39838                             cls: 'roo-select2-container input-group',
39839                             cn: [
39840                                 {
39841                                     tag : 'input',
39842                                     cls : 'form-control roo-money-currency-input',
39843                                     autocomplete: 'new-password'
39844                                 },
39845                                 {
39846                                     tag :'span',
39847                                     cls : 'input-group-addon',
39848                                     cn : [
39849                                         {
39850                                             tag: 'span',
39851                                             cls: 'caret'
39852                                         }
39853                                     ]
39854                                 }
39855                             ]
39856                         }
39857                     ]
39858                 },
39859                 {
39860                     tag : 'div',
39861                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
39862                     cn : [
39863                         {
39864                             tag: 'div',
39865                             cls: this.hasFeedback ? 'has-feedback' : '',
39866                             cn: [
39867                                 input
39868                             ]
39869                         }
39870                     ]
39871                 }
39872             ]
39873             
39874         };
39875         
39876         if (this.fieldLabel.length) {
39877             var indicator = {
39878                 tag: 'i',
39879                 tooltip: 'This field is required'
39880             };
39881
39882             var label = {
39883                 tag: 'label',
39884                 'for':  id,
39885                 cls: 'control-label',
39886                 cn: []
39887             };
39888
39889             var label_text = {
39890                 tag: 'span',
39891                 html: this.fieldLabel
39892             };
39893
39894             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39895             label.cn = [
39896                 indicator,
39897                 label_text
39898             ];
39899
39900             if(this.indicatorpos == 'right') {
39901                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39902                 label.cn = [
39903                     label_text,
39904                     indicator
39905                 ];
39906             }
39907
39908             if(align == 'left') {
39909                 container = {
39910                     tag: 'div',
39911                     cn: [
39912                         container
39913                     ]
39914                 };
39915
39916                 if(this.labelWidth > 12){
39917                     label.style = "width: " + this.labelWidth + 'px';
39918                 }
39919                 if(this.labelWidth < 13 && this.labelmd == 0){
39920                     this.labelmd = this.labelWidth;
39921                 }
39922                 if(this.labellg > 0){
39923                     label.cls += ' col-lg-' + this.labellg;
39924                     input.cls += ' col-lg-' + (12 - this.labellg);
39925                 }
39926                 if(this.labelmd > 0){
39927                     label.cls += ' col-md-' + this.labelmd;
39928                     container.cls += ' col-md-' + (12 - this.labelmd);
39929                 }
39930                 if(this.labelsm > 0){
39931                     label.cls += ' col-sm-' + this.labelsm;
39932                     container.cls += ' col-sm-' + (12 - this.labelsm);
39933                 }
39934                 if(this.labelxs > 0){
39935                     label.cls += ' col-xs-' + this.labelxs;
39936                     container.cls += ' col-xs-' + (12 - this.labelxs);
39937                 }
39938             }
39939         }
39940
39941         cfg.cn = [
39942             label,
39943             container
39944         ];
39945
39946         var settings = this;
39947
39948         ['xs','sm','md','lg'].map(function(size){
39949             if (settings[size]) {
39950                 cfg.cls += ' col-' + size + '-' + settings[size];
39951             }
39952         });
39953         
39954         return cfg;
39955         
39956     },
39957     
39958     initEvents : function()
39959     {
39960         this.initCurrencyEvent();
39961         
39962         this.initNumberEvent();
39963         
39964     },
39965     
39966     initCurrencyEvent : function()
39967     {
39968         if (!this.store) {
39969             throw "can not find store for combo";
39970         }
39971         
39972         this.store = Roo.factory(this.store, Roo.data);
39973         this.store.parent = this;
39974         
39975         this.createList();
39976         
39977         this.indicator = this.indicatorEl();
39978         
39979         this.triggerEl = this.el.select('.input-group-addon', true).first();
39980         
39981         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
39982         
39983         this.currencyEl = this.el.select('.roo-money-currency-input', true).first();
39984         
39985         this.amountEl = this.el.select('.roo-money-amount-input', true).first();
39986         
39987         var _this = this;
39988         
39989         (function(){
39990             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39991             _this.list.setWidth(lw);
39992         }).defer(100);
39993         
39994         this.list.on('mouseover', this.onViewOver, this);
39995         this.list.on('mousemove', this.onViewMove, this);
39996         this.list.on('scroll', this.onViewScroll, this);
39997         
39998         if(!this.tpl){
39999             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40000         }
40001         
40002         this.view = new Roo.View(this.list, this.tpl, {
40003             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40004         });
40005         
40006         this.view.on('click', this.onViewClick, this);
40007         
40008         this.store.on('beforeload', this.onBeforeLoad, this);
40009         this.store.on('load', this.onLoad, this);
40010         this.store.on('loadexception', this.onLoadException, this);
40011         
40012         this.keyNav = new Roo.KeyNav(this.currencyEl, {
40013             "up" : function(e){
40014                 this.inKeyMode = true;
40015                 this.selectPrev();
40016             },
40017
40018             "down" : function(e){
40019                 if(!this.isExpanded()){
40020                     this.onTriggerClick();
40021                 }else{
40022                     this.inKeyMode = true;
40023                     this.selectNext();
40024                 }
40025             },
40026
40027             "enter" : function(e){
40028                 this.collapse();
40029                 
40030                 if(this.fireEvent("specialkey", this, e)){
40031                     this.onViewClick(false);
40032                 }
40033                 
40034                 return true;
40035             },
40036
40037             "esc" : function(e){
40038                 this.collapse();
40039             },
40040
40041             "tab" : function(e){
40042                 this.collapse();
40043                 
40044                 if(this.fireEvent("specialkey", this, e)){
40045                     this.onViewClick(false);
40046                 }
40047                 
40048                 return true;
40049             },
40050
40051             scope : this,
40052
40053             doRelay : function(foo, bar, hname){
40054                 if(hname == 'down' || this.scope.isExpanded()){
40055                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40056                 }
40057                 return true;
40058             },
40059
40060             forceKeyDown: true
40061         });
40062         
40063     },
40064     
40065     initNumberEvent : function(e)
40066     {
40067         var allowed = "0123456789";
40068         
40069         if(this.allowDecimals){
40070             allowed += this.decimalSeparator;
40071         }
40072         
40073         if(this.allowNegative){
40074             allowed += "-";
40075         }
40076         
40077         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40078         
40079         var keyPress = function(e){
40080             
40081             var k = e.getKey();
40082             
40083             var c = e.getCharCode();
40084             
40085             if(
40086                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40087                     allowed.indexOf(String.fromCharCode(c)) === -1
40088             ){
40089                 e.stopEvent();
40090                 return;
40091             }
40092             
40093             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40094                 return;
40095             }
40096             
40097             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40098                 e.stopEvent();
40099             }
40100         };
40101         
40102         this.amountEl.on("keypress", keyPress, this);
40103         
40104     },
40105     
40106     onTriggerClick : function(e)
40107     {   
40108         if(this.disabled){
40109             return;
40110         }
40111         
40112         this.page = 0;
40113         this.loadNext = false;
40114         
40115         if(this.isExpanded()){
40116             this.collapse();
40117             return;
40118         }
40119         
40120         this.hasFocus = true;
40121         
40122         if(this.triggerAction == 'all') {
40123             this.doQuery(this.allQuery, true);
40124             return;
40125         }
40126         
40127         this.doQuery(this.getRawValue());
40128     },
40129     
40130     restrictHeight : function()
40131     {
40132         this.list.alignTo(this.currencyEl, this.listAlign);
40133         this.list.alignTo(this.currencyEl, this.listAlign);
40134     },
40135     
40136     onViewClick : function(view, doFocus, el, e)
40137     {
40138         var index = this.view.getSelectedIndexes()[0];
40139         
40140         var r = this.store.getAt(index);
40141         
40142         if(r){
40143             this.onSelect(r, index);
40144         }
40145     },
40146     
40147     setFromData : function(o)
40148     {
40149         var currency = '';
40150         
40151         this.lastData = o;
40152         
40153         if (this.currencyField) {
40154             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40155         } else {
40156             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
40157         }
40158         
40159         this.lastSelectionText = currency;
40160         this.currencyValue = currency;
40161         
40162         this.setCurrency(currency);
40163         
40164         
40165     },
40166     
40167     setCurrency : function(v)
40168     {   
40169         this.currencyValue = v;
40170         
40171         if(this.rendered){
40172             this.currencyEl.dom.value = (v === null || v === undefined ? '' : v);
40173             this.validate();
40174         }
40175     },
40176     
40177     validate : function()
40178     {
40179         return;
40180         
40181         var v = this.getRawValue();
40182         
40183         if(this.multiple){
40184             v = this.getValue();
40185         }
40186         
40187         if(this.disabled || this.allowBlank || v.length){
40188             this.markValid();
40189             return true;
40190         }
40191         
40192         this.markInvalid();
40193         return false;
40194     },
40195     
40196     parseValue : function(value)
40197     {
40198         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40199         return isNaN(value) ? '' : value;
40200     },
40201     
40202     fixPrecision : function(value)
40203     {
40204         var nan = isNaN(value);
40205         
40206         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40207             return nan ? '' : value;
40208         }
40209         
40210         return parseFloat(value).toFixed(this.decimalPrecision);
40211     },
40212     
40213     decimalPrecisionFcn : function(v)
40214     {
40215         return Math.floor(v);
40216     },
40217     
40218     setValue : function(v)
40219     {
40220         v = this.fixPrecision(v);
40221         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40222     }
40223 });