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 if(field.setFromData && (field.store && !field.store.isLocal)) {
7915                         
7916                         field.setFromData(values);
7917                         
7918                     } else {
7919                         field.setValue(values[id]);
7920                     }
7921
7922
7923                     if(this.trackResetOnLoad){
7924                         field.originalValue = field.getValue();
7925                     }
7926                 }
7927             }
7928         }
7929
7930         //Roo.each(this.childForms || [], function (f) {
7931         //    f.setValues(values);
7932         //});
7933
7934         return this;
7935     },
7936
7937     /**
7938      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7939      * they are returned as an array.
7940      * @param {Boolean} asString
7941      * @return {Object}
7942      */
7943     getValues : function(asString){
7944         //if (this.childForms) {
7945             // copy values from the child forms
7946         //    Roo.each(this.childForms, function (f) {
7947         //        this.setValues(f.getValues());
7948         //    }, this);
7949         //}
7950
7951
7952
7953         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7954         if(asString === true){
7955             return fs;
7956         }
7957         return Roo.urlDecode(fs);
7958     },
7959
7960     /**
7961      * Returns the fields in this form as an object with key/value pairs.
7962      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7963      * @return {Object}
7964      */
7965     getFieldValues : function(with_hidden)
7966     {
7967         var items = this.getItems();
7968         var ret = {};
7969         items.each(function(f){
7970             if (!f.getName()) {
7971                 return;
7972             }
7973             var v = f.getValue();
7974             if (f.inputType =='radio') {
7975                 if (typeof(ret[f.getName()]) == 'undefined') {
7976                     ret[f.getName()] = ''; // empty..
7977                 }
7978
7979                 if (!f.el.dom.checked) {
7980                     return;
7981
7982                 }
7983                 v = f.el.dom.value;
7984
7985             }
7986
7987             // not sure if this supported any more..
7988             if ((typeof(v) == 'object') && f.getRawValue) {
7989                 v = f.getRawValue() ; // dates..
7990             }
7991             // combo boxes where name != hiddenName...
7992             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7993                 ret[f.name] = f.getRawValue();
7994             }
7995             ret[f.getName()] = v;
7996         });
7997
7998         return ret;
7999     },
8000
8001     /**
8002      * Clears all invalid messages in this form.
8003      * @return {BasicForm} this
8004      */
8005     clearInvalid : function(){
8006         var items = this.getItems();
8007
8008         items.each(function(f){
8009            f.clearInvalid();
8010         });
8011
8012
8013
8014         return this;
8015     },
8016
8017     /**
8018      * Resets this form.
8019      * @return {BasicForm} this
8020      */
8021     reset : function(){
8022         var items = this.getItems();
8023         items.each(function(f){
8024             f.reset();
8025         });
8026
8027         Roo.each(this.childForms || [], function (f) {
8028             f.reset();
8029         });
8030
8031
8032         return this;
8033     },
8034     getItems : function()
8035     {
8036         var r=new Roo.util.MixedCollection(false, function(o){
8037             return o.id || (o.id = Roo.id());
8038         });
8039         var iter = function(el) {
8040             if (el.inputEl) {
8041                 r.add(el);
8042             }
8043             if (!el.items) {
8044                 return;
8045             }
8046             Roo.each(el.items,function(e) {
8047                 iter(e);
8048             });
8049
8050
8051         };
8052
8053         iter(this);
8054         return r;
8055
8056
8057
8058
8059     }
8060
8061 });
8062
8063 Roo.apply(Roo.bootstrap.Form, {
8064     
8065     popover : {
8066         
8067         padding : 5,
8068         
8069         isApplied : false,
8070         
8071         isMasked : false,
8072         
8073         form : false,
8074         
8075         target : false,
8076         
8077         toolTip : false,
8078         
8079         intervalID : false,
8080         
8081         maskEl : false,
8082         
8083         apply : function()
8084         {
8085             if(this.isApplied){
8086                 return;
8087             }
8088             
8089             this.maskEl = {
8090                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8091                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8092                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8093                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8094             };
8095             
8096             this.maskEl.top.enableDisplayMode("block");
8097             this.maskEl.left.enableDisplayMode("block");
8098             this.maskEl.bottom.enableDisplayMode("block");
8099             this.maskEl.right.enableDisplayMode("block");
8100             
8101             this.toolTip = new Roo.bootstrap.Tooltip({
8102                 cls : 'roo-form-error-popover',
8103                 alignment : {
8104                     'left' : ['r-l', [-2,0], 'right'],
8105                     'right' : ['l-r', [2,0], 'left'],
8106                     'bottom' : ['tl-bl', [0,2], 'top'],
8107                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8108                 }
8109             });
8110             
8111             this.toolTip.render(Roo.get(document.body));
8112
8113             this.toolTip.el.enableDisplayMode("block");
8114             
8115             Roo.get(document.body).on('click', function(){
8116                 this.unmask();
8117             }, this);
8118             
8119             Roo.get(document.body).on('touchstart', function(){
8120                 this.unmask();
8121             }, this);
8122             
8123             this.isApplied = true
8124         },
8125         
8126         mask : function(form, target)
8127         {
8128             this.form = form;
8129             
8130             this.target = target;
8131             
8132             if(!this.form.errorMask || !target.el){
8133                 return;
8134             }
8135             
8136             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8137             
8138             Roo.log(scrollable);
8139             
8140             var ot = this.target.el.calcOffsetsTo(scrollable);
8141             
8142             var scrollTo = ot[1] - this.form.maskOffset;
8143             
8144             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8145             
8146             scrollable.scrollTo('top', scrollTo);
8147             
8148             var box = this.target.el.getBox();
8149             Roo.log(box);
8150             var zIndex = Roo.bootstrap.Modal.zIndex++;
8151
8152             
8153             this.maskEl.top.setStyle('position', 'absolute');
8154             this.maskEl.top.setStyle('z-index', zIndex);
8155             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8156             this.maskEl.top.setLeft(0);
8157             this.maskEl.top.setTop(0);
8158             this.maskEl.top.show();
8159             
8160             this.maskEl.left.setStyle('position', 'absolute');
8161             this.maskEl.left.setStyle('z-index', zIndex);
8162             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8163             this.maskEl.left.setLeft(0);
8164             this.maskEl.left.setTop(box.y - this.padding);
8165             this.maskEl.left.show();
8166
8167             this.maskEl.bottom.setStyle('position', 'absolute');
8168             this.maskEl.bottom.setStyle('z-index', zIndex);
8169             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8170             this.maskEl.bottom.setLeft(0);
8171             this.maskEl.bottom.setTop(box.bottom + this.padding);
8172             this.maskEl.bottom.show();
8173
8174             this.maskEl.right.setStyle('position', 'absolute');
8175             this.maskEl.right.setStyle('z-index', zIndex);
8176             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8177             this.maskEl.right.setLeft(box.right + this.padding);
8178             this.maskEl.right.setTop(box.y - this.padding);
8179             this.maskEl.right.show();
8180
8181             this.toolTip.bindEl = this.target.el;
8182
8183             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8184
8185             var tip = this.target.blankText;
8186
8187             if(this.target.getValue() !== '' ) {
8188                 
8189                 if (this.target.invalidText.length) {
8190                     tip = this.target.invalidText;
8191                 } else if (this.target.regexText.length){
8192                     tip = this.target.regexText;
8193                 }
8194             }
8195
8196             this.toolTip.show(tip);
8197
8198             this.intervalID = window.setInterval(function() {
8199                 Roo.bootstrap.Form.popover.unmask();
8200             }, 10000);
8201
8202             window.onwheel = function(){ return false;};
8203             
8204             (function(){ this.isMasked = true; }).defer(500, this);
8205             
8206         },
8207         
8208         unmask : function()
8209         {
8210             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8211                 return;
8212             }
8213             
8214             this.maskEl.top.setStyle('position', 'absolute');
8215             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8216             this.maskEl.top.hide();
8217
8218             this.maskEl.left.setStyle('position', 'absolute');
8219             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8220             this.maskEl.left.hide();
8221
8222             this.maskEl.bottom.setStyle('position', 'absolute');
8223             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8224             this.maskEl.bottom.hide();
8225
8226             this.maskEl.right.setStyle('position', 'absolute');
8227             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8228             this.maskEl.right.hide();
8229             
8230             this.toolTip.hide();
8231             
8232             this.toolTip.el.hide();
8233             
8234             window.onwheel = function(){ return true;};
8235             
8236             if(this.intervalID){
8237                 window.clearInterval(this.intervalID);
8238                 this.intervalID = false;
8239             }
8240             
8241             this.isMasked = false;
8242             
8243         }
8244         
8245     }
8246     
8247 });
8248
8249 /*
8250  * Based on:
8251  * Ext JS Library 1.1.1
8252  * Copyright(c) 2006-2007, Ext JS, LLC.
8253  *
8254  * Originally Released Under LGPL - original licence link has changed is not relivant.
8255  *
8256  * Fork - LGPL
8257  * <script type="text/javascript">
8258  */
8259 /**
8260  * @class Roo.form.VTypes
8261  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8262  * @singleton
8263  */
8264 Roo.form.VTypes = function(){
8265     // closure these in so they are only created once.
8266     var alpha = /^[a-zA-Z_]+$/;
8267     var alphanum = /^[a-zA-Z0-9_]+$/;
8268     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8269     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8270
8271     // All these messages and functions are configurable
8272     return {
8273         /**
8274          * The function used to validate email addresses
8275          * @param {String} value The email address
8276          */
8277         'email' : function(v){
8278             return email.test(v);
8279         },
8280         /**
8281          * The error text to display when the email validation function returns false
8282          * @type String
8283          */
8284         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8285         /**
8286          * The keystroke filter mask to be applied on email input
8287          * @type RegExp
8288          */
8289         'emailMask' : /[a-z0-9_\.\-@]/i,
8290
8291         /**
8292          * The function used to validate URLs
8293          * @param {String} value The URL
8294          */
8295         'url' : function(v){
8296             return url.test(v);
8297         },
8298         /**
8299          * The error text to display when the url validation function returns false
8300          * @type String
8301          */
8302         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8303         
8304         /**
8305          * The function used to validate alpha values
8306          * @param {String} value The value
8307          */
8308         'alpha' : function(v){
8309             return alpha.test(v);
8310         },
8311         /**
8312          * The error text to display when the alpha validation function returns false
8313          * @type String
8314          */
8315         'alphaText' : 'This field should only contain letters and _',
8316         /**
8317          * The keystroke filter mask to be applied on alpha input
8318          * @type RegExp
8319          */
8320         'alphaMask' : /[a-z_]/i,
8321
8322         /**
8323          * The function used to validate alphanumeric values
8324          * @param {String} value The value
8325          */
8326         'alphanum' : function(v){
8327             return alphanum.test(v);
8328         },
8329         /**
8330          * The error text to display when the alphanumeric validation function returns false
8331          * @type String
8332          */
8333         'alphanumText' : 'This field should only contain letters, numbers and _',
8334         /**
8335          * The keystroke filter mask to be applied on alphanumeric input
8336          * @type RegExp
8337          */
8338         'alphanumMask' : /[a-z0-9_]/i
8339     };
8340 }();/*
8341  * - LGPL
8342  *
8343  * Input
8344  * 
8345  */
8346
8347 /**
8348  * @class Roo.bootstrap.Input
8349  * @extends Roo.bootstrap.Component
8350  * Bootstrap Input class
8351  * @cfg {Boolean} disabled is it disabled
8352  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8353  * @cfg {String} name name of the input
8354  * @cfg {string} fieldLabel - the label associated
8355  * @cfg {string} placeholder - placeholder to put in text.
8356  * @cfg {string}  before - input group add on before
8357  * @cfg {string} after - input group add on after
8358  * @cfg {string} size - (lg|sm) or leave empty..
8359  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8360  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8361  * @cfg {Number} md colspan out of 12 for computer-sized screens
8362  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8363  * @cfg {string} value default value of the input
8364  * @cfg {Number} labelWidth set the width of label 
8365  * @cfg {Number} labellg set the width of label (1-12)
8366  * @cfg {Number} labelmd set the width of label (1-12)
8367  * @cfg {Number} labelsm set the width of label (1-12)
8368  * @cfg {Number} labelxs set the width of label (1-12)
8369  * @cfg {String} labelAlign (top|left)
8370  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8371  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8372  * @cfg {String} indicatorpos (left|right) default left
8373
8374  * @cfg {String} align (left|center|right) Default left
8375  * @cfg {Boolean} forceFeedback (true|false) Default false
8376  * 
8377  * 
8378  * 
8379  * 
8380  * @constructor
8381  * Create a new Input
8382  * @param {Object} config The config object
8383  */
8384
8385 Roo.bootstrap.Input = function(config){
8386     
8387     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8388     
8389     this.addEvents({
8390         /**
8391          * @event focus
8392          * Fires when this field receives input focus.
8393          * @param {Roo.form.Field} this
8394          */
8395         focus : true,
8396         /**
8397          * @event blur
8398          * Fires when this field loses input focus.
8399          * @param {Roo.form.Field} this
8400          */
8401         blur : true,
8402         /**
8403          * @event specialkey
8404          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8405          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8406          * @param {Roo.form.Field} this
8407          * @param {Roo.EventObject} e The event object
8408          */
8409         specialkey : true,
8410         /**
8411          * @event change
8412          * Fires just before the field blurs if the field value has changed.
8413          * @param {Roo.form.Field} this
8414          * @param {Mixed} newValue The new value
8415          * @param {Mixed} oldValue The original value
8416          */
8417         change : true,
8418         /**
8419          * @event invalid
8420          * Fires after the field has been marked as invalid.
8421          * @param {Roo.form.Field} this
8422          * @param {String} msg The validation message
8423          */
8424         invalid : true,
8425         /**
8426          * @event valid
8427          * Fires after the field has been validated with no errors.
8428          * @param {Roo.form.Field} this
8429          */
8430         valid : true,
8431          /**
8432          * @event keyup
8433          * Fires after the key up
8434          * @param {Roo.form.Field} this
8435          * @param {Roo.EventObject}  e The event Object
8436          */
8437         keyup : true
8438     });
8439 };
8440
8441 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8442      /**
8443      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8444       automatic validation (defaults to "keyup").
8445      */
8446     validationEvent : "keyup",
8447      /**
8448      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8449      */
8450     validateOnBlur : true,
8451     /**
8452      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8453      */
8454     validationDelay : 250,
8455      /**
8456      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8457      */
8458     focusClass : "x-form-focus",  // not needed???
8459     
8460        
8461     /**
8462      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8463      */
8464     invalidClass : "has-warning",
8465     
8466     /**
8467      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8468      */
8469     validClass : "has-success",
8470     
8471     /**
8472      * @cfg {Boolean} hasFeedback (true|false) default true
8473      */
8474     hasFeedback : true,
8475     
8476     /**
8477      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8478      */
8479     invalidFeedbackClass : "glyphicon-warning-sign",
8480     
8481     /**
8482      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8483      */
8484     validFeedbackClass : "glyphicon-ok",
8485     
8486     /**
8487      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8488      */
8489     selectOnFocus : false,
8490     
8491      /**
8492      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8493      */
8494     maskRe : null,
8495        /**
8496      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8497      */
8498     vtype : null,
8499     
8500       /**
8501      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8502      */
8503     disableKeyFilter : false,
8504     
8505        /**
8506      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8507      */
8508     disabled : false,
8509      /**
8510      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8511      */
8512     allowBlank : true,
8513     /**
8514      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8515      */
8516     blankText : "Please complete this mandatory field",
8517     
8518      /**
8519      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8520      */
8521     minLength : 0,
8522     /**
8523      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8524      */
8525     maxLength : Number.MAX_VALUE,
8526     /**
8527      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8528      */
8529     minLengthText : "The minimum length for this field is {0}",
8530     /**
8531      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8532      */
8533     maxLengthText : "The maximum length for this field is {0}",
8534   
8535     
8536     /**
8537      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8538      * If available, this function will be called only after the basic validators all return true, and will be passed the
8539      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8540      */
8541     validator : null,
8542     /**
8543      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8544      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8545      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8546      */
8547     regex : null,
8548     /**
8549      * @cfg {String} regexText -- Depricated - use Invalid Text
8550      */
8551     regexText : "",
8552     
8553     /**
8554      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8555      */
8556     invalidText : "",
8557     
8558     
8559     
8560     autocomplete: false,
8561     
8562     
8563     fieldLabel : '',
8564     inputType : 'text',
8565     
8566     name : false,
8567     placeholder: false,
8568     before : false,
8569     after : false,
8570     size : false,
8571     hasFocus : false,
8572     preventMark: false,
8573     isFormField : true,
8574     value : '',
8575     labelWidth : 2,
8576     labelAlign : false,
8577     readOnly : false,
8578     align : false,
8579     formatedValue : false,
8580     forceFeedback : false,
8581     
8582     indicatorpos : 'left',
8583     
8584     labellg : 0,
8585     labelmd : 0,
8586     labelsm : 0,
8587     labelxs : 0,
8588     
8589     parentLabelAlign : function()
8590     {
8591         var parent = this;
8592         while (parent.parent()) {
8593             parent = parent.parent();
8594             if (typeof(parent.labelAlign) !='undefined') {
8595                 return parent.labelAlign;
8596             }
8597         }
8598         return 'left';
8599         
8600     },
8601     
8602     getAutoCreate : function()
8603     {
8604         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8605         
8606         var id = Roo.id();
8607         
8608         var cfg = {};
8609         
8610         if(this.inputType != 'hidden'){
8611             cfg.cls = 'form-group' //input-group
8612         }
8613         
8614         var input =  {
8615             tag: 'input',
8616             id : id,
8617             type : this.inputType,
8618             value : this.value,
8619             cls : 'form-control',
8620             placeholder : this.placeholder || '',
8621             autocomplete : this.autocomplete || 'new-password'
8622         };
8623         
8624         if(this.align){
8625             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8626         }
8627         
8628         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8629             input.maxLength = this.maxLength;
8630         }
8631         
8632         if (this.disabled) {
8633             input.disabled=true;
8634         }
8635         
8636         if (this.readOnly) {
8637             input.readonly=true;
8638         }
8639         
8640         if (this.name) {
8641             input.name = this.name;
8642         }
8643         
8644         if (this.size) {
8645             input.cls += ' input-' + this.size;
8646         }
8647         
8648         var settings=this;
8649         ['xs','sm','md','lg'].map(function(size){
8650             if (settings[size]) {
8651                 cfg.cls += ' col-' + size + '-' + settings[size];
8652             }
8653         });
8654         
8655         var inputblock = input;
8656         
8657         var feedback = {
8658             tag: 'span',
8659             cls: 'glyphicon form-control-feedback'
8660         };
8661             
8662         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8663             
8664             inputblock = {
8665                 cls : 'has-feedback',
8666                 cn :  [
8667                     input,
8668                     feedback
8669                 ] 
8670             };  
8671         }
8672         
8673         if (this.before || this.after) {
8674             
8675             inputblock = {
8676                 cls : 'input-group',
8677                 cn :  [] 
8678             };
8679             
8680             if (this.before && typeof(this.before) == 'string') {
8681                 
8682                 inputblock.cn.push({
8683                     tag :'span',
8684                     cls : 'roo-input-before input-group-addon',
8685                     html : this.before
8686                 });
8687             }
8688             if (this.before && typeof(this.before) == 'object') {
8689                 this.before = Roo.factory(this.before);
8690                 
8691                 inputblock.cn.push({
8692                     tag :'span',
8693                     cls : 'roo-input-before input-group-' +
8694                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8695                 });
8696             }
8697             
8698             inputblock.cn.push(input);
8699             
8700             if (this.after && typeof(this.after) == 'string') {
8701                 inputblock.cn.push({
8702                     tag :'span',
8703                     cls : 'roo-input-after input-group-addon',
8704                     html : this.after
8705                 });
8706             }
8707             if (this.after && typeof(this.after) == 'object') {
8708                 this.after = Roo.factory(this.after);
8709                 
8710                 inputblock.cn.push({
8711                     tag :'span',
8712                     cls : 'roo-input-after input-group-' +
8713                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8714                 });
8715             }
8716             
8717             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8718                 inputblock.cls += ' has-feedback';
8719                 inputblock.cn.push(feedback);
8720             }
8721         };
8722         
8723         if (align ==='left' && this.fieldLabel.length) {
8724             
8725             cfg.cls += ' roo-form-group-label-left';
8726             
8727             cfg.cn = [
8728                 {
8729                     tag : 'i',
8730                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8731                     tooltip : 'This field is required'
8732                 },
8733                 {
8734                     tag: 'label',
8735                     'for' :  id,
8736                     cls : 'control-label',
8737                     html : this.fieldLabel
8738
8739                 },
8740                 {
8741                     cls : "", 
8742                     cn: [
8743                         inputblock
8744                     ]
8745                 }
8746             ];
8747             
8748             var labelCfg = cfg.cn[1];
8749             var contentCfg = cfg.cn[2];
8750             
8751             if(this.indicatorpos == 'right'){
8752                 cfg.cn = [
8753                     {
8754                         tag: 'label',
8755                         'for' :  id,
8756                         cls : 'control-label',
8757                         cn : [
8758                             {
8759                                 tag : 'span',
8760                                 html : this.fieldLabel
8761                             },
8762                             {
8763                                 tag : 'i',
8764                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8765                                 tooltip : 'This field is required'
8766                             }
8767                         ]
8768                     },
8769                     {
8770                         cls : "",
8771                         cn: [
8772                             inputblock
8773                         ]
8774                     }
8775
8776                 ];
8777                 
8778                 labelCfg = cfg.cn[0];
8779                 contentCfg = cfg.cn[1];
8780             
8781             }
8782             
8783             if(this.labelWidth > 12){
8784                 labelCfg.style = "width: " + this.labelWidth + 'px';
8785             }
8786             
8787             if(this.labelWidth < 13 && this.labelmd == 0){
8788                 this.labelmd = this.labelWidth;
8789             }
8790             
8791             if(this.labellg > 0){
8792                 labelCfg.cls += ' col-lg-' + this.labellg;
8793                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8794             }
8795             
8796             if(this.labelmd > 0){
8797                 labelCfg.cls += ' col-md-' + this.labelmd;
8798                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8799             }
8800             
8801             if(this.labelsm > 0){
8802                 labelCfg.cls += ' col-sm-' + this.labelsm;
8803                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8804             }
8805             
8806             if(this.labelxs > 0){
8807                 labelCfg.cls += ' col-xs-' + this.labelxs;
8808                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8809             }
8810             
8811             
8812         } else if ( this.fieldLabel.length) {
8813                 
8814             cfg.cn = [
8815                 {
8816                     tag : 'i',
8817                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8818                     tooltip : 'This field is required'
8819                 },
8820                 {
8821                     tag: 'label',
8822                    //cls : 'input-group-addon',
8823                     html : this.fieldLabel
8824
8825                 },
8826
8827                inputblock
8828
8829            ];
8830            
8831            if(this.indicatorpos == 'right'){
8832                 
8833                 cfg.cn = [
8834                     {
8835                         tag: 'label',
8836                        //cls : 'input-group-addon',
8837                         html : this.fieldLabel
8838
8839                     },
8840                     {
8841                         tag : 'i',
8842                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8843                         tooltip : 'This field is required'
8844                     },
8845
8846                    inputblock
8847
8848                ];
8849
8850             }
8851
8852         } else {
8853             
8854             cfg.cn = [
8855
8856                     inputblock
8857
8858             ];
8859                 
8860                 
8861         };
8862         
8863         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8864            cfg.cls += ' navbar-form';
8865         }
8866         
8867         if (this.parentType === 'NavGroup') {
8868            cfg.cls += ' navbar-form';
8869            cfg.tag = 'li';
8870         }
8871         
8872         return cfg;
8873         
8874     },
8875     /**
8876      * return the real input element.
8877      */
8878     inputEl: function ()
8879     {
8880         return this.el.select('input.form-control',true).first();
8881     },
8882     
8883     tooltipEl : function()
8884     {
8885         return this.inputEl();
8886     },
8887     
8888     indicatorEl : function()
8889     {
8890         var indicator = this.el.select('i.roo-required-indicator',true).first();
8891         
8892         if(!indicator){
8893             return false;
8894         }
8895         
8896         return indicator;
8897         
8898     },
8899     
8900     setDisabled : function(v)
8901     {
8902         var i  = this.inputEl().dom;
8903         if (!v) {
8904             i.removeAttribute('disabled');
8905             return;
8906             
8907         }
8908         i.setAttribute('disabled','true');
8909     },
8910     initEvents : function()
8911     {
8912           
8913         this.inputEl().on("keydown" , this.fireKey,  this);
8914         this.inputEl().on("focus", this.onFocus,  this);
8915         this.inputEl().on("blur", this.onBlur,  this);
8916         
8917         this.inputEl().relayEvent('keyup', this);
8918         
8919         this.indicator = this.indicatorEl();
8920         
8921         if(this.indicator){
8922             this.indicator.addClass('invisible');
8923             
8924         }
8925  
8926         // reference to original value for reset
8927         this.originalValue = this.getValue();
8928         //Roo.form.TextField.superclass.initEvents.call(this);
8929         if(this.validationEvent == 'keyup'){
8930             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8931             this.inputEl().on('keyup', this.filterValidation, this);
8932         }
8933         else if(this.validationEvent !== false){
8934             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8935         }
8936         
8937         if(this.selectOnFocus){
8938             this.on("focus", this.preFocus, this);
8939             
8940         }
8941         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8942             this.inputEl().on("keypress", this.filterKeys, this);
8943         } else {
8944             this.inputEl().relayEvent('keypress', this);
8945         }
8946        /* if(this.grow){
8947             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8948             this.el.on("click", this.autoSize,  this);
8949         }
8950         */
8951         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8952             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8953         }
8954         
8955         if (typeof(this.before) == 'object') {
8956             this.before.render(this.el.select('.roo-input-before',true).first());
8957         }
8958         if (typeof(this.after) == 'object') {
8959             this.after.render(this.el.select('.roo-input-after',true).first());
8960         }
8961         
8962         
8963     },
8964     filterValidation : function(e){
8965         if(!e.isNavKeyPress()){
8966             this.validationTask.delay(this.validationDelay);
8967         }
8968     },
8969      /**
8970      * Validates the field value
8971      * @return {Boolean} True if the value is valid, else false
8972      */
8973     validate : function(){
8974         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8975         if(this.disabled || this.validateValue(this.getRawValue())){
8976             this.markValid();
8977             return true;
8978         }
8979         
8980         this.markInvalid();
8981         return false;
8982     },
8983     
8984     
8985     /**
8986      * Validates a value according to the field's validation rules and marks the field as invalid
8987      * if the validation fails
8988      * @param {Mixed} value The value to validate
8989      * @return {Boolean} True if the value is valid, else false
8990      */
8991     validateValue : function(value){
8992         if(value.length < 1)  { // if it's blank
8993             if(this.allowBlank){
8994                 return true;
8995             }            
8996             return this.inputEl().hasClass('hide') ? true : false;
8997         }
8998         
8999         if(value.length < this.minLength){
9000             return false;
9001         }
9002         if(value.length > this.maxLength){
9003             return false;
9004         }
9005         if(this.vtype){
9006             var vt = Roo.form.VTypes;
9007             if(!vt[this.vtype](value, this)){
9008                 return false;
9009             }
9010         }
9011         if(typeof this.validator == "function"){
9012             var msg = this.validator(value);
9013             if(msg !== true){
9014                 return false;
9015             }
9016             if (typeof(msg) == 'string') {
9017                 this.invalidText = msg;
9018             }
9019         }
9020         
9021         if(this.regex && !this.regex.test(value)){
9022             return false;
9023         }
9024         
9025         return true;
9026     },
9027
9028     
9029     
9030      // private
9031     fireKey : function(e){
9032         //Roo.log('field ' + e.getKey());
9033         if(e.isNavKeyPress()){
9034             this.fireEvent("specialkey", this, e);
9035         }
9036     },
9037     focus : function (selectText){
9038         if(this.rendered){
9039             this.inputEl().focus();
9040             if(selectText === true){
9041                 this.inputEl().dom.select();
9042             }
9043         }
9044         return this;
9045     } ,
9046     
9047     onFocus : function(){
9048         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9049            // this.el.addClass(this.focusClass);
9050         }
9051         if(!this.hasFocus){
9052             this.hasFocus = true;
9053             this.startValue = this.getValue();
9054             this.fireEvent("focus", this);
9055         }
9056     },
9057     
9058     beforeBlur : Roo.emptyFn,
9059
9060     
9061     // private
9062     onBlur : function(){
9063         this.beforeBlur();
9064         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9065             //this.el.removeClass(this.focusClass);
9066         }
9067         this.hasFocus = false;
9068         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9069             this.validate();
9070         }
9071         var v = this.getValue();
9072         if(String(v) !== String(this.startValue)){
9073             this.fireEvent('change', this, v, this.startValue);
9074         }
9075         this.fireEvent("blur", this);
9076     },
9077     
9078     /**
9079      * Resets the current field value to the originally loaded value and clears any validation messages
9080      */
9081     reset : function(){
9082         this.setValue(this.originalValue);
9083         this.validate();
9084     },
9085      /**
9086      * Returns the name of the field
9087      * @return {Mixed} name The name field
9088      */
9089     getName: function(){
9090         return this.name;
9091     },
9092      /**
9093      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9094      * @return {Mixed} value The field value
9095      */
9096     getValue : function(){
9097         
9098         var v = this.inputEl().getValue();
9099         
9100         return v;
9101     },
9102     /**
9103      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9104      * @return {Mixed} value The field value
9105      */
9106     getRawValue : function(){
9107         var v = this.inputEl().getValue();
9108         
9109         return v;
9110     },
9111     
9112     /**
9113      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9114      * @param {Mixed} value The value to set
9115      */
9116     setRawValue : function(v){
9117         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9118     },
9119     
9120     selectText : function(start, end){
9121         var v = this.getRawValue();
9122         if(v.length > 0){
9123             start = start === undefined ? 0 : start;
9124             end = end === undefined ? v.length : end;
9125             var d = this.inputEl().dom;
9126             if(d.setSelectionRange){
9127                 d.setSelectionRange(start, end);
9128             }else if(d.createTextRange){
9129                 var range = d.createTextRange();
9130                 range.moveStart("character", start);
9131                 range.moveEnd("character", v.length-end);
9132                 range.select();
9133             }
9134         }
9135     },
9136     
9137     /**
9138      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9139      * @param {Mixed} value The value to set
9140      */
9141     setValue : function(v){
9142         this.value = v;
9143         if(this.rendered){
9144             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9145             this.validate();
9146         }
9147     },
9148     
9149     /*
9150     processValue : function(value){
9151         if(this.stripCharsRe){
9152             var newValue = value.replace(this.stripCharsRe, '');
9153             if(newValue !== value){
9154                 this.setRawValue(newValue);
9155                 return newValue;
9156             }
9157         }
9158         return value;
9159     },
9160   */
9161     preFocus : function(){
9162         
9163         if(this.selectOnFocus){
9164             this.inputEl().dom.select();
9165         }
9166     },
9167     filterKeys : function(e){
9168         var k = e.getKey();
9169         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9170             return;
9171         }
9172         var c = e.getCharCode(), cc = String.fromCharCode(c);
9173         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9174             return;
9175         }
9176         if(!this.maskRe.test(cc)){
9177             e.stopEvent();
9178         }
9179     },
9180      /**
9181      * Clear any invalid styles/messages for this field
9182      */
9183     clearInvalid : function(){
9184         
9185         if(!this.el || this.preventMark){ // not rendered
9186             return;
9187         }
9188         
9189      
9190         this.el.removeClass(this.invalidClass);
9191         
9192         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9193             
9194             var feedback = this.el.select('.form-control-feedback', true).first();
9195             
9196             if(feedback){
9197                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9198             }
9199             
9200         }
9201         
9202         this.fireEvent('valid', this);
9203     },
9204     
9205      /**
9206      * Mark this field as valid
9207      */
9208     markValid : function()
9209     {
9210         if(!this.el  || this.preventMark){ // not rendered...
9211             return;
9212         }
9213         
9214         this.el.removeClass([this.invalidClass, this.validClass]);
9215         
9216         var feedback = this.el.select('.form-control-feedback', true).first();
9217             
9218         if(feedback){
9219             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9220         }
9221
9222         if(this.disabled){
9223             return;
9224         }
9225         
9226         if(this.allowBlank && !this.getRawValue().length){
9227             return;
9228         }
9229         
9230         if(this.indicator){
9231             this.indicator.removeClass('visible');
9232             this.indicator.addClass('invisible');
9233         }
9234         
9235         this.el.addClass(this.validClass);
9236         
9237         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9238             
9239             var feedback = this.el.select('.form-control-feedback', true).first();
9240             
9241             if(feedback){
9242                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9243                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9244             }
9245             
9246         }
9247         
9248         this.fireEvent('valid', this);
9249     },
9250     
9251      /**
9252      * Mark this field as invalid
9253      * @param {String} msg The validation message
9254      */
9255     markInvalid : function(msg)
9256     {
9257         if(!this.el  || this.preventMark){ // not rendered
9258             return;
9259         }
9260         
9261         this.el.removeClass([this.invalidClass, this.validClass]);
9262         
9263         var feedback = this.el.select('.form-control-feedback', true).first();
9264             
9265         if(feedback){
9266             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9267         }
9268
9269         if(this.disabled){
9270             return;
9271         }
9272         
9273         if(this.allowBlank && !this.getRawValue().length){
9274             return;
9275         }
9276         
9277         if(this.indicator){
9278             this.indicator.removeClass('invisible');
9279             this.indicator.addClass('visible');
9280         }
9281         
9282         this.el.addClass(this.invalidClass);
9283         
9284         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9285             
9286             var feedback = this.el.select('.form-control-feedback', true).first();
9287             
9288             if(feedback){
9289                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9290                 
9291                 if(this.getValue().length || this.forceFeedback){
9292                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9293                 }
9294                 
9295             }
9296             
9297         }
9298         
9299         this.fireEvent('invalid', this, msg);
9300     },
9301     // private
9302     SafariOnKeyDown : function(event)
9303     {
9304         // this is a workaround for a password hang bug on chrome/ webkit.
9305         if (this.inputEl().dom.type != 'password') {
9306             return;
9307         }
9308         
9309         var isSelectAll = false;
9310         
9311         if(this.inputEl().dom.selectionEnd > 0){
9312             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9313         }
9314         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9315             event.preventDefault();
9316             this.setValue('');
9317             return;
9318         }
9319         
9320         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9321             
9322             event.preventDefault();
9323             // this is very hacky as keydown always get's upper case.
9324             //
9325             var cc = String.fromCharCode(event.getCharCode());
9326             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9327             
9328         }
9329     },
9330     adjustWidth : function(tag, w){
9331         tag = tag.toLowerCase();
9332         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9333             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9334                 if(tag == 'input'){
9335                     return w + 2;
9336                 }
9337                 if(tag == 'textarea'){
9338                     return w-2;
9339                 }
9340             }else if(Roo.isOpera){
9341                 if(tag == 'input'){
9342                     return w + 2;
9343                 }
9344                 if(tag == 'textarea'){
9345                     return w-2;
9346                 }
9347             }
9348         }
9349         return w;
9350     },
9351     
9352     setFieldLabel : function(v)
9353     {
9354         this.fieldLabel = v;
9355         
9356         if(this.rendered){
9357             this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9358         }
9359     }
9360 });
9361
9362  
9363 /*
9364  * - LGPL
9365  *
9366  * Input
9367  * 
9368  */
9369
9370 /**
9371  * @class Roo.bootstrap.TextArea
9372  * @extends Roo.bootstrap.Input
9373  * Bootstrap TextArea class
9374  * @cfg {Number} cols Specifies the visible width of a text area
9375  * @cfg {Number} rows Specifies the visible number of lines in a text area
9376  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9377  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9378  * @cfg {string} html text
9379  * 
9380  * @constructor
9381  * Create a new TextArea
9382  * @param {Object} config The config object
9383  */
9384
9385 Roo.bootstrap.TextArea = function(config){
9386     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9387    
9388 };
9389
9390 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9391      
9392     cols : false,
9393     rows : 5,
9394     readOnly : false,
9395     warp : 'soft',
9396     resize : false,
9397     value: false,
9398     html: false,
9399     
9400     getAutoCreate : function(){
9401         
9402         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9403         
9404         var id = Roo.id();
9405         
9406         var cfg = {};
9407         
9408         if(this.inputType != 'hidden'){
9409             cfg.cls = 'form-group' //input-group
9410         }
9411         
9412         var input =  {
9413             tag: 'textarea',
9414             id : id,
9415             warp : this.warp,
9416             rows : this.rows,
9417             value : this.value || '',
9418             html: this.html || '',
9419             cls : 'form-control',
9420             placeholder : this.placeholder || '' 
9421             
9422         };
9423         
9424         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9425             input.maxLength = this.maxLength;
9426         }
9427         
9428         if(this.resize){
9429             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9430         }
9431         
9432         if(this.cols){
9433             input.cols = this.cols;
9434         }
9435         
9436         if (this.readOnly) {
9437             input.readonly = true;
9438         }
9439         
9440         if (this.name) {
9441             input.name = this.name;
9442         }
9443         
9444         if (this.size) {
9445             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9446         }
9447         
9448         var settings=this;
9449         ['xs','sm','md','lg'].map(function(size){
9450             if (settings[size]) {
9451                 cfg.cls += ' col-' + size + '-' + settings[size];
9452             }
9453         });
9454         
9455         var inputblock = input;
9456         
9457         if(this.hasFeedback && !this.allowBlank){
9458             
9459             var feedback = {
9460                 tag: 'span',
9461                 cls: 'glyphicon form-control-feedback'
9462             };
9463
9464             inputblock = {
9465                 cls : 'has-feedback',
9466                 cn :  [
9467                     input,
9468                     feedback
9469                 ] 
9470             };  
9471         }
9472         
9473         
9474         if (this.before || this.after) {
9475             
9476             inputblock = {
9477                 cls : 'input-group',
9478                 cn :  [] 
9479             };
9480             if (this.before) {
9481                 inputblock.cn.push({
9482                     tag :'span',
9483                     cls : 'input-group-addon',
9484                     html : this.before
9485                 });
9486             }
9487             
9488             inputblock.cn.push(input);
9489             
9490             if(this.hasFeedback && !this.allowBlank){
9491                 inputblock.cls += ' has-feedback';
9492                 inputblock.cn.push(feedback);
9493             }
9494             
9495             if (this.after) {
9496                 inputblock.cn.push({
9497                     tag :'span',
9498                     cls : 'input-group-addon',
9499                     html : this.after
9500                 });
9501             }
9502             
9503         }
9504         
9505         if (align ==='left' && this.fieldLabel.length) {
9506             cfg.cn = [
9507                 {
9508                     tag: 'label',
9509                     'for' :  id,
9510                     cls : 'control-label',
9511                     html : this.fieldLabel
9512                 },
9513                 {
9514                     cls : "",
9515                     cn: [
9516                         inputblock
9517                     ]
9518                 }
9519
9520             ];
9521             
9522             if(this.labelWidth > 12){
9523                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9524             }
9525
9526             if(this.labelWidth < 13 && this.labelmd == 0){
9527                 this.labelmd = this.labelWidth;
9528             }
9529
9530             if(this.labellg > 0){
9531                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9532                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9533             }
9534
9535             if(this.labelmd > 0){
9536                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9537                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9538             }
9539
9540             if(this.labelsm > 0){
9541                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9542                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9543             }
9544
9545             if(this.labelxs > 0){
9546                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9547                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9548             }
9549             
9550         } else if ( this.fieldLabel.length) {
9551             cfg.cn = [
9552
9553                {
9554                    tag: 'label',
9555                    //cls : 'input-group-addon',
9556                    html : this.fieldLabel
9557
9558                },
9559
9560                inputblock
9561
9562            ];
9563
9564         } else {
9565
9566             cfg.cn = [
9567
9568                 inputblock
9569
9570             ];
9571                 
9572         }
9573         
9574         if (this.disabled) {
9575             input.disabled=true;
9576         }
9577         
9578         return cfg;
9579         
9580     },
9581     /**
9582      * return the real textarea element.
9583      */
9584     inputEl: function ()
9585     {
9586         return this.el.select('textarea.form-control',true).first();
9587     },
9588     
9589     /**
9590      * Clear any invalid styles/messages for this field
9591      */
9592     clearInvalid : function()
9593     {
9594         
9595         if(!this.el || this.preventMark){ // not rendered
9596             return;
9597         }
9598         
9599         var label = this.el.select('label', true).first();
9600         var icon = this.el.select('i.fa-star', true).first();
9601         
9602         if(label && icon){
9603             icon.remove();
9604         }
9605         
9606         this.el.removeClass(this.invalidClass);
9607         
9608         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9609             
9610             var feedback = this.el.select('.form-control-feedback', true).first();
9611             
9612             if(feedback){
9613                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9614             }
9615             
9616         }
9617         
9618         this.fireEvent('valid', this);
9619     },
9620     
9621      /**
9622      * Mark this field as valid
9623      */
9624     markValid : function()
9625     {
9626         if(!this.el  || this.preventMark){ // not rendered
9627             return;
9628         }
9629         
9630         this.el.removeClass([this.invalidClass, this.validClass]);
9631         
9632         var feedback = this.el.select('.form-control-feedback', true).first();
9633             
9634         if(feedback){
9635             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9636         }
9637
9638         if(this.disabled || this.allowBlank){
9639             return;
9640         }
9641         
9642         var label = this.el.select('label', true).first();
9643         var icon = this.el.select('i.fa-star', true).first();
9644         
9645         if(label && icon){
9646             icon.remove();
9647         }
9648         
9649         this.el.addClass(this.validClass);
9650         
9651         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9652             
9653             var feedback = this.el.select('.form-control-feedback', true).first();
9654             
9655             if(feedback){
9656                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9657                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9658             }
9659             
9660         }
9661         
9662         this.fireEvent('valid', this);
9663     },
9664     
9665      /**
9666      * Mark this field as invalid
9667      * @param {String} msg The validation message
9668      */
9669     markInvalid : function(msg)
9670     {
9671         if(!this.el  || this.preventMark){ // not rendered
9672             return;
9673         }
9674         
9675         this.el.removeClass([this.invalidClass, this.validClass]);
9676         
9677         var feedback = this.el.select('.form-control-feedback', true).first();
9678             
9679         if(feedback){
9680             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9681         }
9682
9683         if(this.disabled || this.allowBlank){
9684             return;
9685         }
9686         
9687         var label = this.el.select('label', true).first();
9688         var icon = this.el.select('i.fa-star', true).first();
9689         
9690         if(!this.getValue().length && label && !icon){
9691             this.el.createChild({
9692                 tag : 'i',
9693                 cls : 'text-danger fa fa-lg fa-star',
9694                 tooltip : 'This field is required',
9695                 style : 'margin-right:5px;'
9696             }, label, true);
9697         }
9698
9699         this.el.addClass(this.invalidClass);
9700         
9701         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9702             
9703             var feedback = this.el.select('.form-control-feedback', true).first();
9704             
9705             if(feedback){
9706                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9707                 
9708                 if(this.getValue().length || this.forceFeedback){
9709                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9710                 }
9711                 
9712             }
9713             
9714         }
9715         
9716         this.fireEvent('invalid', this, msg);
9717     }
9718 });
9719
9720  
9721 /*
9722  * - LGPL
9723  *
9724  * trigger field - base class for combo..
9725  * 
9726  */
9727  
9728 /**
9729  * @class Roo.bootstrap.TriggerField
9730  * @extends Roo.bootstrap.Input
9731  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9732  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9733  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9734  * for which you can provide a custom implementation.  For example:
9735  * <pre><code>
9736 var trigger = new Roo.bootstrap.TriggerField();
9737 trigger.onTriggerClick = myTriggerFn;
9738 trigger.applyTo('my-field');
9739 </code></pre>
9740  *
9741  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9742  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9743  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9744  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9745  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9746
9747  * @constructor
9748  * Create a new TriggerField.
9749  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9750  * to the base TextField)
9751  */
9752 Roo.bootstrap.TriggerField = function(config){
9753     this.mimicing = false;
9754     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9755 };
9756
9757 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9758     /**
9759      * @cfg {String} triggerClass A CSS class to apply to the trigger
9760      */
9761      /**
9762      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9763      */
9764     hideTrigger:false,
9765
9766     /**
9767      * @cfg {Boolean} removable (true|false) special filter default false
9768      */
9769     removable : false,
9770     
9771     /** @cfg {Boolean} grow @hide */
9772     /** @cfg {Number} growMin @hide */
9773     /** @cfg {Number} growMax @hide */
9774
9775     /**
9776      * @hide 
9777      * @method
9778      */
9779     autoSize: Roo.emptyFn,
9780     // private
9781     monitorTab : true,
9782     // private
9783     deferHeight : true,
9784
9785     
9786     actionMode : 'wrap',
9787     
9788     caret : false,
9789     
9790     
9791     getAutoCreate : function(){
9792        
9793         var align = this.labelAlign || this.parentLabelAlign();
9794         
9795         var id = Roo.id();
9796         
9797         var cfg = {
9798             cls: 'form-group' //input-group
9799         };
9800         
9801         
9802         var input =  {
9803             tag: 'input',
9804             id : id,
9805             type : this.inputType,
9806             cls : 'form-control',
9807             autocomplete: 'new-password',
9808             placeholder : this.placeholder || '' 
9809             
9810         };
9811         if (this.name) {
9812             input.name = this.name;
9813         }
9814         if (this.size) {
9815             input.cls += ' input-' + this.size;
9816         }
9817         
9818         if (this.disabled) {
9819             input.disabled=true;
9820         }
9821         
9822         var inputblock = input;
9823         
9824         if(this.hasFeedback && !this.allowBlank){
9825             
9826             var feedback = {
9827                 tag: 'span',
9828                 cls: 'glyphicon form-control-feedback'
9829             };
9830             
9831             if(this.removable && !this.editable && !this.tickable){
9832                 inputblock = {
9833                     cls : 'has-feedback',
9834                     cn :  [
9835                         inputblock,
9836                         {
9837                             tag: 'button',
9838                             html : 'x',
9839                             cls : 'roo-combo-removable-btn close'
9840                         },
9841                         feedback
9842                     ] 
9843                 };
9844             } else {
9845                 inputblock = {
9846                     cls : 'has-feedback',
9847                     cn :  [
9848                         inputblock,
9849                         feedback
9850                     ] 
9851                 };
9852             }
9853
9854         } else {
9855             if(this.removable && !this.editable && !this.tickable){
9856                 inputblock = {
9857                     cls : 'roo-removable',
9858                     cn :  [
9859                         inputblock,
9860                         {
9861                             tag: 'button',
9862                             html : 'x',
9863                             cls : 'roo-combo-removable-btn close'
9864                         }
9865                     ] 
9866                 };
9867             }
9868         }
9869         
9870         if (this.before || this.after) {
9871             
9872             inputblock = {
9873                 cls : 'input-group',
9874                 cn :  [] 
9875             };
9876             if (this.before) {
9877                 inputblock.cn.push({
9878                     tag :'span',
9879                     cls : 'input-group-addon',
9880                     html : this.before
9881                 });
9882             }
9883             
9884             inputblock.cn.push(input);
9885             
9886             if(this.hasFeedback && !this.allowBlank){
9887                 inputblock.cls += ' has-feedback';
9888                 inputblock.cn.push(feedback);
9889             }
9890             
9891             if (this.after) {
9892                 inputblock.cn.push({
9893                     tag :'span',
9894                     cls : 'input-group-addon',
9895                     html : this.after
9896                 });
9897             }
9898             
9899         };
9900         
9901         var box = {
9902             tag: 'div',
9903             cn: [
9904                 {
9905                     tag: 'input',
9906                     type : 'hidden',
9907                     cls: 'form-hidden-field'
9908                 },
9909                 inputblock
9910             ]
9911             
9912         };
9913         
9914         if(this.multiple){
9915             box = {
9916                 tag: 'div',
9917                 cn: [
9918                     {
9919                         tag: 'input',
9920                         type : 'hidden',
9921                         cls: 'form-hidden-field'
9922                     },
9923                     {
9924                         tag: 'ul',
9925                         cls: 'roo-select2-choices',
9926                         cn:[
9927                             {
9928                                 tag: 'li',
9929                                 cls: 'roo-select2-search-field',
9930                                 cn: [
9931
9932                                     inputblock
9933                                 ]
9934                             }
9935                         ]
9936                     }
9937                 ]
9938             }
9939         };
9940         
9941         var combobox = {
9942             cls: 'roo-select2-container input-group',
9943             cn: [
9944                 box
9945 //                {
9946 //                    tag: 'ul',
9947 //                    cls: 'typeahead typeahead-long dropdown-menu',
9948 //                    style: 'display:none'
9949 //                }
9950             ]
9951         };
9952         
9953         if(!this.multiple && this.showToggleBtn){
9954             
9955             var caret = {
9956                         tag: 'span',
9957                         cls: 'caret'
9958              };
9959             if (this.caret != false) {
9960                 caret = {
9961                      tag: 'i',
9962                      cls: 'fa fa-' + this.caret
9963                 };
9964                 
9965             }
9966             
9967             combobox.cn.push({
9968                 tag :'span',
9969                 cls : 'input-group-addon btn dropdown-toggle',
9970                 cn : [
9971                     caret,
9972                     {
9973                         tag: 'span',
9974                         cls: 'combobox-clear',
9975                         cn  : [
9976                             {
9977                                 tag : 'i',
9978                                 cls: 'icon-remove'
9979                             }
9980                         ]
9981                     }
9982                 ]
9983
9984             })
9985         }
9986         
9987         if(this.multiple){
9988             combobox.cls += ' roo-select2-container-multi';
9989         }
9990         
9991         if (align ==='left' && this.fieldLabel.length) {
9992             
9993             cfg.cls += ' roo-form-group-label-left';
9994
9995             cfg.cn = [
9996                 {
9997                     tag : 'i',
9998                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9999                     tooltip : 'This field is required'
10000                 },
10001                 {
10002                     tag: 'label',
10003                     'for' :  id,
10004                     cls : 'control-label',
10005                     html : this.fieldLabel
10006
10007                 },
10008                 {
10009                     cls : "", 
10010                     cn: [
10011                         combobox
10012                     ]
10013                 }
10014
10015             ];
10016             
10017             var labelCfg = cfg.cn[1];
10018             var contentCfg = cfg.cn[2];
10019             
10020             if(this.indicatorpos == 'right'){
10021                 cfg.cn = [
10022                     {
10023                         tag: 'label',
10024                         'for' :  id,
10025                         cls : 'control-label',
10026                         cn : [
10027                             {
10028                                 tag : 'span',
10029                                 html : this.fieldLabel
10030                             },
10031                             {
10032                                 tag : 'i',
10033                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10034                                 tooltip : 'This field is required'
10035                             }
10036                         ]
10037                     },
10038                     {
10039                         cls : "", 
10040                         cn: [
10041                             combobox
10042                         ]
10043                     }
10044
10045                 ];
10046                 
10047                 labelCfg = cfg.cn[0];
10048                 contentCfg = cfg.cn[1];
10049             }
10050             
10051             if(this.labelWidth > 12){
10052                 labelCfg.style = "width: " + this.labelWidth + 'px';
10053             }
10054             
10055             if(this.labelWidth < 13 && this.labelmd == 0){
10056                 this.labelmd = this.labelWidth;
10057             }
10058             
10059             if(this.labellg > 0){
10060                 labelCfg.cls += ' col-lg-' + this.labellg;
10061                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10062             }
10063             
10064             if(this.labelmd > 0){
10065                 labelCfg.cls += ' col-md-' + this.labelmd;
10066                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10067             }
10068             
10069             if(this.labelsm > 0){
10070                 labelCfg.cls += ' col-sm-' + this.labelsm;
10071                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10072             }
10073             
10074             if(this.labelxs > 0){
10075                 labelCfg.cls += ' col-xs-' + this.labelxs;
10076                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10077             }
10078             
10079         } else if ( this.fieldLabel.length) {
10080 //                Roo.log(" label");
10081             cfg.cn = [
10082                 {
10083                    tag : 'i',
10084                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10085                    tooltip : 'This field is required'
10086                },
10087                {
10088                    tag: 'label',
10089                    //cls : 'input-group-addon',
10090                    html : this.fieldLabel
10091
10092                },
10093
10094                combobox
10095
10096             ];
10097             
10098             if(this.indicatorpos == 'right'){
10099                 
10100                 cfg.cn = [
10101                     {
10102                        tag: 'label',
10103                        cn : [
10104                            {
10105                                tag : 'span',
10106                                html : this.fieldLabel
10107                            },
10108                            {
10109                               tag : 'i',
10110                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10111                               tooltip : 'This field is required'
10112                            }
10113                        ]
10114
10115                     },
10116                     combobox
10117
10118                 ];
10119
10120             }
10121
10122         } else {
10123             
10124 //                Roo.log(" no label && no align");
10125                 cfg = combobox
10126                      
10127                 
10128         }
10129         
10130         var settings=this;
10131         ['xs','sm','md','lg'].map(function(size){
10132             if (settings[size]) {
10133                 cfg.cls += ' col-' + size + '-' + settings[size];
10134             }
10135         });
10136         
10137         return cfg;
10138         
10139     },
10140     
10141     
10142     
10143     // private
10144     onResize : function(w, h){
10145 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10146 //        if(typeof w == 'number'){
10147 //            var x = w - this.trigger.getWidth();
10148 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10149 //            this.trigger.setStyle('left', x+'px');
10150 //        }
10151     },
10152
10153     // private
10154     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10155
10156     // private
10157     getResizeEl : function(){
10158         return this.inputEl();
10159     },
10160
10161     // private
10162     getPositionEl : function(){
10163         return this.inputEl();
10164     },
10165
10166     // private
10167     alignErrorIcon : function(){
10168         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10169     },
10170
10171     // private
10172     initEvents : function(){
10173         
10174         this.createList();
10175         
10176         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10177         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10178         if(!this.multiple && this.showToggleBtn){
10179             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10180             if(this.hideTrigger){
10181                 this.trigger.setDisplayed(false);
10182             }
10183             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10184         }
10185         
10186         if(this.multiple){
10187             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10188         }
10189         
10190         if(this.removable && !this.editable && !this.tickable){
10191             var close = this.closeTriggerEl();
10192             
10193             if(close){
10194                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10195                 close.on('click', this.removeBtnClick, this, close);
10196             }
10197         }
10198         
10199         //this.trigger.addClassOnOver('x-form-trigger-over');
10200         //this.trigger.addClassOnClick('x-form-trigger-click');
10201         
10202         //if(!this.width){
10203         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10204         //}
10205     },
10206     
10207     closeTriggerEl : function()
10208     {
10209         var close = this.el.select('.roo-combo-removable-btn', true).first();
10210         return close ? close : false;
10211     },
10212     
10213     removeBtnClick : function(e, h, el)
10214     {
10215         e.preventDefault();
10216         
10217         if(this.fireEvent("remove", this) !== false){
10218             this.reset();
10219             this.fireEvent("afterremove", this)
10220         }
10221     },
10222     
10223     createList : function()
10224     {
10225         this.list = Roo.get(document.body).createChild({
10226             tag: 'ul',
10227             cls: 'typeahead typeahead-long dropdown-menu',
10228             style: 'display:none'
10229         });
10230         
10231         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10232         
10233     },
10234
10235     // private
10236     initTrigger : function(){
10237        
10238     },
10239
10240     // private
10241     onDestroy : function(){
10242         if(this.trigger){
10243             this.trigger.removeAllListeners();
10244           //  this.trigger.remove();
10245         }
10246         //if(this.wrap){
10247         //    this.wrap.remove();
10248         //}
10249         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10250     },
10251
10252     // private
10253     onFocus : function(){
10254         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10255         /*
10256         if(!this.mimicing){
10257             this.wrap.addClass('x-trigger-wrap-focus');
10258             this.mimicing = true;
10259             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10260             if(this.monitorTab){
10261                 this.el.on("keydown", this.checkTab, this);
10262             }
10263         }
10264         */
10265     },
10266
10267     // private
10268     checkTab : function(e){
10269         if(e.getKey() == e.TAB){
10270             this.triggerBlur();
10271         }
10272     },
10273
10274     // private
10275     onBlur : function(){
10276         // do nothing
10277     },
10278
10279     // private
10280     mimicBlur : function(e, t){
10281         /*
10282         if(!this.wrap.contains(t) && this.validateBlur()){
10283             this.triggerBlur();
10284         }
10285         */
10286     },
10287
10288     // private
10289     triggerBlur : function(){
10290         this.mimicing = false;
10291         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10292         if(this.monitorTab){
10293             this.el.un("keydown", this.checkTab, this);
10294         }
10295         //this.wrap.removeClass('x-trigger-wrap-focus');
10296         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10297     },
10298
10299     // private
10300     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10301     validateBlur : function(e, t){
10302         return true;
10303     },
10304
10305     // private
10306     onDisable : function(){
10307         this.inputEl().dom.disabled = true;
10308         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10309         //if(this.wrap){
10310         //    this.wrap.addClass('x-item-disabled');
10311         //}
10312     },
10313
10314     // private
10315     onEnable : function(){
10316         this.inputEl().dom.disabled = false;
10317         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10318         //if(this.wrap){
10319         //    this.el.removeClass('x-item-disabled');
10320         //}
10321     },
10322
10323     // private
10324     onShow : function(){
10325         var ae = this.getActionEl();
10326         
10327         if(ae){
10328             ae.dom.style.display = '';
10329             ae.dom.style.visibility = 'visible';
10330         }
10331     },
10332
10333     // private
10334     
10335     onHide : function(){
10336         var ae = this.getActionEl();
10337         ae.dom.style.display = 'none';
10338     },
10339
10340     /**
10341      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10342      * by an implementing function.
10343      * @method
10344      * @param {EventObject} e
10345      */
10346     onTriggerClick : Roo.emptyFn
10347 });
10348  /*
10349  * Based on:
10350  * Ext JS Library 1.1.1
10351  * Copyright(c) 2006-2007, Ext JS, LLC.
10352  *
10353  * Originally Released Under LGPL - original licence link has changed is not relivant.
10354  *
10355  * Fork - LGPL
10356  * <script type="text/javascript">
10357  */
10358
10359
10360 /**
10361  * @class Roo.data.SortTypes
10362  * @singleton
10363  * Defines the default sorting (casting?) comparison functions used when sorting data.
10364  */
10365 Roo.data.SortTypes = {
10366     /**
10367      * Default sort that does nothing
10368      * @param {Mixed} s The value being converted
10369      * @return {Mixed} The comparison value
10370      */
10371     none : function(s){
10372         return s;
10373     },
10374     
10375     /**
10376      * The regular expression used to strip tags
10377      * @type {RegExp}
10378      * @property
10379      */
10380     stripTagsRE : /<\/?[^>]+>/gi,
10381     
10382     /**
10383      * Strips all HTML tags to sort on text only
10384      * @param {Mixed} s The value being converted
10385      * @return {String} The comparison value
10386      */
10387     asText : function(s){
10388         return String(s).replace(this.stripTagsRE, "");
10389     },
10390     
10391     /**
10392      * Strips all HTML tags to sort on text only - Case insensitive
10393      * @param {Mixed} s The value being converted
10394      * @return {String} The comparison value
10395      */
10396     asUCText : function(s){
10397         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10398     },
10399     
10400     /**
10401      * Case insensitive string
10402      * @param {Mixed} s The value being converted
10403      * @return {String} The comparison value
10404      */
10405     asUCString : function(s) {
10406         return String(s).toUpperCase();
10407     },
10408     
10409     /**
10410      * Date sorting
10411      * @param {Mixed} s The value being converted
10412      * @return {Number} The comparison value
10413      */
10414     asDate : function(s) {
10415         if(!s){
10416             return 0;
10417         }
10418         if(s instanceof Date){
10419             return s.getTime();
10420         }
10421         return Date.parse(String(s));
10422     },
10423     
10424     /**
10425      * Float sorting
10426      * @param {Mixed} s The value being converted
10427      * @return {Float} The comparison value
10428      */
10429     asFloat : function(s) {
10430         var val = parseFloat(String(s).replace(/,/g, ""));
10431         if(isNaN(val)) {
10432             val = 0;
10433         }
10434         return val;
10435     },
10436     
10437     /**
10438      * Integer sorting
10439      * @param {Mixed} s The value being converted
10440      * @return {Number} The comparison value
10441      */
10442     asInt : function(s) {
10443         var val = parseInt(String(s).replace(/,/g, ""));
10444         if(isNaN(val)) {
10445             val = 0;
10446         }
10447         return val;
10448     }
10449 };/*
10450  * Based on:
10451  * Ext JS Library 1.1.1
10452  * Copyright(c) 2006-2007, Ext JS, LLC.
10453  *
10454  * Originally Released Under LGPL - original licence link has changed is not relivant.
10455  *
10456  * Fork - LGPL
10457  * <script type="text/javascript">
10458  */
10459
10460 /**
10461 * @class Roo.data.Record
10462  * Instances of this class encapsulate both record <em>definition</em> information, and record
10463  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10464  * to access Records cached in an {@link Roo.data.Store} object.<br>
10465  * <p>
10466  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10467  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10468  * objects.<br>
10469  * <p>
10470  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10471  * @constructor
10472  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10473  * {@link #create}. The parameters are the same.
10474  * @param {Array} data An associative Array of data values keyed by the field name.
10475  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10476  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10477  * not specified an integer id is generated.
10478  */
10479 Roo.data.Record = function(data, id){
10480     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10481     this.data = data;
10482 };
10483
10484 /**
10485  * Generate a constructor for a specific record layout.
10486  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10487  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10488  * Each field definition object may contain the following properties: <ul>
10489  * <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,
10490  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10491  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10492  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10493  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10494  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10495  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10496  * this may be omitted.</p></li>
10497  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10498  * <ul><li>auto (Default, implies no conversion)</li>
10499  * <li>string</li>
10500  * <li>int</li>
10501  * <li>float</li>
10502  * <li>boolean</li>
10503  * <li>date</li></ul></p></li>
10504  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10505  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10506  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10507  * by the Reader into an object that will be stored in the Record. It is passed the
10508  * following parameters:<ul>
10509  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10510  * </ul></p></li>
10511  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10512  * </ul>
10513  * <br>usage:<br><pre><code>
10514 var TopicRecord = Roo.data.Record.create(
10515     {name: 'title', mapping: 'topic_title'},
10516     {name: 'author', mapping: 'username'},
10517     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10518     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10519     {name: 'lastPoster', mapping: 'user2'},
10520     {name: 'excerpt', mapping: 'post_text'}
10521 );
10522
10523 var myNewRecord = new TopicRecord({
10524     title: 'Do my job please',
10525     author: 'noobie',
10526     totalPosts: 1,
10527     lastPost: new Date(),
10528     lastPoster: 'Animal',
10529     excerpt: 'No way dude!'
10530 });
10531 myStore.add(myNewRecord);
10532 </code></pre>
10533  * @method create
10534  * @static
10535  */
10536 Roo.data.Record.create = function(o){
10537     var f = function(){
10538         f.superclass.constructor.apply(this, arguments);
10539     };
10540     Roo.extend(f, Roo.data.Record);
10541     var p = f.prototype;
10542     p.fields = new Roo.util.MixedCollection(false, function(field){
10543         return field.name;
10544     });
10545     for(var i = 0, len = o.length; i < len; i++){
10546         p.fields.add(new Roo.data.Field(o[i]));
10547     }
10548     f.getField = function(name){
10549         return p.fields.get(name);  
10550     };
10551     return f;
10552 };
10553
10554 Roo.data.Record.AUTO_ID = 1000;
10555 Roo.data.Record.EDIT = 'edit';
10556 Roo.data.Record.REJECT = 'reject';
10557 Roo.data.Record.COMMIT = 'commit';
10558
10559 Roo.data.Record.prototype = {
10560     /**
10561      * Readonly flag - true if this record has been modified.
10562      * @type Boolean
10563      */
10564     dirty : false,
10565     editing : false,
10566     error: null,
10567     modified: null,
10568
10569     // private
10570     join : function(store){
10571         this.store = store;
10572     },
10573
10574     /**
10575      * Set the named field to the specified value.
10576      * @param {String} name The name of the field to set.
10577      * @param {Object} value The value to set the field to.
10578      */
10579     set : function(name, value){
10580         if(this.data[name] == value){
10581             return;
10582         }
10583         this.dirty = true;
10584         if(!this.modified){
10585             this.modified = {};
10586         }
10587         if(typeof this.modified[name] == 'undefined'){
10588             this.modified[name] = this.data[name];
10589         }
10590         this.data[name] = value;
10591         if(!this.editing && this.store){
10592             this.store.afterEdit(this);
10593         }       
10594     },
10595
10596     /**
10597      * Get the value of the named field.
10598      * @param {String} name The name of the field to get the value of.
10599      * @return {Object} The value of the field.
10600      */
10601     get : function(name){
10602         return this.data[name]; 
10603     },
10604
10605     // private
10606     beginEdit : function(){
10607         this.editing = true;
10608         this.modified = {}; 
10609     },
10610
10611     // private
10612     cancelEdit : function(){
10613         this.editing = false;
10614         delete this.modified;
10615     },
10616
10617     // private
10618     endEdit : function(){
10619         this.editing = false;
10620         if(this.dirty && this.store){
10621             this.store.afterEdit(this);
10622         }
10623     },
10624
10625     /**
10626      * Usually called by the {@link Roo.data.Store} which owns the Record.
10627      * Rejects all changes made to the Record since either creation, or the last commit operation.
10628      * Modified fields are reverted to their original values.
10629      * <p>
10630      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10631      * of reject operations.
10632      */
10633     reject : function(){
10634         var m = this.modified;
10635         for(var n in m){
10636             if(typeof m[n] != "function"){
10637                 this.data[n] = m[n];
10638             }
10639         }
10640         this.dirty = false;
10641         delete this.modified;
10642         this.editing = false;
10643         if(this.store){
10644             this.store.afterReject(this);
10645         }
10646     },
10647
10648     /**
10649      * Usually called by the {@link Roo.data.Store} which owns the Record.
10650      * Commits all changes made to the Record since either creation, or the last commit operation.
10651      * <p>
10652      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10653      * of commit operations.
10654      */
10655     commit : function(){
10656         this.dirty = false;
10657         delete this.modified;
10658         this.editing = false;
10659         if(this.store){
10660             this.store.afterCommit(this);
10661         }
10662     },
10663
10664     // private
10665     hasError : function(){
10666         return this.error != null;
10667     },
10668
10669     // private
10670     clearError : function(){
10671         this.error = null;
10672     },
10673
10674     /**
10675      * Creates a copy of this record.
10676      * @param {String} id (optional) A new record id if you don't want to use this record's id
10677      * @return {Record}
10678      */
10679     copy : function(newId) {
10680         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10681     }
10682 };/*
10683  * Based on:
10684  * Ext JS Library 1.1.1
10685  * Copyright(c) 2006-2007, Ext JS, LLC.
10686  *
10687  * Originally Released Under LGPL - original licence link has changed is not relivant.
10688  *
10689  * Fork - LGPL
10690  * <script type="text/javascript">
10691  */
10692
10693
10694
10695 /**
10696  * @class Roo.data.Store
10697  * @extends Roo.util.Observable
10698  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10699  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10700  * <p>
10701  * 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
10702  * has no knowledge of the format of the data returned by the Proxy.<br>
10703  * <p>
10704  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10705  * instances from the data object. These records are cached and made available through accessor functions.
10706  * @constructor
10707  * Creates a new Store.
10708  * @param {Object} config A config object containing the objects needed for the Store to access data,
10709  * and read the data into Records.
10710  */
10711 Roo.data.Store = function(config){
10712     this.data = new Roo.util.MixedCollection(false);
10713     this.data.getKey = function(o){
10714         return o.id;
10715     };
10716     this.baseParams = {};
10717     // private
10718     this.paramNames = {
10719         "start" : "start",
10720         "limit" : "limit",
10721         "sort" : "sort",
10722         "dir" : "dir",
10723         "multisort" : "_multisort"
10724     };
10725
10726     if(config && config.data){
10727         this.inlineData = config.data;
10728         delete config.data;
10729     }
10730
10731     Roo.apply(this, config);
10732     
10733     if(this.reader){ // reader passed
10734         this.reader = Roo.factory(this.reader, Roo.data);
10735         this.reader.xmodule = this.xmodule || false;
10736         if(!this.recordType){
10737             this.recordType = this.reader.recordType;
10738         }
10739         if(this.reader.onMetaChange){
10740             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10741         }
10742     }
10743
10744     if(this.recordType){
10745         this.fields = this.recordType.prototype.fields;
10746     }
10747     this.modified = [];
10748
10749     this.addEvents({
10750         /**
10751          * @event datachanged
10752          * Fires when the data cache has changed, and a widget which is using this Store
10753          * as a Record cache should refresh its view.
10754          * @param {Store} this
10755          */
10756         datachanged : true,
10757         /**
10758          * @event metachange
10759          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10760          * @param {Store} this
10761          * @param {Object} meta The JSON metadata
10762          */
10763         metachange : true,
10764         /**
10765          * @event add
10766          * Fires when Records have been added to the Store
10767          * @param {Store} this
10768          * @param {Roo.data.Record[]} records The array of Records added
10769          * @param {Number} index The index at which the record(s) were added
10770          */
10771         add : true,
10772         /**
10773          * @event remove
10774          * Fires when a Record has been removed from the Store
10775          * @param {Store} this
10776          * @param {Roo.data.Record} record The Record that was removed
10777          * @param {Number} index The index at which the record was removed
10778          */
10779         remove : true,
10780         /**
10781          * @event update
10782          * Fires when a Record has been updated
10783          * @param {Store} this
10784          * @param {Roo.data.Record} record The Record that was updated
10785          * @param {String} operation The update operation being performed.  Value may be one of:
10786          * <pre><code>
10787  Roo.data.Record.EDIT
10788  Roo.data.Record.REJECT
10789  Roo.data.Record.COMMIT
10790          * </code></pre>
10791          */
10792         update : true,
10793         /**
10794          * @event clear
10795          * Fires when the data cache has been cleared.
10796          * @param {Store} this
10797          */
10798         clear : true,
10799         /**
10800          * @event beforeload
10801          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10802          * the load action will be canceled.
10803          * @param {Store} this
10804          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10805          */
10806         beforeload : true,
10807         /**
10808          * @event beforeloadadd
10809          * Fires after a new set of Records has been loaded.
10810          * @param {Store} this
10811          * @param {Roo.data.Record[]} records The Records that were loaded
10812          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10813          */
10814         beforeloadadd : true,
10815         /**
10816          * @event load
10817          * Fires after a new set of Records has been loaded, before they are added to the store.
10818          * @param {Store} this
10819          * @param {Roo.data.Record[]} records The Records that were loaded
10820          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10821          * @params {Object} return from reader
10822          */
10823         load : true,
10824         /**
10825          * @event loadexception
10826          * Fires if an exception occurs in the Proxy during loading.
10827          * Called with the signature of the Proxy's "loadexception" event.
10828          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10829          * 
10830          * @param {Proxy} 
10831          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10832          * @param {Object} load options 
10833          * @param {Object} jsonData from your request (normally this contains the Exception)
10834          */
10835         loadexception : true
10836     });
10837     
10838     if(this.proxy){
10839         this.proxy = Roo.factory(this.proxy, Roo.data);
10840         this.proxy.xmodule = this.xmodule || false;
10841         this.relayEvents(this.proxy,  ["loadexception"]);
10842     }
10843     this.sortToggle = {};
10844     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10845
10846     Roo.data.Store.superclass.constructor.call(this);
10847
10848     if(this.inlineData){
10849         this.loadData(this.inlineData);
10850         delete this.inlineData;
10851     }
10852 };
10853
10854 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10855      /**
10856     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10857     * without a remote query - used by combo/forms at present.
10858     */
10859     
10860     /**
10861     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10862     */
10863     /**
10864     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10865     */
10866     /**
10867     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10868     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10869     */
10870     /**
10871     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10872     * on any HTTP request
10873     */
10874     /**
10875     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10876     */
10877     /**
10878     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10879     */
10880     multiSort: false,
10881     /**
10882     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10883     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10884     */
10885     remoteSort : false,
10886
10887     /**
10888     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10889      * loaded or when a record is removed. (defaults to false).
10890     */
10891     pruneModifiedRecords : false,
10892
10893     // private
10894     lastOptions : null,
10895
10896     /**
10897      * Add Records to the Store and fires the add event.
10898      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10899      */
10900     add : function(records){
10901         records = [].concat(records);
10902         for(var i = 0, len = records.length; i < len; i++){
10903             records[i].join(this);
10904         }
10905         var index = this.data.length;
10906         this.data.addAll(records);
10907         this.fireEvent("add", this, records, index);
10908     },
10909
10910     /**
10911      * Remove a Record from the Store and fires the remove event.
10912      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10913      */
10914     remove : function(record){
10915         var index = this.data.indexOf(record);
10916         this.data.removeAt(index);
10917         if(this.pruneModifiedRecords){
10918             this.modified.remove(record);
10919         }
10920         this.fireEvent("remove", this, record, index);
10921     },
10922
10923     /**
10924      * Remove all Records from the Store and fires the clear event.
10925      */
10926     removeAll : function(){
10927         this.data.clear();
10928         if(this.pruneModifiedRecords){
10929             this.modified = [];
10930         }
10931         this.fireEvent("clear", this);
10932     },
10933
10934     /**
10935      * Inserts Records to the Store at the given index and fires the add event.
10936      * @param {Number} index The start index at which to insert the passed Records.
10937      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10938      */
10939     insert : function(index, records){
10940         records = [].concat(records);
10941         for(var i = 0, len = records.length; i < len; i++){
10942             this.data.insert(index, records[i]);
10943             records[i].join(this);
10944         }
10945         this.fireEvent("add", this, records, index);
10946     },
10947
10948     /**
10949      * Get the index within the cache of the passed Record.
10950      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10951      * @return {Number} The index of the passed Record. Returns -1 if not found.
10952      */
10953     indexOf : function(record){
10954         return this.data.indexOf(record);
10955     },
10956
10957     /**
10958      * Get the index within the cache of the Record with the passed id.
10959      * @param {String} id The id of the Record to find.
10960      * @return {Number} The index of the Record. Returns -1 if not found.
10961      */
10962     indexOfId : function(id){
10963         return this.data.indexOfKey(id);
10964     },
10965
10966     /**
10967      * Get the Record with the specified id.
10968      * @param {String} id The id of the Record to find.
10969      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10970      */
10971     getById : function(id){
10972         return this.data.key(id);
10973     },
10974
10975     /**
10976      * Get the Record at the specified index.
10977      * @param {Number} index The index of the Record to find.
10978      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10979      */
10980     getAt : function(index){
10981         return this.data.itemAt(index);
10982     },
10983
10984     /**
10985      * Returns a range of Records between specified indices.
10986      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10987      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10988      * @return {Roo.data.Record[]} An array of Records
10989      */
10990     getRange : function(start, end){
10991         return this.data.getRange(start, end);
10992     },
10993
10994     // private
10995     storeOptions : function(o){
10996         o = Roo.apply({}, o);
10997         delete o.callback;
10998         delete o.scope;
10999         this.lastOptions = o;
11000     },
11001
11002     /**
11003      * Loads the Record cache from the configured Proxy using the configured Reader.
11004      * <p>
11005      * If using remote paging, then the first load call must specify the <em>start</em>
11006      * and <em>limit</em> properties in the options.params property to establish the initial
11007      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11008      * <p>
11009      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11010      * and this call will return before the new data has been loaded. Perform any post-processing
11011      * in a callback function, or in a "load" event handler.</strong>
11012      * <p>
11013      * @param {Object} options An object containing properties which control loading options:<ul>
11014      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11015      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11016      * passed the following arguments:<ul>
11017      * <li>r : Roo.data.Record[]</li>
11018      * <li>options: Options object from the load call</li>
11019      * <li>success: Boolean success indicator</li></ul></li>
11020      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11021      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11022      * </ul>
11023      */
11024     load : function(options){
11025         options = options || {};
11026         if(this.fireEvent("beforeload", this, options) !== false){
11027             this.storeOptions(options);
11028             var p = Roo.apply(options.params || {}, this.baseParams);
11029             // if meta was not loaded from remote source.. try requesting it.
11030             if (!this.reader.metaFromRemote) {
11031                 p._requestMeta = 1;
11032             }
11033             if(this.sortInfo && this.remoteSort){
11034                 var pn = this.paramNames;
11035                 p[pn["sort"]] = this.sortInfo.field;
11036                 p[pn["dir"]] = this.sortInfo.direction;
11037             }
11038             if (this.multiSort) {
11039                 var pn = this.paramNames;
11040                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11041             }
11042             
11043             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11044         }
11045     },
11046
11047     /**
11048      * Reloads the Record cache from the configured Proxy using the configured Reader and
11049      * the options from the last load operation performed.
11050      * @param {Object} options (optional) An object containing properties which may override the options
11051      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11052      * the most recently used options are reused).
11053      */
11054     reload : function(options){
11055         this.load(Roo.applyIf(options||{}, this.lastOptions));
11056     },
11057
11058     // private
11059     // Called as a callback by the Reader during a load operation.
11060     loadRecords : function(o, options, success){
11061         if(!o || success === false){
11062             if(success !== false){
11063                 this.fireEvent("load", this, [], options, o);
11064             }
11065             if(options.callback){
11066                 options.callback.call(options.scope || this, [], options, false);
11067             }
11068             return;
11069         }
11070         // if data returned failure - throw an exception.
11071         if (o.success === false) {
11072             // show a message if no listener is registered.
11073             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11074                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11075             }
11076             // loadmask wil be hooked into this..
11077             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11078             return;
11079         }
11080         var r = o.records, t = o.totalRecords || r.length;
11081         
11082         this.fireEvent("beforeloadadd", this, r, options, o);
11083         
11084         if(!options || options.add !== true){
11085             if(this.pruneModifiedRecords){
11086                 this.modified = [];
11087             }
11088             for(var i = 0, len = r.length; i < len; i++){
11089                 r[i].join(this);
11090             }
11091             if(this.snapshot){
11092                 this.data = this.snapshot;
11093                 delete this.snapshot;
11094             }
11095             this.data.clear();
11096             this.data.addAll(r);
11097             this.totalLength = t;
11098             this.applySort();
11099             this.fireEvent("datachanged", this);
11100         }else{
11101             this.totalLength = Math.max(t, this.data.length+r.length);
11102             this.add(r);
11103         }
11104         
11105         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11106                 
11107             var e = new Roo.data.Record({});
11108
11109             e.set(this.parent.displayField, this.parent.emptyTitle);
11110             e.set(this.parent.valueField, '');
11111
11112             this.insert(0, e);
11113         }
11114             
11115         this.fireEvent("load", this, r, options, o);
11116         if(options.callback){
11117             options.callback.call(options.scope || this, r, options, true);
11118         }
11119     },
11120
11121
11122     /**
11123      * Loads data from a passed data block. A Reader which understands the format of the data
11124      * must have been configured in the constructor.
11125      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11126      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11127      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11128      */
11129     loadData : function(o, append){
11130         var r = this.reader.readRecords(o);
11131         this.loadRecords(r, {add: append}, true);
11132     },
11133
11134     /**
11135      * Gets the number of cached records.
11136      * <p>
11137      * <em>If using paging, this may not be the total size of the dataset. If the data object
11138      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11139      * the data set size</em>
11140      */
11141     getCount : function(){
11142         return this.data.length || 0;
11143     },
11144
11145     /**
11146      * Gets the total number of records in the dataset as returned by the server.
11147      * <p>
11148      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11149      * the dataset size</em>
11150      */
11151     getTotalCount : function(){
11152         return this.totalLength || 0;
11153     },
11154
11155     /**
11156      * Returns the sort state of the Store as an object with two properties:
11157      * <pre><code>
11158  field {String} The name of the field by which the Records are sorted
11159  direction {String} The sort order, "ASC" or "DESC"
11160      * </code></pre>
11161      */
11162     getSortState : function(){
11163         return this.sortInfo;
11164     },
11165
11166     // private
11167     applySort : function(){
11168         if(this.sortInfo && !this.remoteSort){
11169             var s = this.sortInfo, f = s.field;
11170             var st = this.fields.get(f).sortType;
11171             var fn = function(r1, r2){
11172                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11173                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11174             };
11175             this.data.sort(s.direction, fn);
11176             if(this.snapshot && this.snapshot != this.data){
11177                 this.snapshot.sort(s.direction, fn);
11178             }
11179         }
11180     },
11181
11182     /**
11183      * Sets the default sort column and order to be used by the next load operation.
11184      * @param {String} fieldName The name of the field to sort by.
11185      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11186      */
11187     setDefaultSort : function(field, dir){
11188         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11189     },
11190
11191     /**
11192      * Sort the Records.
11193      * If remote sorting is used, the sort is performed on the server, and the cache is
11194      * reloaded. If local sorting is used, the cache is sorted internally.
11195      * @param {String} fieldName The name of the field to sort by.
11196      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11197      */
11198     sort : function(fieldName, dir){
11199         var f = this.fields.get(fieldName);
11200         if(!dir){
11201             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11202             
11203             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11204                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11205             }else{
11206                 dir = f.sortDir;
11207             }
11208         }
11209         this.sortToggle[f.name] = dir;
11210         this.sortInfo = {field: f.name, direction: dir};
11211         if(!this.remoteSort){
11212             this.applySort();
11213             this.fireEvent("datachanged", this);
11214         }else{
11215             this.load(this.lastOptions);
11216         }
11217     },
11218
11219     /**
11220      * Calls the specified function for each of the Records in the cache.
11221      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11222      * Returning <em>false</em> aborts and exits the iteration.
11223      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11224      */
11225     each : function(fn, scope){
11226         this.data.each(fn, scope);
11227     },
11228
11229     /**
11230      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11231      * (e.g., during paging).
11232      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11233      */
11234     getModifiedRecords : function(){
11235         return this.modified;
11236     },
11237
11238     // private
11239     createFilterFn : function(property, value, anyMatch){
11240         if(!value.exec){ // not a regex
11241             value = String(value);
11242             if(value.length == 0){
11243                 return false;
11244             }
11245             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11246         }
11247         return function(r){
11248             return value.test(r.data[property]);
11249         };
11250     },
11251
11252     /**
11253      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11254      * @param {String} property A field on your records
11255      * @param {Number} start The record index to start at (defaults to 0)
11256      * @param {Number} end The last record index to include (defaults to length - 1)
11257      * @return {Number} The sum
11258      */
11259     sum : function(property, start, end){
11260         var rs = this.data.items, v = 0;
11261         start = start || 0;
11262         end = (end || end === 0) ? end : rs.length-1;
11263
11264         for(var i = start; i <= end; i++){
11265             v += (rs[i].data[property] || 0);
11266         }
11267         return v;
11268     },
11269
11270     /**
11271      * Filter the records by a specified property.
11272      * @param {String} field A field on your records
11273      * @param {String/RegExp} value Either a string that the field
11274      * should start with or a RegExp to test against the field
11275      * @param {Boolean} anyMatch True to match any part not just the beginning
11276      */
11277     filter : function(property, value, anyMatch){
11278         var fn = this.createFilterFn(property, value, anyMatch);
11279         return fn ? this.filterBy(fn) : this.clearFilter();
11280     },
11281
11282     /**
11283      * Filter by a function. The specified function will be called with each
11284      * record in this data source. If the function returns true the record is included,
11285      * otherwise it is filtered.
11286      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11287      * @param {Object} scope (optional) The scope of the function (defaults to this)
11288      */
11289     filterBy : function(fn, scope){
11290         this.snapshot = this.snapshot || this.data;
11291         this.data = this.queryBy(fn, scope||this);
11292         this.fireEvent("datachanged", this);
11293     },
11294
11295     /**
11296      * Query the records by a specified property.
11297      * @param {String} field A field on your records
11298      * @param {String/RegExp} value Either a string that the field
11299      * should start with or a RegExp to test against the field
11300      * @param {Boolean} anyMatch True to match any part not just the beginning
11301      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11302      */
11303     query : function(property, value, anyMatch){
11304         var fn = this.createFilterFn(property, value, anyMatch);
11305         return fn ? this.queryBy(fn) : this.data.clone();
11306     },
11307
11308     /**
11309      * Query by a function. The specified function will be called with each
11310      * record in this data source. If the function returns true the record is included
11311      * in the results.
11312      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11313      * @param {Object} scope (optional) The scope of the function (defaults to this)
11314       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11315      **/
11316     queryBy : function(fn, scope){
11317         var data = this.snapshot || this.data;
11318         return data.filterBy(fn, scope||this);
11319     },
11320
11321     /**
11322      * Collects unique values for a particular dataIndex from this store.
11323      * @param {String} dataIndex The property to collect
11324      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11325      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11326      * @return {Array} An array of the unique values
11327      **/
11328     collect : function(dataIndex, allowNull, bypassFilter){
11329         var d = (bypassFilter === true && this.snapshot) ?
11330                 this.snapshot.items : this.data.items;
11331         var v, sv, r = [], l = {};
11332         for(var i = 0, len = d.length; i < len; i++){
11333             v = d[i].data[dataIndex];
11334             sv = String(v);
11335             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11336                 l[sv] = true;
11337                 r[r.length] = v;
11338             }
11339         }
11340         return r;
11341     },
11342
11343     /**
11344      * Revert to a view of the Record cache with no filtering applied.
11345      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11346      */
11347     clearFilter : function(suppressEvent){
11348         if(this.snapshot && this.snapshot != this.data){
11349             this.data = this.snapshot;
11350             delete this.snapshot;
11351             if(suppressEvent !== true){
11352                 this.fireEvent("datachanged", this);
11353             }
11354         }
11355     },
11356
11357     // private
11358     afterEdit : function(record){
11359         if(this.modified.indexOf(record) == -1){
11360             this.modified.push(record);
11361         }
11362         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11363     },
11364     
11365     // private
11366     afterReject : function(record){
11367         this.modified.remove(record);
11368         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11369     },
11370
11371     // private
11372     afterCommit : function(record){
11373         this.modified.remove(record);
11374         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11375     },
11376
11377     /**
11378      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11379      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11380      */
11381     commitChanges : function(){
11382         var m = this.modified.slice(0);
11383         this.modified = [];
11384         for(var i = 0, len = m.length; i < len; i++){
11385             m[i].commit();
11386         }
11387     },
11388
11389     /**
11390      * Cancel outstanding changes on all changed records.
11391      */
11392     rejectChanges : function(){
11393         var m = this.modified.slice(0);
11394         this.modified = [];
11395         for(var i = 0, len = m.length; i < len; i++){
11396             m[i].reject();
11397         }
11398     },
11399
11400     onMetaChange : function(meta, rtype, o){
11401         this.recordType = rtype;
11402         this.fields = rtype.prototype.fields;
11403         delete this.snapshot;
11404         this.sortInfo = meta.sortInfo || this.sortInfo;
11405         this.modified = [];
11406         this.fireEvent('metachange', this, this.reader.meta);
11407     },
11408     
11409     moveIndex : function(data, type)
11410     {
11411         var index = this.indexOf(data);
11412         
11413         var newIndex = index + type;
11414         
11415         this.remove(data);
11416         
11417         this.insert(newIndex, data);
11418         
11419     }
11420 });/*
11421  * Based on:
11422  * Ext JS Library 1.1.1
11423  * Copyright(c) 2006-2007, Ext JS, LLC.
11424  *
11425  * Originally Released Under LGPL - original licence link has changed is not relivant.
11426  *
11427  * Fork - LGPL
11428  * <script type="text/javascript">
11429  */
11430
11431 /**
11432  * @class Roo.data.SimpleStore
11433  * @extends Roo.data.Store
11434  * Small helper class to make creating Stores from Array data easier.
11435  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11436  * @cfg {Array} fields An array of field definition objects, or field name strings.
11437  * @cfg {Array} data The multi-dimensional array of data
11438  * @constructor
11439  * @param {Object} config
11440  */
11441 Roo.data.SimpleStore = function(config){
11442     Roo.data.SimpleStore.superclass.constructor.call(this, {
11443         isLocal : true,
11444         reader: new Roo.data.ArrayReader({
11445                 id: config.id
11446             },
11447             Roo.data.Record.create(config.fields)
11448         ),
11449         proxy : new Roo.data.MemoryProxy(config.data)
11450     });
11451     this.load();
11452 };
11453 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11454  * Based on:
11455  * Ext JS Library 1.1.1
11456  * Copyright(c) 2006-2007, Ext JS, LLC.
11457  *
11458  * Originally Released Under LGPL - original licence link has changed is not relivant.
11459  *
11460  * Fork - LGPL
11461  * <script type="text/javascript">
11462  */
11463
11464 /**
11465 /**
11466  * @extends Roo.data.Store
11467  * @class Roo.data.JsonStore
11468  * Small helper class to make creating Stores for JSON data easier. <br/>
11469 <pre><code>
11470 var store = new Roo.data.JsonStore({
11471     url: 'get-images.php',
11472     root: 'images',
11473     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11474 });
11475 </code></pre>
11476  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11477  * JsonReader and HttpProxy (unless inline data is provided).</b>
11478  * @cfg {Array} fields An array of field definition objects, or field name strings.
11479  * @constructor
11480  * @param {Object} config
11481  */
11482 Roo.data.JsonStore = function(c){
11483     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11484         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11485         reader: new Roo.data.JsonReader(c, c.fields)
11486     }));
11487 };
11488 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11489  * Based on:
11490  * Ext JS Library 1.1.1
11491  * Copyright(c) 2006-2007, Ext JS, LLC.
11492  *
11493  * Originally Released Under LGPL - original licence link has changed is not relivant.
11494  *
11495  * Fork - LGPL
11496  * <script type="text/javascript">
11497  */
11498
11499  
11500 Roo.data.Field = function(config){
11501     if(typeof config == "string"){
11502         config = {name: config};
11503     }
11504     Roo.apply(this, config);
11505     
11506     if(!this.type){
11507         this.type = "auto";
11508     }
11509     
11510     var st = Roo.data.SortTypes;
11511     // named sortTypes are supported, here we look them up
11512     if(typeof this.sortType == "string"){
11513         this.sortType = st[this.sortType];
11514     }
11515     
11516     // set default sortType for strings and dates
11517     if(!this.sortType){
11518         switch(this.type){
11519             case "string":
11520                 this.sortType = st.asUCString;
11521                 break;
11522             case "date":
11523                 this.sortType = st.asDate;
11524                 break;
11525             default:
11526                 this.sortType = st.none;
11527         }
11528     }
11529
11530     // define once
11531     var stripRe = /[\$,%]/g;
11532
11533     // prebuilt conversion function for this field, instead of
11534     // switching every time we're reading a value
11535     if(!this.convert){
11536         var cv, dateFormat = this.dateFormat;
11537         switch(this.type){
11538             case "":
11539             case "auto":
11540             case undefined:
11541                 cv = function(v){ return v; };
11542                 break;
11543             case "string":
11544                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11545                 break;
11546             case "int":
11547                 cv = function(v){
11548                     return v !== undefined && v !== null && v !== '' ?
11549                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11550                     };
11551                 break;
11552             case "float":
11553                 cv = function(v){
11554                     return v !== undefined && v !== null && v !== '' ?
11555                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11556                     };
11557                 break;
11558             case "bool":
11559             case "boolean":
11560                 cv = function(v){ return v === true || v === "true" || v == 1; };
11561                 break;
11562             case "date":
11563                 cv = function(v){
11564                     if(!v){
11565                         return '';
11566                     }
11567                     if(v instanceof Date){
11568                         return v;
11569                     }
11570                     if(dateFormat){
11571                         if(dateFormat == "timestamp"){
11572                             return new Date(v*1000);
11573                         }
11574                         return Date.parseDate(v, dateFormat);
11575                     }
11576                     var parsed = Date.parse(v);
11577                     return parsed ? new Date(parsed) : null;
11578                 };
11579              break;
11580             
11581         }
11582         this.convert = cv;
11583     }
11584 };
11585
11586 Roo.data.Field.prototype = {
11587     dateFormat: null,
11588     defaultValue: "",
11589     mapping: null,
11590     sortType : null,
11591     sortDir : "ASC"
11592 };/*
11593  * Based on:
11594  * Ext JS Library 1.1.1
11595  * Copyright(c) 2006-2007, Ext JS, LLC.
11596  *
11597  * Originally Released Under LGPL - original licence link has changed is not relivant.
11598  *
11599  * Fork - LGPL
11600  * <script type="text/javascript">
11601  */
11602  
11603 // Base class for reading structured data from a data source.  This class is intended to be
11604 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11605
11606 /**
11607  * @class Roo.data.DataReader
11608  * Base class for reading structured data from a data source.  This class is intended to be
11609  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11610  */
11611
11612 Roo.data.DataReader = function(meta, recordType){
11613     
11614     this.meta = meta;
11615     
11616     this.recordType = recordType instanceof Array ? 
11617         Roo.data.Record.create(recordType) : recordType;
11618 };
11619
11620 Roo.data.DataReader.prototype = {
11621      /**
11622      * Create an empty record
11623      * @param {Object} data (optional) - overlay some values
11624      * @return {Roo.data.Record} record created.
11625      */
11626     newRow :  function(d) {
11627         var da =  {};
11628         this.recordType.prototype.fields.each(function(c) {
11629             switch( c.type) {
11630                 case 'int' : da[c.name] = 0; break;
11631                 case 'date' : da[c.name] = new Date(); break;
11632                 case 'float' : da[c.name] = 0.0; break;
11633                 case 'boolean' : da[c.name] = false; break;
11634                 default : da[c.name] = ""; break;
11635             }
11636             
11637         });
11638         return new this.recordType(Roo.apply(da, d));
11639     }
11640     
11641 };/*
11642  * Based on:
11643  * Ext JS Library 1.1.1
11644  * Copyright(c) 2006-2007, Ext JS, LLC.
11645  *
11646  * Originally Released Under LGPL - original licence link has changed is not relivant.
11647  *
11648  * Fork - LGPL
11649  * <script type="text/javascript">
11650  */
11651
11652 /**
11653  * @class Roo.data.DataProxy
11654  * @extends Roo.data.Observable
11655  * This class is an abstract base class for implementations which provide retrieval of
11656  * unformatted data objects.<br>
11657  * <p>
11658  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11659  * (of the appropriate type which knows how to parse the data object) to provide a block of
11660  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11661  * <p>
11662  * Custom implementations must implement the load method as described in
11663  * {@link Roo.data.HttpProxy#load}.
11664  */
11665 Roo.data.DataProxy = function(){
11666     this.addEvents({
11667         /**
11668          * @event beforeload
11669          * Fires before a network request is made to retrieve a data object.
11670          * @param {Object} This DataProxy object.
11671          * @param {Object} params The params parameter to the load function.
11672          */
11673         beforeload : true,
11674         /**
11675          * @event load
11676          * Fires before the load method's callback is called.
11677          * @param {Object} This DataProxy object.
11678          * @param {Object} o The data object.
11679          * @param {Object} arg The callback argument object passed to the load function.
11680          */
11681         load : true,
11682         /**
11683          * @event loadexception
11684          * Fires if an Exception occurs during data retrieval.
11685          * @param {Object} This DataProxy object.
11686          * @param {Object} o The data object.
11687          * @param {Object} arg The callback argument object passed to the load function.
11688          * @param {Object} e The Exception.
11689          */
11690         loadexception : true
11691     });
11692     Roo.data.DataProxy.superclass.constructor.call(this);
11693 };
11694
11695 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11696
11697     /**
11698      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11699      */
11700 /*
11701  * Based on:
11702  * Ext JS Library 1.1.1
11703  * Copyright(c) 2006-2007, Ext JS, LLC.
11704  *
11705  * Originally Released Under LGPL - original licence link has changed is not relivant.
11706  *
11707  * Fork - LGPL
11708  * <script type="text/javascript">
11709  */
11710 /**
11711  * @class Roo.data.MemoryProxy
11712  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11713  * to the Reader when its load method is called.
11714  * @constructor
11715  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11716  */
11717 Roo.data.MemoryProxy = function(data){
11718     if (data.data) {
11719         data = data.data;
11720     }
11721     Roo.data.MemoryProxy.superclass.constructor.call(this);
11722     this.data = data;
11723 };
11724
11725 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11726     
11727     /**
11728      * Load data from the requested source (in this case an in-memory
11729      * data object passed to the constructor), read the data object into
11730      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11731      * process that block using the passed callback.
11732      * @param {Object} params This parameter is not used by the MemoryProxy class.
11733      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11734      * object into a block of Roo.data.Records.
11735      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11736      * The function must be passed <ul>
11737      * <li>The Record block object</li>
11738      * <li>The "arg" argument from the load function</li>
11739      * <li>A boolean success indicator</li>
11740      * </ul>
11741      * @param {Object} scope The scope in which to call the callback
11742      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11743      */
11744     load : function(params, reader, callback, scope, arg){
11745         params = params || {};
11746         var result;
11747         try {
11748             result = reader.readRecords(this.data);
11749         }catch(e){
11750             this.fireEvent("loadexception", this, arg, null, e);
11751             callback.call(scope, null, arg, false);
11752             return;
11753         }
11754         callback.call(scope, result, arg, true);
11755     },
11756     
11757     // private
11758     update : function(params, records){
11759         
11760     }
11761 });/*
11762  * Based on:
11763  * Ext JS Library 1.1.1
11764  * Copyright(c) 2006-2007, Ext JS, LLC.
11765  *
11766  * Originally Released Under LGPL - original licence link has changed is not relivant.
11767  *
11768  * Fork - LGPL
11769  * <script type="text/javascript">
11770  */
11771 /**
11772  * @class Roo.data.HttpProxy
11773  * @extends Roo.data.DataProxy
11774  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11775  * configured to reference a certain URL.<br><br>
11776  * <p>
11777  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11778  * from which the running page was served.<br><br>
11779  * <p>
11780  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11781  * <p>
11782  * Be aware that to enable the browser to parse an XML document, the server must set
11783  * the Content-Type header in the HTTP response to "text/xml".
11784  * @constructor
11785  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11786  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11787  * will be used to make the request.
11788  */
11789 Roo.data.HttpProxy = function(conn){
11790     Roo.data.HttpProxy.superclass.constructor.call(this);
11791     // is conn a conn config or a real conn?
11792     this.conn = conn;
11793     this.useAjax = !conn || !conn.events;
11794   
11795 };
11796
11797 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11798     // thse are take from connection...
11799     
11800     /**
11801      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11802      */
11803     /**
11804      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11805      * extra parameters to each request made by this object. (defaults to undefined)
11806      */
11807     /**
11808      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11809      *  to each request made by this object. (defaults to undefined)
11810      */
11811     /**
11812      * @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)
11813      */
11814     /**
11815      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11816      */
11817      /**
11818      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11819      * @type Boolean
11820      */
11821   
11822
11823     /**
11824      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11825      * @type Boolean
11826      */
11827     /**
11828      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11829      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11830      * a finer-grained basis than the DataProxy events.
11831      */
11832     getConnection : function(){
11833         return this.useAjax ? Roo.Ajax : this.conn;
11834     },
11835
11836     /**
11837      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11838      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11839      * process that block using the passed callback.
11840      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11841      * for the request to the remote server.
11842      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11843      * object into a block of Roo.data.Records.
11844      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11845      * The function must be passed <ul>
11846      * <li>The Record block object</li>
11847      * <li>The "arg" argument from the load function</li>
11848      * <li>A boolean success indicator</li>
11849      * </ul>
11850      * @param {Object} scope The scope in which to call the callback
11851      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11852      */
11853     load : function(params, reader, callback, scope, arg){
11854         if(this.fireEvent("beforeload", this, params) !== false){
11855             var  o = {
11856                 params : params || {},
11857                 request: {
11858                     callback : callback,
11859                     scope : scope,
11860                     arg : arg
11861                 },
11862                 reader: reader,
11863                 callback : this.loadResponse,
11864                 scope: this
11865             };
11866             if(this.useAjax){
11867                 Roo.applyIf(o, this.conn);
11868                 if(this.activeRequest){
11869                     Roo.Ajax.abort(this.activeRequest);
11870                 }
11871                 this.activeRequest = Roo.Ajax.request(o);
11872             }else{
11873                 this.conn.request(o);
11874             }
11875         }else{
11876             callback.call(scope||this, null, arg, false);
11877         }
11878     },
11879
11880     // private
11881     loadResponse : function(o, success, response){
11882         delete this.activeRequest;
11883         if(!success){
11884             this.fireEvent("loadexception", this, o, response);
11885             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11886             return;
11887         }
11888         var result;
11889         try {
11890             result = o.reader.read(response);
11891         }catch(e){
11892             this.fireEvent("loadexception", this, o, response, e);
11893             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11894             return;
11895         }
11896         
11897         this.fireEvent("load", this, o, o.request.arg);
11898         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11899     },
11900
11901     // private
11902     update : function(dataSet){
11903
11904     },
11905
11906     // private
11907     updateResponse : function(dataSet){
11908
11909     }
11910 });/*
11911  * Based on:
11912  * Ext JS Library 1.1.1
11913  * Copyright(c) 2006-2007, Ext JS, LLC.
11914  *
11915  * Originally Released Under LGPL - original licence link has changed is not relivant.
11916  *
11917  * Fork - LGPL
11918  * <script type="text/javascript">
11919  */
11920
11921 /**
11922  * @class Roo.data.ScriptTagProxy
11923  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11924  * other than the originating domain of the running page.<br><br>
11925  * <p>
11926  * <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
11927  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11928  * <p>
11929  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11930  * source code that is used as the source inside a &lt;script> tag.<br><br>
11931  * <p>
11932  * In order for the browser to process the returned data, the server must wrap the data object
11933  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11934  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11935  * depending on whether the callback name was passed:
11936  * <p>
11937  * <pre><code>
11938 boolean scriptTag = false;
11939 String cb = request.getParameter("callback");
11940 if (cb != null) {
11941     scriptTag = true;
11942     response.setContentType("text/javascript");
11943 } else {
11944     response.setContentType("application/x-json");
11945 }
11946 Writer out = response.getWriter();
11947 if (scriptTag) {
11948     out.write(cb + "(");
11949 }
11950 out.print(dataBlock.toJsonString());
11951 if (scriptTag) {
11952     out.write(");");
11953 }
11954 </pre></code>
11955  *
11956  * @constructor
11957  * @param {Object} config A configuration object.
11958  */
11959 Roo.data.ScriptTagProxy = function(config){
11960     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11961     Roo.apply(this, config);
11962     this.head = document.getElementsByTagName("head")[0];
11963 };
11964
11965 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11966
11967 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11968     /**
11969      * @cfg {String} url The URL from which to request the data object.
11970      */
11971     /**
11972      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11973      */
11974     timeout : 30000,
11975     /**
11976      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11977      * the server the name of the callback function set up by the load call to process the returned data object.
11978      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11979      * javascript output which calls this named function passing the data object as its only parameter.
11980      */
11981     callbackParam : "callback",
11982     /**
11983      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11984      * name to the request.
11985      */
11986     nocache : true,
11987
11988     /**
11989      * Load data from the configured URL, read the data object into
11990      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11991      * process that block using the passed callback.
11992      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11993      * for the request to the remote server.
11994      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11995      * object into a block of Roo.data.Records.
11996      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11997      * The function must be passed <ul>
11998      * <li>The Record block object</li>
11999      * <li>The "arg" argument from the load function</li>
12000      * <li>A boolean success indicator</li>
12001      * </ul>
12002      * @param {Object} scope The scope in which to call the callback
12003      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12004      */
12005     load : function(params, reader, callback, scope, arg){
12006         if(this.fireEvent("beforeload", this, params) !== false){
12007
12008             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12009
12010             var url = this.url;
12011             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12012             if(this.nocache){
12013                 url += "&_dc=" + (new Date().getTime());
12014             }
12015             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12016             var trans = {
12017                 id : transId,
12018                 cb : "stcCallback"+transId,
12019                 scriptId : "stcScript"+transId,
12020                 params : params,
12021                 arg : arg,
12022                 url : url,
12023                 callback : callback,
12024                 scope : scope,
12025                 reader : reader
12026             };
12027             var conn = this;
12028
12029             window[trans.cb] = function(o){
12030                 conn.handleResponse(o, trans);
12031             };
12032
12033             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12034
12035             if(this.autoAbort !== false){
12036                 this.abort();
12037             }
12038
12039             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12040
12041             var script = document.createElement("script");
12042             script.setAttribute("src", url);
12043             script.setAttribute("type", "text/javascript");
12044             script.setAttribute("id", trans.scriptId);
12045             this.head.appendChild(script);
12046
12047             this.trans = trans;
12048         }else{
12049             callback.call(scope||this, null, arg, false);
12050         }
12051     },
12052
12053     // private
12054     isLoading : function(){
12055         return this.trans ? true : false;
12056     },
12057
12058     /**
12059      * Abort the current server request.
12060      */
12061     abort : function(){
12062         if(this.isLoading()){
12063             this.destroyTrans(this.trans);
12064         }
12065     },
12066
12067     // private
12068     destroyTrans : function(trans, isLoaded){
12069         this.head.removeChild(document.getElementById(trans.scriptId));
12070         clearTimeout(trans.timeoutId);
12071         if(isLoaded){
12072             window[trans.cb] = undefined;
12073             try{
12074                 delete window[trans.cb];
12075             }catch(e){}
12076         }else{
12077             // if hasn't been loaded, wait for load to remove it to prevent script error
12078             window[trans.cb] = function(){
12079                 window[trans.cb] = undefined;
12080                 try{
12081                     delete window[trans.cb];
12082                 }catch(e){}
12083             };
12084         }
12085     },
12086
12087     // private
12088     handleResponse : function(o, trans){
12089         this.trans = false;
12090         this.destroyTrans(trans, true);
12091         var result;
12092         try {
12093             result = trans.reader.readRecords(o);
12094         }catch(e){
12095             this.fireEvent("loadexception", this, o, trans.arg, e);
12096             trans.callback.call(trans.scope||window, null, trans.arg, false);
12097             return;
12098         }
12099         this.fireEvent("load", this, o, trans.arg);
12100         trans.callback.call(trans.scope||window, result, trans.arg, true);
12101     },
12102
12103     // private
12104     handleFailure : function(trans){
12105         this.trans = false;
12106         this.destroyTrans(trans, false);
12107         this.fireEvent("loadexception", this, null, trans.arg);
12108         trans.callback.call(trans.scope||window, null, trans.arg, false);
12109     }
12110 });/*
12111  * Based on:
12112  * Ext JS Library 1.1.1
12113  * Copyright(c) 2006-2007, Ext JS, LLC.
12114  *
12115  * Originally Released Under LGPL - original licence link has changed is not relivant.
12116  *
12117  * Fork - LGPL
12118  * <script type="text/javascript">
12119  */
12120
12121 /**
12122  * @class Roo.data.JsonReader
12123  * @extends Roo.data.DataReader
12124  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12125  * based on mappings in a provided Roo.data.Record constructor.
12126  * 
12127  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12128  * in the reply previously. 
12129  * 
12130  * <p>
12131  * Example code:
12132  * <pre><code>
12133 var RecordDef = Roo.data.Record.create([
12134     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12135     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12136 ]);
12137 var myReader = new Roo.data.JsonReader({
12138     totalProperty: "results",    // The property which contains the total dataset size (optional)
12139     root: "rows",                // The property which contains an Array of row objects
12140     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12141 }, RecordDef);
12142 </code></pre>
12143  * <p>
12144  * This would consume a JSON file like this:
12145  * <pre><code>
12146 { 'results': 2, 'rows': [
12147     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12148     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12149 }
12150 </code></pre>
12151  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12152  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12153  * paged from the remote server.
12154  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12155  * @cfg {String} root name of the property which contains the Array of row objects.
12156  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12157  * @cfg {Array} fields Array of field definition objects
12158  * @constructor
12159  * Create a new JsonReader
12160  * @param {Object} meta Metadata configuration options
12161  * @param {Object} recordType Either an Array of field definition objects,
12162  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12163  */
12164 Roo.data.JsonReader = function(meta, recordType){
12165     
12166     meta = meta || {};
12167     // set some defaults:
12168     Roo.applyIf(meta, {
12169         totalProperty: 'total',
12170         successProperty : 'success',
12171         root : 'data',
12172         id : 'id'
12173     });
12174     
12175     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12176 };
12177 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12178     
12179     /**
12180      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12181      * Used by Store query builder to append _requestMeta to params.
12182      * 
12183      */
12184     metaFromRemote : false,
12185     /**
12186      * This method is only used by a DataProxy which has retrieved data from a remote server.
12187      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12188      * @return {Object} data A data block which is used by an Roo.data.Store object as
12189      * a cache of Roo.data.Records.
12190      */
12191     read : function(response){
12192         var json = response.responseText;
12193        
12194         var o = /* eval:var:o */ eval("("+json+")");
12195         if(!o) {
12196             throw {message: "JsonReader.read: Json object not found"};
12197         }
12198         
12199         if(o.metaData){
12200             
12201             delete this.ef;
12202             this.metaFromRemote = true;
12203             this.meta = o.metaData;
12204             this.recordType = Roo.data.Record.create(o.metaData.fields);
12205             this.onMetaChange(this.meta, this.recordType, o);
12206         }
12207         return this.readRecords(o);
12208     },
12209
12210     // private function a store will implement
12211     onMetaChange : function(meta, recordType, o){
12212
12213     },
12214
12215     /**
12216          * @ignore
12217          */
12218     simpleAccess: function(obj, subsc) {
12219         return obj[subsc];
12220     },
12221
12222         /**
12223          * @ignore
12224          */
12225     getJsonAccessor: function(){
12226         var re = /[\[\.]/;
12227         return function(expr) {
12228             try {
12229                 return(re.test(expr))
12230                     ? new Function("obj", "return obj." + expr)
12231                     : function(obj){
12232                         return obj[expr];
12233                     };
12234             } catch(e){}
12235             return Roo.emptyFn;
12236         };
12237     }(),
12238
12239     /**
12240      * Create a data block containing Roo.data.Records from an XML document.
12241      * @param {Object} o An object which contains an Array of row objects in the property specified
12242      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12243      * which contains the total size of the dataset.
12244      * @return {Object} data A data block which is used by an Roo.data.Store object as
12245      * a cache of Roo.data.Records.
12246      */
12247     readRecords : function(o){
12248         /**
12249          * After any data loads, the raw JSON data is available for further custom processing.
12250          * @type Object
12251          */
12252         this.o = o;
12253         var s = this.meta, Record = this.recordType,
12254             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12255
12256 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12257         if (!this.ef) {
12258             if(s.totalProperty) {
12259                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12260                 }
12261                 if(s.successProperty) {
12262                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12263                 }
12264                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12265                 if (s.id) {
12266                         var g = this.getJsonAccessor(s.id);
12267                         this.getId = function(rec) {
12268                                 var r = g(rec);  
12269                                 return (r === undefined || r === "") ? null : r;
12270                         };
12271                 } else {
12272                         this.getId = function(){return null;};
12273                 }
12274             this.ef = [];
12275             for(var jj = 0; jj < fl; jj++){
12276                 f = fi[jj];
12277                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12278                 this.ef[jj] = this.getJsonAccessor(map);
12279             }
12280         }
12281
12282         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12283         if(s.totalProperty){
12284             var vt = parseInt(this.getTotal(o), 10);
12285             if(!isNaN(vt)){
12286                 totalRecords = vt;
12287             }
12288         }
12289         if(s.successProperty){
12290             var vs = this.getSuccess(o);
12291             if(vs === false || vs === 'false'){
12292                 success = false;
12293             }
12294         }
12295         var records = [];
12296         for(var i = 0; i < c; i++){
12297                 var n = root[i];
12298             var values = {};
12299             var id = this.getId(n);
12300             for(var j = 0; j < fl; j++){
12301                 f = fi[j];
12302             var v = this.ef[j](n);
12303             if (!f.convert) {
12304                 Roo.log('missing convert for ' + f.name);
12305                 Roo.log(f);
12306                 continue;
12307             }
12308             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12309             }
12310             var record = new Record(values, id);
12311             record.json = n;
12312             records[i] = record;
12313         }
12314         return {
12315             raw : o,
12316             success : success,
12317             records : records,
12318             totalRecords : totalRecords
12319         };
12320     }
12321 });/*
12322  * Based on:
12323  * Ext JS Library 1.1.1
12324  * Copyright(c) 2006-2007, Ext JS, LLC.
12325  *
12326  * Originally Released Under LGPL - original licence link has changed is not relivant.
12327  *
12328  * Fork - LGPL
12329  * <script type="text/javascript">
12330  */
12331
12332 /**
12333  * @class Roo.data.ArrayReader
12334  * @extends Roo.data.DataReader
12335  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12336  * Each element of that Array represents a row of data fields. The
12337  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12338  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12339  * <p>
12340  * Example code:.
12341  * <pre><code>
12342 var RecordDef = Roo.data.Record.create([
12343     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12344     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12345 ]);
12346 var myReader = new Roo.data.ArrayReader({
12347     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12348 }, RecordDef);
12349 </code></pre>
12350  * <p>
12351  * This would consume an Array like this:
12352  * <pre><code>
12353 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12354   </code></pre>
12355  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12356  * @constructor
12357  * Create a new JsonReader
12358  * @param {Object} meta Metadata configuration options.
12359  * @param {Object} recordType Either an Array of field definition objects
12360  * as specified to {@link Roo.data.Record#create},
12361  * or an {@link Roo.data.Record} object
12362  * created using {@link Roo.data.Record#create}.
12363  */
12364 Roo.data.ArrayReader = function(meta, recordType){
12365     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12366 };
12367
12368 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12369     /**
12370      * Create a data block containing Roo.data.Records from an XML document.
12371      * @param {Object} o An Array of row objects which represents the dataset.
12372      * @return {Object} data A data block which is used by an Roo.data.Store object as
12373      * a cache of Roo.data.Records.
12374      */
12375     readRecords : function(o){
12376         var sid = this.meta ? this.meta.id : null;
12377         var recordType = this.recordType, fields = recordType.prototype.fields;
12378         var records = [];
12379         var root = o;
12380             for(var i = 0; i < root.length; i++){
12381                     var n = root[i];
12382                 var values = {};
12383                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12384                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12385                 var f = fields.items[j];
12386                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12387                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12388                 v = f.convert(v);
12389                 values[f.name] = v;
12390             }
12391                 var record = new recordType(values, id);
12392                 record.json = n;
12393                 records[records.length] = record;
12394             }
12395             return {
12396                 records : records,
12397                 totalRecords : records.length
12398             };
12399     }
12400 });/*
12401  * - LGPL
12402  * * 
12403  */
12404
12405 /**
12406  * @class Roo.bootstrap.ComboBox
12407  * @extends Roo.bootstrap.TriggerField
12408  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12409  * @cfg {Boolean} append (true|false) default false
12410  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12411  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12412  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12413  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12414  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12415  * @cfg {Boolean} animate default true
12416  * @cfg {Boolean} emptyResultText only for touch device
12417  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12418  * @cfg {String} emptyTitle default ''
12419  * @constructor
12420  * Create a new ComboBox.
12421  * @param {Object} config Configuration options
12422  */
12423 Roo.bootstrap.ComboBox = function(config){
12424     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12425     this.addEvents({
12426         /**
12427          * @event expand
12428          * Fires when the dropdown list is expanded
12429         * @param {Roo.bootstrap.ComboBox} combo This combo box
12430         */
12431         'expand' : true,
12432         /**
12433          * @event collapse
12434          * Fires when the dropdown list is collapsed
12435         * @param {Roo.bootstrap.ComboBox} combo This combo box
12436         */
12437         'collapse' : true,
12438         /**
12439          * @event beforeselect
12440          * Fires before a list item is selected. Return false to cancel the selection.
12441         * @param {Roo.bootstrap.ComboBox} combo This combo box
12442         * @param {Roo.data.Record} record The data record returned from the underlying store
12443         * @param {Number} index The index of the selected item in the dropdown list
12444         */
12445         'beforeselect' : true,
12446         /**
12447          * @event select
12448          * Fires when a list item is selected
12449         * @param {Roo.bootstrap.ComboBox} combo This combo box
12450         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12451         * @param {Number} index The index of the selected item in the dropdown list
12452         */
12453         'select' : true,
12454         /**
12455          * @event beforequery
12456          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12457          * The event object passed has these properties:
12458         * @param {Roo.bootstrap.ComboBox} combo This combo box
12459         * @param {String} query The query
12460         * @param {Boolean} forceAll true to force "all" query
12461         * @param {Boolean} cancel true to cancel the query
12462         * @param {Object} e The query event object
12463         */
12464         'beforequery': true,
12465          /**
12466          * @event add
12467          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12468         * @param {Roo.bootstrap.ComboBox} combo This combo box
12469         */
12470         'add' : true,
12471         /**
12472          * @event edit
12473          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12474         * @param {Roo.bootstrap.ComboBox} combo This combo box
12475         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12476         */
12477         'edit' : true,
12478         /**
12479          * @event remove
12480          * Fires when the remove value from the combobox array
12481         * @param {Roo.bootstrap.ComboBox} combo This combo box
12482         */
12483         'remove' : true,
12484         /**
12485          * @event afterremove
12486          * Fires when the remove value from the combobox array
12487         * @param {Roo.bootstrap.ComboBox} combo This combo box
12488         */
12489         'afterremove' : true,
12490         /**
12491          * @event specialfilter
12492          * Fires when specialfilter
12493             * @param {Roo.bootstrap.ComboBox} combo This combo box
12494             */
12495         'specialfilter' : true,
12496         /**
12497          * @event tick
12498          * Fires when tick the element
12499             * @param {Roo.bootstrap.ComboBox} combo This combo box
12500             */
12501         'tick' : true,
12502         /**
12503          * @event touchviewdisplay
12504          * Fires when touch view require special display (default is using displayField)
12505             * @param {Roo.bootstrap.ComboBox} combo This combo box
12506             * @param {Object} cfg set html .
12507             */
12508         'touchviewdisplay' : true
12509         
12510     });
12511     
12512     this.item = [];
12513     this.tickItems = [];
12514     
12515     this.selectedIndex = -1;
12516     if(this.mode == 'local'){
12517         if(config.queryDelay === undefined){
12518             this.queryDelay = 10;
12519         }
12520         if(config.minChars === undefined){
12521             this.minChars = 0;
12522         }
12523     }
12524 };
12525
12526 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12527      
12528     /**
12529      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12530      * rendering into an Roo.Editor, defaults to false)
12531      */
12532     /**
12533      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12534      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12535      */
12536     /**
12537      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12538      */
12539     /**
12540      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12541      * the dropdown list (defaults to undefined, with no header element)
12542      */
12543
12544      /**
12545      * @cfg {String/Roo.Template} tpl The template to use to render the output
12546      */
12547      
12548      /**
12549      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12550      */
12551     listWidth: undefined,
12552     /**
12553      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12554      * mode = 'remote' or 'text' if mode = 'local')
12555      */
12556     displayField: undefined,
12557     
12558     /**
12559      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12560      * mode = 'remote' or 'value' if mode = 'local'). 
12561      * Note: use of a valueField requires the user make a selection
12562      * in order for a value to be mapped.
12563      */
12564     valueField: undefined,
12565     /**
12566      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12567      */
12568     modalTitle : '',
12569     
12570     /**
12571      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12572      * field's data value (defaults to the underlying DOM element's name)
12573      */
12574     hiddenName: undefined,
12575     /**
12576      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12577      */
12578     listClass: '',
12579     /**
12580      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12581      */
12582     selectedClass: 'active',
12583     
12584     /**
12585      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12586      */
12587     shadow:'sides',
12588     /**
12589      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12590      * anchor positions (defaults to 'tl-bl')
12591      */
12592     listAlign: 'tl-bl?',
12593     /**
12594      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12595      */
12596     maxHeight: 300,
12597     /**
12598      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12599      * query specified by the allQuery config option (defaults to 'query')
12600      */
12601     triggerAction: 'query',
12602     /**
12603      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12604      * (defaults to 4, does not apply if editable = false)
12605      */
12606     minChars : 4,
12607     /**
12608      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12609      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12610      */
12611     typeAhead: false,
12612     /**
12613      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12614      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12615      */
12616     queryDelay: 500,
12617     /**
12618      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12619      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12620      */
12621     pageSize: 0,
12622     /**
12623      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12624      * when editable = true (defaults to false)
12625      */
12626     selectOnFocus:false,
12627     /**
12628      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12629      */
12630     queryParam: 'query',
12631     /**
12632      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12633      * when mode = 'remote' (defaults to 'Loading...')
12634      */
12635     loadingText: 'Loading...',
12636     /**
12637      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12638      */
12639     resizable: false,
12640     /**
12641      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12642      */
12643     handleHeight : 8,
12644     /**
12645      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12646      * traditional select (defaults to true)
12647      */
12648     editable: true,
12649     /**
12650      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12651      */
12652     allQuery: '',
12653     /**
12654      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12655      */
12656     mode: 'remote',
12657     /**
12658      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12659      * listWidth has a higher value)
12660      */
12661     minListWidth : 70,
12662     /**
12663      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12664      * allow the user to set arbitrary text into the field (defaults to false)
12665      */
12666     forceSelection:false,
12667     /**
12668      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12669      * if typeAhead = true (defaults to 250)
12670      */
12671     typeAheadDelay : 250,
12672     /**
12673      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12674      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12675      */
12676     valueNotFoundText : undefined,
12677     /**
12678      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12679      */
12680     blockFocus : false,
12681     
12682     /**
12683      * @cfg {Boolean} disableClear Disable showing of clear button.
12684      */
12685     disableClear : false,
12686     /**
12687      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12688      */
12689     alwaysQuery : false,
12690     
12691     /**
12692      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12693      */
12694     multiple : false,
12695     
12696     /**
12697      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12698      */
12699     invalidClass : "has-warning",
12700     
12701     /**
12702      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12703      */
12704     validClass : "has-success",
12705     
12706     /**
12707      * @cfg {Boolean} specialFilter (true|false) special filter default false
12708      */
12709     specialFilter : false,
12710     
12711     /**
12712      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12713      */
12714     mobileTouchView : true,
12715     
12716     /**
12717      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12718      */
12719     useNativeIOS : false,
12720     
12721     ios_options : false,
12722     
12723     //private
12724     addicon : false,
12725     editicon: false,
12726     
12727     page: 0,
12728     hasQuery: false,
12729     append: false,
12730     loadNext: false,
12731     autoFocus : true,
12732     tickable : false,
12733     btnPosition : 'right',
12734     triggerList : true,
12735     showToggleBtn : true,
12736     animate : true,
12737     emptyResultText: 'Empty',
12738     triggerText : 'Select',
12739     emptyTitle : '',
12740     
12741     // element that contains real text value.. (when hidden is used..)
12742     
12743     getAutoCreate : function()
12744     {   
12745         var cfg = false;
12746         //render
12747         /*
12748          * Render classic select for iso
12749          */
12750         
12751         if(Roo.isIOS && this.useNativeIOS){
12752             cfg = this.getAutoCreateNativeIOS();
12753             return cfg;
12754         }
12755         
12756         /*
12757          * Touch Devices
12758          */
12759         
12760         if(Roo.isTouch && this.mobileTouchView){
12761             cfg = this.getAutoCreateTouchView();
12762             return cfg;;
12763         }
12764         
12765         /*
12766          *  Normal ComboBox
12767          */
12768         if(!this.tickable){
12769             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12770             return cfg;
12771         }
12772         
12773         /*
12774          *  ComboBox with tickable selections
12775          */
12776              
12777         var align = this.labelAlign || this.parentLabelAlign();
12778         
12779         cfg = {
12780             cls : 'form-group roo-combobox-tickable' //input-group
12781         };
12782         
12783         var btn_text_select = '';
12784         var btn_text_done = '';
12785         var btn_text_cancel = '';
12786         
12787         if (this.btn_text_show) {
12788             btn_text_select = 'Select';
12789             btn_text_done = 'Done';
12790             btn_text_cancel = 'Cancel'; 
12791         }
12792         
12793         var buttons = {
12794             tag : 'div',
12795             cls : 'tickable-buttons',
12796             cn : [
12797                 {
12798                     tag : 'button',
12799                     type : 'button',
12800                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12801                     //html : this.triggerText
12802                     html: btn_text_select
12803                 },
12804                 {
12805                     tag : 'button',
12806                     type : 'button',
12807                     name : 'ok',
12808                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12809                     //html : 'Done'
12810                     html: btn_text_done
12811                 },
12812                 {
12813                     tag : 'button',
12814                     type : 'button',
12815                     name : 'cancel',
12816                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12817                     //html : 'Cancel'
12818                     html: btn_text_cancel
12819                 }
12820             ]
12821         };
12822         
12823         if(this.editable){
12824             buttons.cn.unshift({
12825                 tag: 'input',
12826                 cls: 'roo-select2-search-field-input'
12827             });
12828         }
12829         
12830         var _this = this;
12831         
12832         Roo.each(buttons.cn, function(c){
12833             if (_this.size) {
12834                 c.cls += ' btn-' + _this.size;
12835             }
12836
12837             if (_this.disabled) {
12838                 c.disabled = true;
12839             }
12840         });
12841         
12842         var box = {
12843             tag: 'div',
12844             cn: [
12845                 {
12846                     tag: 'input',
12847                     type : 'hidden',
12848                     cls: 'form-hidden-field'
12849                 },
12850                 {
12851                     tag: 'ul',
12852                     cls: 'roo-select2-choices',
12853                     cn:[
12854                         {
12855                             tag: 'li',
12856                             cls: 'roo-select2-search-field',
12857                             cn: [
12858                                 buttons
12859                             ]
12860                         }
12861                     ]
12862                 }
12863             ]
12864         };
12865         
12866         var combobox = {
12867             cls: 'roo-select2-container input-group roo-select2-container-multi',
12868             cn: [
12869                 box
12870 //                {
12871 //                    tag: 'ul',
12872 //                    cls: 'typeahead typeahead-long dropdown-menu',
12873 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12874 //                }
12875             ]
12876         };
12877         
12878         if(this.hasFeedback && !this.allowBlank){
12879             
12880             var feedback = {
12881                 tag: 'span',
12882                 cls: 'glyphicon form-control-feedback'
12883             };
12884
12885             combobox.cn.push(feedback);
12886         }
12887         
12888         
12889         if (align ==='left' && this.fieldLabel.length) {
12890             
12891             cfg.cls += ' roo-form-group-label-left';
12892             
12893             cfg.cn = [
12894                 {
12895                     tag : 'i',
12896                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12897                     tooltip : 'This field is required'
12898                 },
12899                 {
12900                     tag: 'label',
12901                     'for' :  id,
12902                     cls : 'control-label',
12903                     html : this.fieldLabel
12904
12905                 },
12906                 {
12907                     cls : "", 
12908                     cn: [
12909                         combobox
12910                     ]
12911                 }
12912
12913             ];
12914             
12915             var labelCfg = cfg.cn[1];
12916             var contentCfg = cfg.cn[2];
12917             
12918
12919             if(this.indicatorpos == 'right'){
12920                 
12921                 cfg.cn = [
12922                     {
12923                         tag: 'label',
12924                         'for' :  id,
12925                         cls : 'control-label',
12926                         cn : [
12927                             {
12928                                 tag : 'span',
12929                                 html : this.fieldLabel
12930                             },
12931                             {
12932                                 tag : 'i',
12933                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12934                                 tooltip : 'This field is required'
12935                             }
12936                         ]
12937                     },
12938                     {
12939                         cls : "",
12940                         cn: [
12941                             combobox
12942                         ]
12943                     }
12944
12945                 ];
12946                 
12947                 
12948                 
12949                 labelCfg = cfg.cn[0];
12950                 contentCfg = cfg.cn[1];
12951             
12952             }
12953             
12954             if(this.labelWidth > 12){
12955                 labelCfg.style = "width: " + this.labelWidth + 'px';
12956             }
12957             
12958             if(this.labelWidth < 13 && this.labelmd == 0){
12959                 this.labelmd = this.labelWidth;
12960             }
12961             
12962             if(this.labellg > 0){
12963                 labelCfg.cls += ' col-lg-' + this.labellg;
12964                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12965             }
12966             
12967             if(this.labelmd > 0){
12968                 labelCfg.cls += ' col-md-' + this.labelmd;
12969                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12970             }
12971             
12972             if(this.labelsm > 0){
12973                 labelCfg.cls += ' col-sm-' + this.labelsm;
12974                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12975             }
12976             
12977             if(this.labelxs > 0){
12978                 labelCfg.cls += ' col-xs-' + this.labelxs;
12979                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12980             }
12981                 
12982                 
12983         } else if ( this.fieldLabel.length) {
12984 //                Roo.log(" label");
12985                  cfg.cn = [
12986                     {
12987                         tag : 'i',
12988                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12989                         tooltip : 'This field is required'
12990                     },
12991                     {
12992                         tag: 'label',
12993                         //cls : 'input-group-addon',
12994                         html : this.fieldLabel
12995                     },
12996                     combobox
12997                 ];
12998                 
12999                 if(this.indicatorpos == 'right'){
13000                     cfg.cn = [
13001                         {
13002                             tag: 'label',
13003                             //cls : 'input-group-addon',
13004                             html : this.fieldLabel
13005                         },
13006                         {
13007                             tag : 'i',
13008                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13009                             tooltip : 'This field is required'
13010                         },
13011                         combobox
13012                     ];
13013                     
13014                 }
13015
13016         } else {
13017             
13018 //                Roo.log(" no label && no align");
13019                 cfg = combobox
13020                      
13021                 
13022         }
13023          
13024         var settings=this;
13025         ['xs','sm','md','lg'].map(function(size){
13026             if (settings[size]) {
13027                 cfg.cls += ' col-' + size + '-' + settings[size];
13028             }
13029         });
13030         
13031         return cfg;
13032         
13033     },
13034     
13035     _initEventsCalled : false,
13036     
13037     // private
13038     initEvents: function()
13039     {   
13040         if (this._initEventsCalled) { // as we call render... prevent looping...
13041             return;
13042         }
13043         this._initEventsCalled = true;
13044         
13045         if (!this.store) {
13046             throw "can not find store for combo";
13047         }
13048         
13049         this.indicator = this.indicatorEl();
13050         
13051         this.store = Roo.factory(this.store, Roo.data);
13052         this.store.parent = this;
13053         
13054         // if we are building from html. then this element is so complex, that we can not really
13055         // use the rendered HTML.
13056         // so we have to trash and replace the previous code.
13057         if (Roo.XComponent.build_from_html) {
13058             // remove this element....
13059             var e = this.el.dom, k=0;
13060             while (e ) { e = e.previousSibling;  ++k;}
13061
13062             this.el.remove();
13063             
13064             this.el=false;
13065             this.rendered = false;
13066             
13067             this.render(this.parent().getChildContainer(true), k);
13068         }
13069         
13070         if(Roo.isIOS && this.useNativeIOS){
13071             this.initIOSView();
13072             return;
13073         }
13074         
13075         /*
13076          * Touch Devices
13077          */
13078         
13079         if(Roo.isTouch && this.mobileTouchView){
13080             this.initTouchView();
13081             return;
13082         }
13083         
13084         if(this.tickable){
13085             this.initTickableEvents();
13086             return;
13087         }
13088         
13089         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13090         
13091         if(this.hiddenName){
13092             
13093             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13094             
13095             this.hiddenField.dom.value =
13096                 this.hiddenValue !== undefined ? this.hiddenValue :
13097                 this.value !== undefined ? this.value : '';
13098
13099             // prevent input submission
13100             this.el.dom.removeAttribute('name');
13101             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13102              
13103              
13104         }
13105         //if(Roo.isGecko){
13106         //    this.el.dom.setAttribute('autocomplete', 'off');
13107         //}
13108         
13109         var cls = 'x-combo-list';
13110         
13111         //this.list = new Roo.Layer({
13112         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13113         //});
13114         
13115         var _this = this;
13116         
13117         (function(){
13118             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13119             _this.list.setWidth(lw);
13120         }).defer(100);
13121         
13122         this.list.on('mouseover', this.onViewOver, this);
13123         this.list.on('mousemove', this.onViewMove, this);
13124         this.list.on('scroll', this.onViewScroll, this);
13125         
13126         /*
13127         this.list.swallowEvent('mousewheel');
13128         this.assetHeight = 0;
13129
13130         if(this.title){
13131             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13132             this.assetHeight += this.header.getHeight();
13133         }
13134
13135         this.innerList = this.list.createChild({cls:cls+'-inner'});
13136         this.innerList.on('mouseover', this.onViewOver, this);
13137         this.innerList.on('mousemove', this.onViewMove, this);
13138         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13139         
13140         if(this.allowBlank && !this.pageSize && !this.disableClear){
13141             this.footer = this.list.createChild({cls:cls+'-ft'});
13142             this.pageTb = new Roo.Toolbar(this.footer);
13143            
13144         }
13145         if(this.pageSize){
13146             this.footer = this.list.createChild({cls:cls+'-ft'});
13147             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13148                     {pageSize: this.pageSize});
13149             
13150         }
13151         
13152         if (this.pageTb && this.allowBlank && !this.disableClear) {
13153             var _this = this;
13154             this.pageTb.add(new Roo.Toolbar.Fill(), {
13155                 cls: 'x-btn-icon x-btn-clear',
13156                 text: '&#160;',
13157                 handler: function()
13158                 {
13159                     _this.collapse();
13160                     _this.clearValue();
13161                     _this.onSelect(false, -1);
13162                 }
13163             });
13164         }
13165         if (this.footer) {
13166             this.assetHeight += this.footer.getHeight();
13167         }
13168         */
13169             
13170         if(!this.tpl){
13171             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13172         }
13173
13174         this.view = new Roo.View(this.list, this.tpl, {
13175             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13176         });
13177         //this.view.wrapEl.setDisplayed(false);
13178         this.view.on('click', this.onViewClick, this);
13179         
13180         
13181         this.store.on('beforeload', this.onBeforeLoad, this);
13182         this.store.on('load', this.onLoad, this);
13183         this.store.on('loadexception', this.onLoadException, this);
13184         /*
13185         if(this.resizable){
13186             this.resizer = new Roo.Resizable(this.list,  {
13187                pinned:true, handles:'se'
13188             });
13189             this.resizer.on('resize', function(r, w, h){
13190                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13191                 this.listWidth = w;
13192                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13193                 this.restrictHeight();
13194             }, this);
13195             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13196         }
13197         */
13198         if(!this.editable){
13199             this.editable = true;
13200             this.setEditable(false);
13201         }
13202         
13203         /*
13204         
13205         if (typeof(this.events.add.listeners) != 'undefined') {
13206             
13207             this.addicon = this.wrap.createChild(
13208                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13209        
13210             this.addicon.on('click', function(e) {
13211                 this.fireEvent('add', this);
13212             }, this);
13213         }
13214         if (typeof(this.events.edit.listeners) != 'undefined') {
13215             
13216             this.editicon = this.wrap.createChild(
13217                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13218             if (this.addicon) {
13219                 this.editicon.setStyle('margin-left', '40px');
13220             }
13221             this.editicon.on('click', function(e) {
13222                 
13223                 // we fire even  if inothing is selected..
13224                 this.fireEvent('edit', this, this.lastData );
13225                 
13226             }, this);
13227         }
13228         */
13229         
13230         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13231             "up" : function(e){
13232                 this.inKeyMode = true;
13233                 this.selectPrev();
13234             },
13235
13236             "down" : function(e){
13237                 if(!this.isExpanded()){
13238                     this.onTriggerClick();
13239                 }else{
13240                     this.inKeyMode = true;
13241                     this.selectNext();
13242                 }
13243             },
13244
13245             "enter" : function(e){
13246 //                this.onViewClick();
13247                 //return true;
13248                 this.collapse();
13249                 
13250                 if(this.fireEvent("specialkey", this, e)){
13251                     this.onViewClick(false);
13252                 }
13253                 
13254                 return true;
13255             },
13256
13257             "esc" : function(e){
13258                 this.collapse();
13259             },
13260
13261             "tab" : function(e){
13262                 this.collapse();
13263                 
13264                 if(this.fireEvent("specialkey", this, e)){
13265                     this.onViewClick(false);
13266                 }
13267                 
13268                 return true;
13269             },
13270
13271             scope : this,
13272
13273             doRelay : function(foo, bar, hname){
13274                 if(hname == 'down' || this.scope.isExpanded()){
13275                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13276                 }
13277                 return true;
13278             },
13279
13280             forceKeyDown: true
13281         });
13282         
13283         
13284         this.queryDelay = Math.max(this.queryDelay || 10,
13285                 this.mode == 'local' ? 10 : 250);
13286         
13287         
13288         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13289         
13290         if(this.typeAhead){
13291             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13292         }
13293         if(this.editable !== false){
13294             this.inputEl().on("keyup", this.onKeyUp, this);
13295         }
13296         if(this.forceSelection){
13297             this.inputEl().on('blur', this.doForce, this);
13298         }
13299         
13300         if(this.multiple){
13301             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13302             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13303         }
13304     },
13305     
13306     initTickableEvents: function()
13307     {   
13308         this.createList();
13309         
13310         if(this.hiddenName){
13311             
13312             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13313             
13314             this.hiddenField.dom.value =
13315                 this.hiddenValue !== undefined ? this.hiddenValue :
13316                 this.value !== undefined ? this.value : '';
13317
13318             // prevent input submission
13319             this.el.dom.removeAttribute('name');
13320             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13321              
13322              
13323         }
13324         
13325 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13326         
13327         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13328         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13329         if(this.triggerList){
13330             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13331         }
13332          
13333         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13334         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13335         
13336         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13337         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13338         
13339         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13340         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13341         
13342         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13343         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13344         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13345         
13346         this.okBtn.hide();
13347         this.cancelBtn.hide();
13348         
13349         var _this = this;
13350         
13351         (function(){
13352             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13353             _this.list.setWidth(lw);
13354         }).defer(100);
13355         
13356         this.list.on('mouseover', this.onViewOver, this);
13357         this.list.on('mousemove', this.onViewMove, this);
13358         
13359         this.list.on('scroll', this.onViewScroll, this);
13360         
13361         if(!this.tpl){
13362             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>';
13363         }
13364
13365         this.view = new Roo.View(this.list, this.tpl, {
13366             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13367         });
13368         
13369         //this.view.wrapEl.setDisplayed(false);
13370         this.view.on('click', this.onViewClick, this);
13371         
13372         
13373         
13374         this.store.on('beforeload', this.onBeforeLoad, this);
13375         this.store.on('load', this.onLoad, this);
13376         this.store.on('loadexception', this.onLoadException, this);
13377         
13378         if(this.editable){
13379             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13380                 "up" : function(e){
13381                     this.inKeyMode = true;
13382                     this.selectPrev();
13383                 },
13384
13385                 "down" : function(e){
13386                     this.inKeyMode = true;
13387                     this.selectNext();
13388                 },
13389
13390                 "enter" : function(e){
13391                     if(this.fireEvent("specialkey", this, e)){
13392                         this.onViewClick(false);
13393                     }
13394                     
13395                     return true;
13396                 },
13397
13398                 "esc" : function(e){
13399                     this.onTickableFooterButtonClick(e, false, false);
13400                 },
13401
13402                 "tab" : function(e){
13403                     this.fireEvent("specialkey", this, e);
13404                     
13405                     this.onTickableFooterButtonClick(e, false, false);
13406                     
13407                     return true;
13408                 },
13409
13410                 scope : this,
13411
13412                 doRelay : function(e, fn, key){
13413                     if(this.scope.isExpanded()){
13414                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13415                     }
13416                     return true;
13417                 },
13418
13419                 forceKeyDown: true
13420             });
13421         }
13422         
13423         this.queryDelay = Math.max(this.queryDelay || 10,
13424                 this.mode == 'local' ? 10 : 250);
13425         
13426         
13427         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13428         
13429         if(this.typeAhead){
13430             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13431         }
13432         
13433         if(this.editable !== false){
13434             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13435         }
13436         
13437         this.indicator = this.indicatorEl();
13438         
13439         if(this.indicator){
13440             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13441             this.indicator.hide();
13442         }
13443         
13444     },
13445
13446     onDestroy : function(){
13447         if(this.view){
13448             this.view.setStore(null);
13449             this.view.el.removeAllListeners();
13450             this.view.el.remove();
13451             this.view.purgeListeners();
13452         }
13453         if(this.list){
13454             this.list.dom.innerHTML  = '';
13455         }
13456         
13457         if(this.store){
13458             this.store.un('beforeload', this.onBeforeLoad, this);
13459             this.store.un('load', this.onLoad, this);
13460             this.store.un('loadexception', this.onLoadException, this);
13461         }
13462         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13463     },
13464
13465     // private
13466     fireKey : function(e){
13467         if(e.isNavKeyPress() && !this.list.isVisible()){
13468             this.fireEvent("specialkey", this, e);
13469         }
13470     },
13471
13472     // private
13473     onResize: function(w, h){
13474 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13475 //        
13476 //        if(typeof w != 'number'){
13477 //            // we do not handle it!?!?
13478 //            return;
13479 //        }
13480 //        var tw = this.trigger.getWidth();
13481 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13482 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13483 //        var x = w - tw;
13484 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13485 //            
13486 //        //this.trigger.setStyle('left', x+'px');
13487 //        
13488 //        if(this.list && this.listWidth === undefined){
13489 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13490 //            this.list.setWidth(lw);
13491 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13492 //        }
13493         
13494     
13495         
13496     },
13497
13498     /**
13499      * Allow or prevent the user from directly editing the field text.  If false is passed,
13500      * the user will only be able to select from the items defined in the dropdown list.  This method
13501      * is the runtime equivalent of setting the 'editable' config option at config time.
13502      * @param {Boolean} value True to allow the user to directly edit the field text
13503      */
13504     setEditable : function(value){
13505         if(value == this.editable){
13506             return;
13507         }
13508         this.editable = value;
13509         if(!value){
13510             this.inputEl().dom.setAttribute('readOnly', true);
13511             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13512             this.inputEl().addClass('x-combo-noedit');
13513         }else{
13514             this.inputEl().dom.setAttribute('readOnly', false);
13515             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13516             this.inputEl().removeClass('x-combo-noedit');
13517         }
13518     },
13519
13520     // private
13521     
13522     onBeforeLoad : function(combo,opts){
13523         if(!this.hasFocus){
13524             return;
13525         }
13526          if (!opts.add) {
13527             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13528          }
13529         this.restrictHeight();
13530         this.selectedIndex = -1;
13531     },
13532
13533     // private
13534     onLoad : function(){
13535         
13536         this.hasQuery = false;
13537         
13538         if(!this.hasFocus){
13539             return;
13540         }
13541         
13542         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13543             this.loading.hide();
13544         }
13545         
13546         if(this.store.getCount() > 0){
13547             
13548             this.expand();
13549             this.restrictHeight();
13550             if(this.lastQuery == this.allQuery){
13551                 if(this.editable && !this.tickable){
13552                     this.inputEl().dom.select();
13553                 }
13554                 
13555                 if(
13556                     !this.selectByValue(this.value, true) &&
13557                     this.autoFocus && 
13558                     (
13559                         !this.store.lastOptions ||
13560                         typeof(this.store.lastOptions.add) == 'undefined' || 
13561                         this.store.lastOptions.add != true
13562                     )
13563                 ){
13564                     this.select(0, true);
13565                 }
13566             }else{
13567                 if(this.autoFocus){
13568                     this.selectNext();
13569                 }
13570                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13571                     this.taTask.delay(this.typeAheadDelay);
13572                 }
13573             }
13574         }else{
13575             this.onEmptyResults();
13576         }
13577         
13578         //this.el.focus();
13579     },
13580     // private
13581     onLoadException : function()
13582     {
13583         this.hasQuery = false;
13584         
13585         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13586             this.loading.hide();
13587         }
13588         
13589         if(this.tickable && this.editable){
13590             return;
13591         }
13592         
13593         this.collapse();
13594         // only causes errors at present
13595         //Roo.log(this.store.reader.jsonData);
13596         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13597             // fixme
13598             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13599         //}
13600         
13601         
13602     },
13603     // private
13604     onTypeAhead : function(){
13605         if(this.store.getCount() > 0){
13606             var r = this.store.getAt(0);
13607             var newValue = r.data[this.displayField];
13608             var len = newValue.length;
13609             var selStart = this.getRawValue().length;
13610             
13611             if(selStart != len){
13612                 this.setRawValue(newValue);
13613                 this.selectText(selStart, newValue.length);
13614             }
13615         }
13616     },
13617
13618     // private
13619     onSelect : function(record, index){
13620         
13621         if(this.fireEvent('beforeselect', this, record, index) !== false){
13622         
13623             this.setFromData(index > -1 ? record.data : false);
13624             
13625             this.collapse();
13626             this.fireEvent('select', this, record, index);
13627         }
13628     },
13629
13630     /**
13631      * Returns the currently selected field value or empty string if no value is set.
13632      * @return {String} value The selected value
13633      */
13634     getValue : function()
13635     {
13636         if(Roo.isIOS && this.useNativeIOS){
13637             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13638         }
13639         
13640         if(this.multiple){
13641             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13642         }
13643         
13644         if(this.valueField){
13645             return typeof this.value != 'undefined' ? this.value : '';
13646         }else{
13647             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13648         }
13649     },
13650     
13651     getRawValue : function()
13652     {
13653         if(Roo.isIOS && this.useNativeIOS){
13654             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13655         }
13656         
13657         var v = this.inputEl().getValue();
13658         
13659         return v;
13660     },
13661
13662     /**
13663      * Clears any text/value currently set in the field
13664      */
13665     clearValue : function(){
13666         
13667         if(this.hiddenField){
13668             this.hiddenField.dom.value = '';
13669         }
13670         this.value = '';
13671         this.setRawValue('');
13672         this.lastSelectionText = '';
13673         this.lastData = false;
13674         
13675         var close = this.closeTriggerEl();
13676         
13677         if(close){
13678             close.hide();
13679         }
13680         
13681         this.validate();
13682         
13683     },
13684
13685     /**
13686      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13687      * will be displayed in the field.  If the value does not match the data value of an existing item,
13688      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13689      * Otherwise the field will be blank (although the value will still be set).
13690      * @param {String} value The value to match
13691      */
13692     setValue : function(v)
13693     {
13694         if(Roo.isIOS && this.useNativeIOS){
13695             this.setIOSValue(v);
13696             return;
13697         }
13698         
13699         if(this.multiple){
13700             this.syncValue();
13701             return;
13702         }
13703         
13704         var text = v;
13705         if(this.valueField){
13706             var r = this.findRecord(this.valueField, v);
13707             if(r){
13708                 text = r.data[this.displayField];
13709             }else if(this.valueNotFoundText !== undefined){
13710                 text = this.valueNotFoundText;
13711             }
13712         }
13713         this.lastSelectionText = text;
13714         if(this.hiddenField){
13715             this.hiddenField.dom.value = v;
13716         }
13717         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13718         this.value = v;
13719         
13720         var close = this.closeTriggerEl();
13721         
13722         if(close){
13723             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13724         }
13725         
13726         this.validate();
13727     },
13728     /**
13729      * @property {Object} the last set data for the element
13730      */
13731     
13732     lastData : false,
13733     /**
13734      * Sets the value of the field based on a object which is related to the record format for the store.
13735      * @param {Object} value the value to set as. or false on reset?
13736      */
13737     setFromData : function(o){
13738         
13739         if(this.multiple){
13740             this.addItem(o);
13741             return;
13742         }
13743             
13744         var dv = ''; // display value
13745         var vv = ''; // value value..
13746         this.lastData = o;
13747         if (this.displayField) {
13748             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13749         } else {
13750             // this is an error condition!!!
13751             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13752         }
13753         
13754         if(this.valueField){
13755             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13756         }
13757         
13758         var close = this.closeTriggerEl();
13759         
13760         if(close){
13761             if(dv.length || vv * 1 > 0){
13762                 close.show() ;
13763                 this.blockFocus=true;
13764             } else {
13765                 close.hide();
13766             }             
13767         }
13768         
13769         if(this.hiddenField){
13770             this.hiddenField.dom.value = vv;
13771             
13772             this.lastSelectionText = dv;
13773             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13774             this.value = vv;
13775             return;
13776         }
13777         // no hidden field.. - we store the value in 'value', but still display
13778         // display field!!!!
13779         this.lastSelectionText = dv;
13780         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13781         this.value = vv;
13782         
13783         
13784         
13785     },
13786     // private
13787     reset : function(){
13788         // overridden so that last data is reset..
13789         
13790         if(this.multiple){
13791             this.clearItem();
13792             return;
13793         }
13794         
13795         this.setValue(this.originalValue);
13796         //this.clearInvalid();
13797         this.lastData = false;
13798         if (this.view) {
13799             this.view.clearSelections();
13800         }
13801         
13802         this.validate();
13803     },
13804     // private
13805     findRecord : function(prop, value){
13806         var record;
13807         if(this.store.getCount() > 0){
13808             this.store.each(function(r){
13809                 if(r.data[prop] == value){
13810                     record = r;
13811                     return false;
13812                 }
13813                 return true;
13814             });
13815         }
13816         return record;
13817     },
13818     
13819     getName: function()
13820     {
13821         // returns hidden if it's set..
13822         if (!this.rendered) {return ''};
13823         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13824         
13825     },
13826     // private
13827     onViewMove : function(e, t){
13828         this.inKeyMode = false;
13829     },
13830
13831     // private
13832     onViewOver : function(e, t){
13833         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13834             return;
13835         }
13836         var item = this.view.findItemFromChild(t);
13837         
13838         if(item){
13839             var index = this.view.indexOf(item);
13840             this.select(index, false);
13841         }
13842     },
13843
13844     // private
13845     onViewClick : function(view, doFocus, el, e)
13846     {
13847         var index = this.view.getSelectedIndexes()[0];
13848         
13849         var r = this.store.getAt(index);
13850         
13851         if(this.tickable){
13852             
13853             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13854                 return;
13855             }
13856             
13857             var rm = false;
13858             var _this = this;
13859             
13860             Roo.each(this.tickItems, function(v,k){
13861                 
13862                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13863                     Roo.log(v);
13864                     _this.tickItems.splice(k, 1);
13865                     
13866                     if(typeof(e) == 'undefined' && view == false){
13867                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13868                     }
13869                     
13870                     rm = true;
13871                     return;
13872                 }
13873             });
13874             
13875             if(rm){
13876                 return;
13877             }
13878             
13879             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13880                 this.tickItems.push(r.data);
13881             }
13882             
13883             if(typeof(e) == 'undefined' && view == false){
13884                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13885             }
13886                     
13887             return;
13888         }
13889         
13890         if(r){
13891             this.onSelect(r, index);
13892         }
13893         if(doFocus !== false && !this.blockFocus){
13894             this.inputEl().focus();
13895         }
13896     },
13897
13898     // private
13899     restrictHeight : function(){
13900         //this.innerList.dom.style.height = '';
13901         //var inner = this.innerList.dom;
13902         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13903         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13904         //this.list.beginUpdate();
13905         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13906         this.list.alignTo(this.inputEl(), this.listAlign);
13907         this.list.alignTo(this.inputEl(), this.listAlign);
13908         //this.list.endUpdate();
13909     },
13910
13911     // private
13912     onEmptyResults : function(){
13913         
13914         if(this.tickable && this.editable){
13915             this.restrictHeight();
13916             return;
13917         }
13918         
13919         this.collapse();
13920     },
13921
13922     /**
13923      * Returns true if the dropdown list is expanded, else false.
13924      */
13925     isExpanded : function(){
13926         return this.list.isVisible();
13927     },
13928
13929     /**
13930      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13931      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13932      * @param {String} value The data value of the item to select
13933      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13934      * selected item if it is not currently in view (defaults to true)
13935      * @return {Boolean} True if the value matched an item in the list, else false
13936      */
13937     selectByValue : function(v, scrollIntoView){
13938         if(v !== undefined && v !== null){
13939             var r = this.findRecord(this.valueField || this.displayField, v);
13940             if(r){
13941                 this.select(this.store.indexOf(r), scrollIntoView);
13942                 return true;
13943             }
13944         }
13945         return false;
13946     },
13947
13948     /**
13949      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13950      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13951      * @param {Number} index The zero-based index of the list item to select
13952      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13953      * selected item if it is not currently in view (defaults to true)
13954      */
13955     select : function(index, scrollIntoView){
13956         this.selectedIndex = index;
13957         this.view.select(index);
13958         if(scrollIntoView !== false){
13959             var el = this.view.getNode(index);
13960             /*
13961              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13962              */
13963             if(el){
13964                 this.list.scrollChildIntoView(el, false);
13965             }
13966         }
13967     },
13968
13969     // private
13970     selectNext : function(){
13971         var ct = this.store.getCount();
13972         if(ct > 0){
13973             if(this.selectedIndex == -1){
13974                 this.select(0);
13975             }else if(this.selectedIndex < ct-1){
13976                 this.select(this.selectedIndex+1);
13977             }
13978         }
13979     },
13980
13981     // private
13982     selectPrev : function(){
13983         var ct = this.store.getCount();
13984         if(ct > 0){
13985             if(this.selectedIndex == -1){
13986                 this.select(0);
13987             }else if(this.selectedIndex != 0){
13988                 this.select(this.selectedIndex-1);
13989             }
13990         }
13991     },
13992
13993     // private
13994     onKeyUp : function(e){
13995         if(this.editable !== false && !e.isSpecialKey()){
13996             this.lastKey = e.getKey();
13997             this.dqTask.delay(this.queryDelay);
13998         }
13999     },
14000
14001     // private
14002     validateBlur : function(){
14003         return !this.list || !this.list.isVisible();   
14004     },
14005
14006     // private
14007     initQuery : function(){
14008         
14009         var v = this.getRawValue();
14010         
14011         if(this.tickable && this.editable){
14012             v = this.tickableInputEl().getValue();
14013         }
14014         
14015         this.doQuery(v);
14016     },
14017
14018     // private
14019     doForce : function(){
14020         if(this.inputEl().dom.value.length > 0){
14021             this.inputEl().dom.value =
14022                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14023              
14024         }
14025     },
14026
14027     /**
14028      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14029      * query allowing the query action to be canceled if needed.
14030      * @param {String} query The SQL query to execute
14031      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14032      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14033      * saved in the current store (defaults to false)
14034      */
14035     doQuery : function(q, forceAll){
14036         
14037         if(q === undefined || q === null){
14038             q = '';
14039         }
14040         var qe = {
14041             query: q,
14042             forceAll: forceAll,
14043             combo: this,
14044             cancel:false
14045         };
14046         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14047             return false;
14048         }
14049         q = qe.query;
14050         
14051         forceAll = qe.forceAll;
14052         if(forceAll === true || (q.length >= this.minChars)){
14053             
14054             this.hasQuery = true;
14055             
14056             if(this.lastQuery != q || this.alwaysQuery){
14057                 this.lastQuery = q;
14058                 if(this.mode == 'local'){
14059                     this.selectedIndex = -1;
14060                     if(forceAll){
14061                         this.store.clearFilter();
14062                     }else{
14063                         
14064                         if(this.specialFilter){
14065                             this.fireEvent('specialfilter', this);
14066                             this.onLoad();
14067                             return;
14068                         }
14069                         
14070                         this.store.filter(this.displayField, q);
14071                     }
14072                     
14073                     this.store.fireEvent("datachanged", this.store);
14074                     
14075                     this.onLoad();
14076                     
14077                     
14078                 }else{
14079                     
14080                     this.store.baseParams[this.queryParam] = q;
14081                     
14082                     var options = {params : this.getParams(q)};
14083                     
14084                     if(this.loadNext){
14085                         options.add = true;
14086                         options.params.start = this.page * this.pageSize;
14087                     }
14088                     
14089                     this.store.load(options);
14090                     
14091                     /*
14092                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14093                      *  we should expand the list on onLoad
14094                      *  so command out it
14095                      */
14096 //                    this.expand();
14097                 }
14098             }else{
14099                 this.selectedIndex = -1;
14100                 this.onLoad();   
14101             }
14102         }
14103         
14104         this.loadNext = false;
14105     },
14106     
14107     // private
14108     getParams : function(q){
14109         var p = {};
14110         //p[this.queryParam] = q;
14111         
14112         if(this.pageSize){
14113             p.start = 0;
14114             p.limit = this.pageSize;
14115         }
14116         return p;
14117     },
14118
14119     /**
14120      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14121      */
14122     collapse : function(){
14123         if(!this.isExpanded()){
14124             return;
14125         }
14126         
14127         this.list.hide();
14128         
14129         this.hasFocus = false;
14130         
14131         if(this.tickable){
14132             this.okBtn.hide();
14133             this.cancelBtn.hide();
14134             this.trigger.show();
14135             
14136             if(this.editable){
14137                 this.tickableInputEl().dom.value = '';
14138                 this.tickableInputEl().blur();
14139             }
14140             
14141         }
14142         
14143         Roo.get(document).un('mousedown', this.collapseIf, this);
14144         Roo.get(document).un('mousewheel', this.collapseIf, this);
14145         if (!this.editable) {
14146             Roo.get(document).un('keydown', this.listKeyPress, this);
14147         }
14148         this.fireEvent('collapse', this);
14149         
14150         this.validate();
14151     },
14152
14153     // private
14154     collapseIf : function(e){
14155         var in_combo  = e.within(this.el);
14156         var in_list =  e.within(this.list);
14157         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14158         
14159         if (in_combo || in_list || is_list) {
14160             //e.stopPropagation();
14161             return;
14162         }
14163         
14164         if(this.tickable){
14165             this.onTickableFooterButtonClick(e, false, false);
14166         }
14167
14168         this.collapse();
14169         
14170     },
14171
14172     /**
14173      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14174      */
14175     expand : function(){
14176        
14177         if(this.isExpanded() || !this.hasFocus){
14178             return;
14179         }
14180         
14181         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14182         this.list.setWidth(lw);
14183         
14184         Roo.log('expand');
14185         
14186         this.list.show();
14187         
14188         this.restrictHeight();
14189         
14190         if(this.tickable){
14191             
14192             this.tickItems = Roo.apply([], this.item);
14193             
14194             this.okBtn.show();
14195             this.cancelBtn.show();
14196             this.trigger.hide();
14197             
14198             if(this.editable){
14199                 this.tickableInputEl().focus();
14200             }
14201             
14202         }
14203         
14204         Roo.get(document).on('mousedown', this.collapseIf, this);
14205         Roo.get(document).on('mousewheel', this.collapseIf, this);
14206         if (!this.editable) {
14207             Roo.get(document).on('keydown', this.listKeyPress, this);
14208         }
14209         
14210         this.fireEvent('expand', this);
14211     },
14212
14213     // private
14214     // Implements the default empty TriggerField.onTriggerClick function
14215     onTriggerClick : function(e)
14216     {
14217         Roo.log('trigger click');
14218         
14219         if(this.disabled || !this.triggerList){
14220             return;
14221         }
14222         
14223         this.page = 0;
14224         this.loadNext = false;
14225         
14226         if(this.isExpanded()){
14227             this.collapse();
14228             if (!this.blockFocus) {
14229                 this.inputEl().focus();
14230             }
14231             
14232         }else {
14233             this.hasFocus = true;
14234             if(this.triggerAction == 'all') {
14235                 this.doQuery(this.allQuery, true);
14236             } else {
14237                 this.doQuery(this.getRawValue());
14238             }
14239             if (!this.blockFocus) {
14240                 this.inputEl().focus();
14241             }
14242         }
14243     },
14244     
14245     onTickableTriggerClick : function(e)
14246     {
14247         if(this.disabled){
14248             return;
14249         }
14250         
14251         this.page = 0;
14252         this.loadNext = false;
14253         this.hasFocus = true;
14254         
14255         if(this.triggerAction == 'all') {
14256             this.doQuery(this.allQuery, true);
14257         } else {
14258             this.doQuery(this.getRawValue());
14259         }
14260     },
14261     
14262     onSearchFieldClick : function(e)
14263     {
14264         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14265             this.onTickableFooterButtonClick(e, false, false);
14266             return;
14267         }
14268         
14269         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14270             return;
14271         }
14272         
14273         this.page = 0;
14274         this.loadNext = false;
14275         this.hasFocus = true;
14276         
14277         if(this.triggerAction == 'all') {
14278             this.doQuery(this.allQuery, true);
14279         } else {
14280             this.doQuery(this.getRawValue());
14281         }
14282     },
14283     
14284     listKeyPress : function(e)
14285     {
14286         //Roo.log('listkeypress');
14287         // scroll to first matching element based on key pres..
14288         if (e.isSpecialKey()) {
14289             return false;
14290         }
14291         var k = String.fromCharCode(e.getKey()).toUpperCase();
14292         //Roo.log(k);
14293         var match  = false;
14294         var csel = this.view.getSelectedNodes();
14295         var cselitem = false;
14296         if (csel.length) {
14297             var ix = this.view.indexOf(csel[0]);
14298             cselitem  = this.store.getAt(ix);
14299             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14300                 cselitem = false;
14301             }
14302             
14303         }
14304         
14305         this.store.each(function(v) { 
14306             if (cselitem) {
14307                 // start at existing selection.
14308                 if (cselitem.id == v.id) {
14309                     cselitem = false;
14310                 }
14311                 return true;
14312             }
14313                 
14314             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14315                 match = this.store.indexOf(v);
14316                 return false;
14317             }
14318             return true;
14319         }, this);
14320         
14321         if (match === false) {
14322             return true; // no more action?
14323         }
14324         // scroll to?
14325         this.view.select(match);
14326         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14327         sn.scrollIntoView(sn.dom.parentNode, false);
14328     },
14329     
14330     onViewScroll : function(e, t){
14331         
14332         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){
14333             return;
14334         }
14335         
14336         this.hasQuery = true;
14337         
14338         this.loading = this.list.select('.loading', true).first();
14339         
14340         if(this.loading === null){
14341             this.list.createChild({
14342                 tag: 'div',
14343                 cls: 'loading roo-select2-more-results roo-select2-active',
14344                 html: 'Loading more results...'
14345             });
14346             
14347             this.loading = this.list.select('.loading', true).first();
14348             
14349             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14350             
14351             this.loading.hide();
14352         }
14353         
14354         this.loading.show();
14355         
14356         var _combo = this;
14357         
14358         this.page++;
14359         this.loadNext = true;
14360         
14361         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14362         
14363         return;
14364     },
14365     
14366     addItem : function(o)
14367     {   
14368         var dv = ''; // display value
14369         
14370         if (this.displayField) {
14371             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14372         } else {
14373             // this is an error condition!!!
14374             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14375         }
14376         
14377         if(!dv.length){
14378             return;
14379         }
14380         
14381         var choice = this.choices.createChild({
14382             tag: 'li',
14383             cls: 'roo-select2-search-choice',
14384             cn: [
14385                 {
14386                     tag: 'div',
14387                     html: dv
14388                 },
14389                 {
14390                     tag: 'a',
14391                     href: '#',
14392                     cls: 'roo-select2-search-choice-close fa fa-times',
14393                     tabindex: '-1'
14394                 }
14395             ]
14396             
14397         }, this.searchField);
14398         
14399         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14400         
14401         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14402         
14403         this.item.push(o);
14404         
14405         this.lastData = o;
14406         
14407         this.syncValue();
14408         
14409         this.inputEl().dom.value = '';
14410         
14411         this.validate();
14412     },
14413     
14414     onRemoveItem : function(e, _self, o)
14415     {
14416         e.preventDefault();
14417         
14418         this.lastItem = Roo.apply([], this.item);
14419         
14420         var index = this.item.indexOf(o.data) * 1;
14421         
14422         if( index < 0){
14423             Roo.log('not this item?!');
14424             return;
14425         }
14426         
14427         this.item.splice(index, 1);
14428         o.item.remove();
14429         
14430         this.syncValue();
14431         
14432         this.fireEvent('remove', this, e);
14433         
14434         this.validate();
14435         
14436     },
14437     
14438     syncValue : function()
14439     {
14440         if(!this.item.length){
14441             this.clearValue();
14442             return;
14443         }
14444             
14445         var value = [];
14446         var _this = this;
14447         Roo.each(this.item, function(i){
14448             if(_this.valueField){
14449                 value.push(i[_this.valueField]);
14450                 return;
14451             }
14452
14453             value.push(i);
14454         });
14455
14456         this.value = value.join(',');
14457
14458         if(this.hiddenField){
14459             this.hiddenField.dom.value = this.value;
14460         }
14461         
14462         this.store.fireEvent("datachanged", this.store);
14463         
14464         this.validate();
14465     },
14466     
14467     clearItem : function()
14468     {
14469         if(!this.multiple){
14470             return;
14471         }
14472         
14473         this.item = [];
14474         
14475         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14476            c.remove();
14477         });
14478         
14479         this.syncValue();
14480         
14481         this.validate();
14482         
14483         if(this.tickable && !Roo.isTouch){
14484             this.view.refresh();
14485         }
14486     },
14487     
14488     inputEl: function ()
14489     {
14490         if(Roo.isIOS && this.useNativeIOS){
14491             return this.el.select('select.roo-ios-select', true).first();
14492         }
14493         
14494         if(Roo.isTouch && this.mobileTouchView){
14495             return this.el.select('input.form-control',true).first();
14496         }
14497         
14498         if(this.tickable){
14499             return this.searchField;
14500         }
14501         
14502         return this.el.select('input.form-control',true).first();
14503     },
14504     
14505     onTickableFooterButtonClick : function(e, btn, el)
14506     {
14507         e.preventDefault();
14508         
14509         this.lastItem = Roo.apply([], this.item);
14510         
14511         if(btn && btn.name == 'cancel'){
14512             this.tickItems = Roo.apply([], this.item);
14513             this.collapse();
14514             return;
14515         }
14516         
14517         this.clearItem();
14518         
14519         var _this = this;
14520         
14521         Roo.each(this.tickItems, function(o){
14522             _this.addItem(o);
14523         });
14524         
14525         this.collapse();
14526         
14527     },
14528     
14529     validate : function()
14530     {
14531         var v = this.getRawValue();
14532         
14533         if(this.multiple){
14534             v = this.getValue();
14535         }
14536         
14537         if(this.disabled || this.allowBlank || v.length){
14538             this.markValid();
14539             return true;
14540         }
14541         
14542         this.markInvalid();
14543         return false;
14544     },
14545     
14546     tickableInputEl : function()
14547     {
14548         if(!this.tickable || !this.editable){
14549             return this.inputEl();
14550         }
14551         
14552         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14553     },
14554     
14555     
14556     getAutoCreateTouchView : function()
14557     {
14558         var id = Roo.id();
14559         
14560         var cfg = {
14561             cls: 'form-group' //input-group
14562         };
14563         
14564         var input =  {
14565             tag: 'input',
14566             id : id,
14567             type : this.inputType,
14568             cls : 'form-control x-combo-noedit',
14569             autocomplete: 'new-password',
14570             placeholder : this.placeholder || '',
14571             readonly : true
14572         };
14573         
14574         if (this.name) {
14575             input.name = this.name;
14576         }
14577         
14578         if (this.size) {
14579             input.cls += ' input-' + this.size;
14580         }
14581         
14582         if (this.disabled) {
14583             input.disabled = true;
14584         }
14585         
14586         var inputblock = {
14587             cls : '',
14588             cn : [
14589                 input
14590             ]
14591         };
14592         
14593         if(this.before){
14594             inputblock.cls += ' input-group';
14595             
14596             inputblock.cn.unshift({
14597                 tag :'span',
14598                 cls : 'input-group-addon',
14599                 html : this.before
14600             });
14601         }
14602         
14603         if(this.removable && !this.multiple){
14604             inputblock.cls += ' roo-removable';
14605             
14606             inputblock.cn.push({
14607                 tag: 'button',
14608                 html : 'x',
14609                 cls : 'roo-combo-removable-btn close'
14610             });
14611         }
14612
14613         if(this.hasFeedback && !this.allowBlank){
14614             
14615             inputblock.cls += ' has-feedback';
14616             
14617             inputblock.cn.push({
14618                 tag: 'span',
14619                 cls: 'glyphicon form-control-feedback'
14620             });
14621             
14622         }
14623         
14624         if (this.after) {
14625             
14626             inputblock.cls += (this.before) ? '' : ' input-group';
14627             
14628             inputblock.cn.push({
14629                 tag :'span',
14630                 cls : 'input-group-addon',
14631                 html : this.after
14632             });
14633         }
14634
14635         var box = {
14636             tag: 'div',
14637             cn: [
14638                 {
14639                     tag: 'input',
14640                     type : 'hidden',
14641                     cls: 'form-hidden-field'
14642                 },
14643                 inputblock
14644             ]
14645             
14646         };
14647         
14648         if(this.multiple){
14649             box = {
14650                 tag: 'div',
14651                 cn: [
14652                     {
14653                         tag: 'input',
14654                         type : 'hidden',
14655                         cls: 'form-hidden-field'
14656                     },
14657                     {
14658                         tag: 'ul',
14659                         cls: 'roo-select2-choices',
14660                         cn:[
14661                             {
14662                                 tag: 'li',
14663                                 cls: 'roo-select2-search-field',
14664                                 cn: [
14665
14666                                     inputblock
14667                                 ]
14668                             }
14669                         ]
14670                     }
14671                 ]
14672             }
14673         };
14674         
14675         var combobox = {
14676             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14677             cn: [
14678                 box
14679             ]
14680         };
14681         
14682         if(!this.multiple && this.showToggleBtn){
14683             
14684             var caret = {
14685                         tag: 'span',
14686                         cls: 'caret'
14687             };
14688             
14689             if (this.caret != false) {
14690                 caret = {
14691                      tag: 'i',
14692                      cls: 'fa fa-' + this.caret
14693                 };
14694                 
14695             }
14696             
14697             combobox.cn.push({
14698                 tag :'span',
14699                 cls : 'input-group-addon btn dropdown-toggle',
14700                 cn : [
14701                     caret,
14702                     {
14703                         tag: 'span',
14704                         cls: 'combobox-clear',
14705                         cn  : [
14706                             {
14707                                 tag : 'i',
14708                                 cls: 'icon-remove'
14709                             }
14710                         ]
14711                     }
14712                 ]
14713
14714             })
14715         }
14716         
14717         if(this.multiple){
14718             combobox.cls += ' roo-select2-container-multi';
14719         }
14720         
14721         var align = this.labelAlign || this.parentLabelAlign();
14722         
14723         if (align ==='left' && this.fieldLabel.length) {
14724
14725             cfg.cn = [
14726                 {
14727                    tag : 'i',
14728                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14729                    tooltip : 'This field is required'
14730                 },
14731                 {
14732                     tag: 'label',
14733                     cls : 'control-label',
14734                     html : this.fieldLabel
14735
14736                 },
14737                 {
14738                     cls : '', 
14739                     cn: [
14740                         combobox
14741                     ]
14742                 }
14743             ];
14744             
14745             var labelCfg = cfg.cn[1];
14746             var contentCfg = cfg.cn[2];
14747             
14748
14749             if(this.indicatorpos == 'right'){
14750                 cfg.cn = [
14751                     {
14752                         tag: 'label',
14753                         'for' :  id,
14754                         cls : 'control-label',
14755                         cn : [
14756                             {
14757                                 tag : 'span',
14758                                 html : this.fieldLabel
14759                             },
14760                             {
14761                                 tag : 'i',
14762                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14763                                 tooltip : 'This field is required'
14764                             }
14765                         ]
14766                     },
14767                     {
14768                         cls : "",
14769                         cn: [
14770                             combobox
14771                         ]
14772                     }
14773
14774                 ];
14775                 
14776                 labelCfg = cfg.cn[0];
14777                 contentCfg = cfg.cn[1];
14778             }
14779             
14780            
14781             
14782             if(this.labelWidth > 12){
14783                 labelCfg.style = "width: " + this.labelWidth + 'px';
14784             }
14785             
14786             if(this.labelWidth < 13 && this.labelmd == 0){
14787                 this.labelmd = this.labelWidth;
14788             }
14789             
14790             if(this.labellg > 0){
14791                 labelCfg.cls += ' col-lg-' + this.labellg;
14792                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14793             }
14794             
14795             if(this.labelmd > 0){
14796                 labelCfg.cls += ' col-md-' + this.labelmd;
14797                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14798             }
14799             
14800             if(this.labelsm > 0){
14801                 labelCfg.cls += ' col-sm-' + this.labelsm;
14802                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14803             }
14804             
14805             if(this.labelxs > 0){
14806                 labelCfg.cls += ' col-xs-' + this.labelxs;
14807                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14808             }
14809                 
14810                 
14811         } else if ( this.fieldLabel.length) {
14812             cfg.cn = [
14813                 {
14814                    tag : 'i',
14815                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14816                    tooltip : 'This field is required'
14817                 },
14818                 {
14819                     tag: 'label',
14820                     cls : 'control-label',
14821                     html : this.fieldLabel
14822
14823                 },
14824                 {
14825                     cls : '', 
14826                     cn: [
14827                         combobox
14828                     ]
14829                 }
14830             ];
14831             
14832             if(this.indicatorpos == 'right'){
14833                 cfg.cn = [
14834                     {
14835                         tag: 'label',
14836                         cls : 'control-label',
14837                         html : this.fieldLabel,
14838                         cn : [
14839                             {
14840                                tag : 'i',
14841                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14842                                tooltip : 'This field is required'
14843                             }
14844                         ]
14845                     },
14846                     {
14847                         cls : '', 
14848                         cn: [
14849                             combobox
14850                         ]
14851                     }
14852                 ];
14853             }
14854         } else {
14855             cfg.cn = combobox;    
14856         }
14857         
14858         
14859         var settings = this;
14860         
14861         ['xs','sm','md','lg'].map(function(size){
14862             if (settings[size]) {
14863                 cfg.cls += ' col-' + size + '-' + settings[size];
14864             }
14865         });
14866         
14867         return cfg;
14868     },
14869     
14870     initTouchView : function()
14871     {
14872         this.renderTouchView();
14873         
14874         this.touchViewEl.on('scroll', function(){
14875             this.el.dom.scrollTop = 0;
14876         }, this);
14877         
14878         this.originalValue = this.getValue();
14879         
14880         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14881         
14882         this.inputEl().on("click", this.showTouchView, this);
14883         if (this.triggerEl) {
14884             this.triggerEl.on("click", this.showTouchView, this);
14885         }
14886         
14887         
14888         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14889         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14890         
14891         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14892         
14893         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14894         this.store.on('load', this.onTouchViewLoad, this);
14895         this.store.on('loadexception', this.onTouchViewLoadException, this);
14896         
14897         if(this.hiddenName){
14898             
14899             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14900             
14901             this.hiddenField.dom.value =
14902                 this.hiddenValue !== undefined ? this.hiddenValue :
14903                 this.value !== undefined ? this.value : '';
14904         
14905             this.el.dom.removeAttribute('name');
14906             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14907         }
14908         
14909         if(this.multiple){
14910             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14911             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14912         }
14913         
14914         if(this.removable && !this.multiple){
14915             var close = this.closeTriggerEl();
14916             if(close){
14917                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14918                 close.on('click', this.removeBtnClick, this, close);
14919             }
14920         }
14921         /*
14922          * fix the bug in Safari iOS8
14923          */
14924         this.inputEl().on("focus", function(e){
14925             document.activeElement.blur();
14926         }, this);
14927         
14928         return;
14929         
14930         
14931     },
14932     
14933     renderTouchView : function()
14934     {
14935         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14936         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14937         
14938         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14939         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14940         
14941         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14942         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14943         this.touchViewBodyEl.setStyle('overflow', 'auto');
14944         
14945         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14946         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14947         
14948         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14949         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14950         
14951     },
14952     
14953     showTouchView : function()
14954     {
14955         if(this.disabled){
14956             return;
14957         }
14958         
14959         this.touchViewHeaderEl.hide();
14960
14961         if(this.modalTitle.length){
14962             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14963             this.touchViewHeaderEl.show();
14964         }
14965
14966         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14967         this.touchViewEl.show();
14968
14969         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14970         
14971         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14972         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14973
14974         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14975
14976         if(this.modalTitle.length){
14977             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14978         }
14979         
14980         this.touchViewBodyEl.setHeight(bodyHeight);
14981
14982         if(this.animate){
14983             var _this = this;
14984             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14985         }else{
14986             this.touchViewEl.addClass('in');
14987         }
14988
14989         this.doTouchViewQuery();
14990         
14991     },
14992     
14993     hideTouchView : function()
14994     {
14995         this.touchViewEl.removeClass('in');
14996
14997         if(this.animate){
14998             var _this = this;
14999             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15000         }else{
15001             this.touchViewEl.setStyle('display', 'none');
15002         }
15003         
15004     },
15005     
15006     setTouchViewValue : function()
15007     {
15008         if(this.multiple){
15009             this.clearItem();
15010         
15011             var _this = this;
15012
15013             Roo.each(this.tickItems, function(o){
15014                 this.addItem(o);
15015             }, this);
15016         }
15017         
15018         this.hideTouchView();
15019     },
15020     
15021     doTouchViewQuery : function()
15022     {
15023         var qe = {
15024             query: '',
15025             forceAll: true,
15026             combo: this,
15027             cancel:false
15028         };
15029         
15030         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15031             return false;
15032         }
15033         
15034         if(!this.alwaysQuery || this.mode == 'local'){
15035             this.onTouchViewLoad();
15036             return;
15037         }
15038         
15039         this.store.load();
15040     },
15041     
15042     onTouchViewBeforeLoad : function(combo,opts)
15043     {
15044         return;
15045     },
15046
15047     // private
15048     onTouchViewLoad : function()
15049     {
15050         if(this.store.getCount() < 1){
15051             this.onTouchViewEmptyResults();
15052             return;
15053         }
15054         
15055         this.clearTouchView();
15056         
15057         var rawValue = this.getRawValue();
15058         
15059         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15060         
15061         this.tickItems = [];
15062         
15063         this.store.data.each(function(d, rowIndex){
15064             var row = this.touchViewListGroup.createChild(template);
15065             
15066             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15067                 row.addClass(d.data.cls);
15068             }
15069             
15070             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15071                 var cfg = {
15072                     data : d.data,
15073                     html : d.data[this.displayField]
15074                 };
15075                 
15076                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15077                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15078                 }
15079             }
15080             row.removeClass('selected');
15081             if(!this.multiple && this.valueField &&
15082                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15083             {
15084                 // radio buttons..
15085                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15086                 row.addClass('selected');
15087             }
15088             
15089             if(this.multiple && this.valueField &&
15090                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15091             {
15092                 
15093                 // checkboxes...
15094                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15095                 this.tickItems.push(d.data);
15096             }
15097             
15098             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15099             
15100         }, this);
15101         
15102         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15103         
15104         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15105
15106         if(this.modalTitle.length){
15107             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15108         }
15109
15110         var listHeight = this.touchViewListGroup.getHeight();
15111         
15112         var _this = this;
15113         
15114         if(firstChecked && listHeight > bodyHeight){
15115             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15116         }
15117         
15118     },
15119     
15120     onTouchViewLoadException : function()
15121     {
15122         this.hideTouchView();
15123     },
15124     
15125     onTouchViewEmptyResults : function()
15126     {
15127         this.clearTouchView();
15128         
15129         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15130         
15131         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15132         
15133     },
15134     
15135     clearTouchView : function()
15136     {
15137         this.touchViewListGroup.dom.innerHTML = '';
15138     },
15139     
15140     onTouchViewClick : function(e, el, o)
15141     {
15142         e.preventDefault();
15143         
15144         var row = o.row;
15145         var rowIndex = o.rowIndex;
15146         
15147         var r = this.store.getAt(rowIndex);
15148         
15149         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15150             
15151             if(!this.multiple){
15152                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15153                     c.dom.removeAttribute('checked');
15154                 }, this);
15155
15156                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15157
15158                 this.setFromData(r.data);
15159
15160                 var close = this.closeTriggerEl();
15161
15162                 if(close){
15163                     close.show();
15164                 }
15165
15166                 this.hideTouchView();
15167
15168                 this.fireEvent('select', this, r, rowIndex);
15169
15170                 return;
15171             }
15172
15173             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15174                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15175                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15176                 return;
15177             }
15178
15179             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15180             this.addItem(r.data);
15181             this.tickItems.push(r.data);
15182         }
15183     },
15184     
15185     getAutoCreateNativeIOS : function()
15186     {
15187         var cfg = {
15188             cls: 'form-group' //input-group,
15189         };
15190         
15191         var combobox =  {
15192             tag: 'select',
15193             cls : 'roo-ios-select'
15194         };
15195         
15196         if (this.name) {
15197             combobox.name = this.name;
15198         }
15199         
15200         if (this.disabled) {
15201             combobox.disabled = true;
15202         }
15203         
15204         var settings = this;
15205         
15206         ['xs','sm','md','lg'].map(function(size){
15207             if (settings[size]) {
15208                 cfg.cls += ' col-' + size + '-' + settings[size];
15209             }
15210         });
15211         
15212         cfg.cn = combobox;
15213         
15214         return cfg;
15215         
15216     },
15217     
15218     initIOSView : function()
15219     {
15220         this.store.on('load', this.onIOSViewLoad, this);
15221         
15222         return;
15223     },
15224     
15225     onIOSViewLoad : function()
15226     {
15227         if(this.store.getCount() < 1){
15228             return;
15229         }
15230         
15231         this.clearIOSView();
15232         
15233         if(this.allowBlank) {
15234             
15235             var default_text = '-- SELECT --';
15236             
15237             if(this.placeholder.length){
15238                 default_text = this.placeholder;
15239             }
15240             
15241             if(this.emptyTitle.length){
15242                 default_text += ' - ' + this.emptyTitle + ' -';
15243             }
15244             
15245             var opt = this.inputEl().createChild({
15246                 tag: 'option',
15247                 value : 0,
15248                 html : default_text
15249             });
15250             
15251             var o = {};
15252             o[this.valueField] = 0;
15253             o[this.displayField] = default_text;
15254             
15255             this.ios_options.push({
15256                 data : o,
15257                 el : opt
15258             });
15259             
15260         }
15261         
15262         this.store.data.each(function(d, rowIndex){
15263             
15264             var html = '';
15265             
15266             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15267                 html = d.data[this.displayField];
15268             }
15269             
15270             var value = '';
15271             
15272             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15273                 value = d.data[this.valueField];
15274             }
15275             
15276             var option = {
15277                 tag: 'option',
15278                 value : value,
15279                 html : html
15280             };
15281             
15282             if(this.value == d.data[this.valueField]){
15283                 option['selected'] = true;
15284             }
15285             
15286             var opt = this.inputEl().createChild(option);
15287             
15288             this.ios_options.push({
15289                 data : d.data,
15290                 el : opt
15291             });
15292             
15293         }, this);
15294         
15295         this.inputEl().on('change', function(){
15296            this.fireEvent('select', this);
15297         }, this);
15298         
15299     },
15300     
15301     clearIOSView: function()
15302     {
15303         this.inputEl().dom.innerHTML = '';
15304         
15305         this.ios_options = [];
15306     },
15307     
15308     setIOSValue: function(v)
15309     {
15310         this.value = v;
15311         
15312         if(!this.ios_options){
15313             return;
15314         }
15315         
15316         Roo.each(this.ios_options, function(opts){
15317            
15318            opts.el.dom.removeAttribute('selected');
15319            
15320            if(opts.data[this.valueField] != v){
15321                return;
15322            }
15323            
15324            opts.el.dom.setAttribute('selected', true);
15325            
15326         }, this);
15327     }
15328
15329     /** 
15330     * @cfg {Boolean} grow 
15331     * @hide 
15332     */
15333     /** 
15334     * @cfg {Number} growMin 
15335     * @hide 
15336     */
15337     /** 
15338     * @cfg {Number} growMax 
15339     * @hide 
15340     */
15341     /**
15342      * @hide
15343      * @method autoSize
15344      */
15345 });
15346
15347 Roo.apply(Roo.bootstrap.ComboBox,  {
15348     
15349     header : {
15350         tag: 'div',
15351         cls: 'modal-header',
15352         cn: [
15353             {
15354                 tag: 'h4',
15355                 cls: 'modal-title'
15356             }
15357         ]
15358     },
15359     
15360     body : {
15361         tag: 'div',
15362         cls: 'modal-body',
15363         cn: [
15364             {
15365                 tag: 'ul',
15366                 cls: 'list-group'
15367             }
15368         ]
15369     },
15370     
15371     listItemRadio : {
15372         tag: 'li',
15373         cls: 'list-group-item',
15374         cn: [
15375             {
15376                 tag: 'span',
15377                 cls: 'roo-combobox-list-group-item-value'
15378             },
15379             {
15380                 tag: 'div',
15381                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15382                 cn: [
15383                     {
15384                         tag: 'input',
15385                         type: 'radio'
15386                     },
15387                     {
15388                         tag: 'label'
15389                     }
15390                 ]
15391             }
15392         ]
15393     },
15394     
15395     listItemCheckbox : {
15396         tag: 'li',
15397         cls: 'list-group-item',
15398         cn: [
15399             {
15400                 tag: 'span',
15401                 cls: 'roo-combobox-list-group-item-value'
15402             },
15403             {
15404                 tag: 'div',
15405                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15406                 cn: [
15407                     {
15408                         tag: 'input',
15409                         type: 'checkbox'
15410                     },
15411                     {
15412                         tag: 'label'
15413                     }
15414                 ]
15415             }
15416         ]
15417     },
15418     
15419     emptyResult : {
15420         tag: 'div',
15421         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15422     },
15423     
15424     footer : {
15425         tag: 'div',
15426         cls: 'modal-footer',
15427         cn: [
15428             {
15429                 tag: 'div',
15430                 cls: 'row',
15431                 cn: [
15432                     {
15433                         tag: 'div',
15434                         cls: 'col-xs-6 text-left',
15435                         cn: {
15436                             tag: 'button',
15437                             cls: 'btn btn-danger roo-touch-view-cancel',
15438                             html: 'Cancel'
15439                         }
15440                     },
15441                     {
15442                         tag: 'div',
15443                         cls: 'col-xs-6 text-right',
15444                         cn: {
15445                             tag: 'button',
15446                             cls: 'btn btn-success roo-touch-view-ok',
15447                             html: 'OK'
15448                         }
15449                     }
15450                 ]
15451             }
15452         ]
15453         
15454     }
15455 });
15456
15457 Roo.apply(Roo.bootstrap.ComboBox,  {
15458     
15459     touchViewTemplate : {
15460         tag: 'div',
15461         cls: 'modal fade roo-combobox-touch-view',
15462         cn: [
15463             {
15464                 tag: 'div',
15465                 cls: 'modal-dialog',
15466                 style : 'position:fixed', // we have to fix position....
15467                 cn: [
15468                     {
15469                         tag: 'div',
15470                         cls: 'modal-content',
15471                         cn: [
15472                             Roo.bootstrap.ComboBox.header,
15473                             Roo.bootstrap.ComboBox.body,
15474                             Roo.bootstrap.ComboBox.footer
15475                         ]
15476                     }
15477                 ]
15478             }
15479         ]
15480     }
15481 });/*
15482  * Based on:
15483  * Ext JS Library 1.1.1
15484  * Copyright(c) 2006-2007, Ext JS, LLC.
15485  *
15486  * Originally Released Under LGPL - original licence link has changed is not relivant.
15487  *
15488  * Fork - LGPL
15489  * <script type="text/javascript">
15490  */
15491
15492 /**
15493  * @class Roo.View
15494  * @extends Roo.util.Observable
15495  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15496  * This class also supports single and multi selection modes. <br>
15497  * Create a data model bound view:
15498  <pre><code>
15499  var store = new Roo.data.Store(...);
15500
15501  var view = new Roo.View({
15502     el : "my-element",
15503     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15504  
15505     singleSelect: true,
15506     selectedClass: "ydataview-selected",
15507     store: store
15508  });
15509
15510  // listen for node click?
15511  view.on("click", function(vw, index, node, e){
15512  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15513  });
15514
15515  // load XML data
15516  dataModel.load("foobar.xml");
15517  </code></pre>
15518  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15519  * <br><br>
15520  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15521  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15522  * 
15523  * Note: old style constructor is still suported (container, template, config)
15524  * 
15525  * @constructor
15526  * Create a new View
15527  * @param {Object} config The config object
15528  * 
15529  */
15530 Roo.View = function(config, depreciated_tpl, depreciated_config){
15531     
15532     this.parent = false;
15533     
15534     if (typeof(depreciated_tpl) == 'undefined') {
15535         // new way.. - universal constructor.
15536         Roo.apply(this, config);
15537         this.el  = Roo.get(this.el);
15538     } else {
15539         // old format..
15540         this.el  = Roo.get(config);
15541         this.tpl = depreciated_tpl;
15542         Roo.apply(this, depreciated_config);
15543     }
15544     this.wrapEl  = this.el.wrap().wrap();
15545     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15546     
15547     
15548     if(typeof(this.tpl) == "string"){
15549         this.tpl = new Roo.Template(this.tpl);
15550     } else {
15551         // support xtype ctors..
15552         this.tpl = new Roo.factory(this.tpl, Roo);
15553     }
15554     
15555     
15556     this.tpl.compile();
15557     
15558     /** @private */
15559     this.addEvents({
15560         /**
15561          * @event beforeclick
15562          * Fires before a click is processed. Returns false to cancel the default action.
15563          * @param {Roo.View} this
15564          * @param {Number} index The index of the target node
15565          * @param {HTMLElement} node The target node
15566          * @param {Roo.EventObject} e The raw event object
15567          */
15568             "beforeclick" : true,
15569         /**
15570          * @event click
15571          * Fires when a template node is clicked.
15572          * @param {Roo.View} this
15573          * @param {Number} index The index of the target node
15574          * @param {HTMLElement} node The target node
15575          * @param {Roo.EventObject} e The raw event object
15576          */
15577             "click" : true,
15578         /**
15579          * @event dblclick
15580          * Fires when a template node is double clicked.
15581          * @param {Roo.View} this
15582          * @param {Number} index The index of the target node
15583          * @param {HTMLElement} node The target node
15584          * @param {Roo.EventObject} e The raw event object
15585          */
15586             "dblclick" : true,
15587         /**
15588          * @event contextmenu
15589          * Fires when a template node is right clicked.
15590          * @param {Roo.View} this
15591          * @param {Number} index The index of the target node
15592          * @param {HTMLElement} node The target node
15593          * @param {Roo.EventObject} e The raw event object
15594          */
15595             "contextmenu" : true,
15596         /**
15597          * @event selectionchange
15598          * Fires when the selected nodes change.
15599          * @param {Roo.View} this
15600          * @param {Array} selections Array of the selected nodes
15601          */
15602             "selectionchange" : true,
15603     
15604         /**
15605          * @event beforeselect
15606          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15607          * @param {Roo.View} this
15608          * @param {HTMLElement} node The node to be selected
15609          * @param {Array} selections Array of currently selected nodes
15610          */
15611             "beforeselect" : true,
15612         /**
15613          * @event preparedata
15614          * Fires on every row to render, to allow you to change the data.
15615          * @param {Roo.View} this
15616          * @param {Object} data to be rendered (change this)
15617          */
15618           "preparedata" : true
15619           
15620           
15621         });
15622
15623
15624
15625     this.el.on({
15626         "click": this.onClick,
15627         "dblclick": this.onDblClick,
15628         "contextmenu": this.onContextMenu,
15629         scope:this
15630     });
15631
15632     this.selections = [];
15633     this.nodes = [];
15634     this.cmp = new Roo.CompositeElementLite([]);
15635     if(this.store){
15636         this.store = Roo.factory(this.store, Roo.data);
15637         this.setStore(this.store, true);
15638     }
15639     
15640     if ( this.footer && this.footer.xtype) {
15641            
15642          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15643         
15644         this.footer.dataSource = this.store;
15645         this.footer.container = fctr;
15646         this.footer = Roo.factory(this.footer, Roo);
15647         fctr.insertFirst(this.el);
15648         
15649         // this is a bit insane - as the paging toolbar seems to detach the el..
15650 //        dom.parentNode.parentNode.parentNode
15651          // they get detached?
15652     }
15653     
15654     
15655     Roo.View.superclass.constructor.call(this);
15656     
15657     
15658 };
15659
15660 Roo.extend(Roo.View, Roo.util.Observable, {
15661     
15662      /**
15663      * @cfg {Roo.data.Store} store Data store to load data from.
15664      */
15665     store : false,
15666     
15667     /**
15668      * @cfg {String|Roo.Element} el The container element.
15669      */
15670     el : '',
15671     
15672     /**
15673      * @cfg {String|Roo.Template} tpl The template used by this View 
15674      */
15675     tpl : false,
15676     /**
15677      * @cfg {String} dataName the named area of the template to use as the data area
15678      *                          Works with domtemplates roo-name="name"
15679      */
15680     dataName: false,
15681     /**
15682      * @cfg {String} selectedClass The css class to add to selected nodes
15683      */
15684     selectedClass : "x-view-selected",
15685      /**
15686      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15687      */
15688     emptyText : "",
15689     
15690     /**
15691      * @cfg {String} text to display on mask (default Loading)
15692      */
15693     mask : false,
15694     /**
15695      * @cfg {Boolean} multiSelect Allow multiple selection
15696      */
15697     multiSelect : false,
15698     /**
15699      * @cfg {Boolean} singleSelect Allow single selection
15700      */
15701     singleSelect:  false,
15702     
15703     /**
15704      * @cfg {Boolean} toggleSelect - selecting 
15705      */
15706     toggleSelect : false,
15707     
15708     /**
15709      * @cfg {Boolean} tickable - selecting 
15710      */
15711     tickable : false,
15712     
15713     /**
15714      * Returns the element this view is bound to.
15715      * @return {Roo.Element}
15716      */
15717     getEl : function(){
15718         return this.wrapEl;
15719     },
15720     
15721     
15722
15723     /**
15724      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15725      */
15726     refresh : function(){
15727         //Roo.log('refresh');
15728         var t = this.tpl;
15729         
15730         // if we are using something like 'domtemplate', then
15731         // the what gets used is:
15732         // t.applySubtemplate(NAME, data, wrapping data..)
15733         // the outer template then get' applied with
15734         //     the store 'extra data'
15735         // and the body get's added to the
15736         //      roo-name="data" node?
15737         //      <span class='roo-tpl-{name}'></span> ?????
15738         
15739         
15740         
15741         this.clearSelections();
15742         this.el.update("");
15743         var html = [];
15744         var records = this.store.getRange();
15745         if(records.length < 1) {
15746             
15747             // is this valid??  = should it render a template??
15748             
15749             this.el.update(this.emptyText);
15750             return;
15751         }
15752         var el = this.el;
15753         if (this.dataName) {
15754             this.el.update(t.apply(this.store.meta)); //????
15755             el = this.el.child('.roo-tpl-' + this.dataName);
15756         }
15757         
15758         for(var i = 0, len = records.length; i < len; i++){
15759             var data = this.prepareData(records[i].data, i, records[i]);
15760             this.fireEvent("preparedata", this, data, i, records[i]);
15761             
15762             var d = Roo.apply({}, data);
15763             
15764             if(this.tickable){
15765                 Roo.apply(d, {'roo-id' : Roo.id()});
15766                 
15767                 var _this = this;
15768             
15769                 Roo.each(this.parent.item, function(item){
15770                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15771                         return;
15772                     }
15773                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15774                 });
15775             }
15776             
15777             html[html.length] = Roo.util.Format.trim(
15778                 this.dataName ?
15779                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15780                     t.apply(d)
15781             );
15782         }
15783         
15784         
15785         
15786         el.update(html.join(""));
15787         this.nodes = el.dom.childNodes;
15788         this.updateIndexes(0);
15789     },
15790     
15791
15792     /**
15793      * Function to override to reformat the data that is sent to
15794      * the template for each node.
15795      * DEPRICATED - use the preparedata event handler.
15796      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15797      * a JSON object for an UpdateManager bound view).
15798      */
15799     prepareData : function(data, index, record)
15800     {
15801         this.fireEvent("preparedata", this, data, index, record);
15802         return data;
15803     },
15804
15805     onUpdate : function(ds, record){
15806         // Roo.log('on update');   
15807         this.clearSelections();
15808         var index = this.store.indexOf(record);
15809         var n = this.nodes[index];
15810         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15811         n.parentNode.removeChild(n);
15812         this.updateIndexes(index, index);
15813     },
15814
15815     
15816     
15817 // --------- FIXME     
15818     onAdd : function(ds, records, index)
15819     {
15820         //Roo.log(['on Add', ds, records, index] );        
15821         this.clearSelections();
15822         if(this.nodes.length == 0){
15823             this.refresh();
15824             return;
15825         }
15826         var n = this.nodes[index];
15827         for(var i = 0, len = records.length; i < len; i++){
15828             var d = this.prepareData(records[i].data, i, records[i]);
15829             if(n){
15830                 this.tpl.insertBefore(n, d);
15831             }else{
15832                 
15833                 this.tpl.append(this.el, d);
15834             }
15835         }
15836         this.updateIndexes(index);
15837     },
15838
15839     onRemove : function(ds, record, index){
15840        // Roo.log('onRemove');
15841         this.clearSelections();
15842         var el = this.dataName  ?
15843             this.el.child('.roo-tpl-' + this.dataName) :
15844             this.el; 
15845         
15846         el.dom.removeChild(this.nodes[index]);
15847         this.updateIndexes(index);
15848     },
15849
15850     /**
15851      * Refresh an individual node.
15852      * @param {Number} index
15853      */
15854     refreshNode : function(index){
15855         this.onUpdate(this.store, this.store.getAt(index));
15856     },
15857
15858     updateIndexes : function(startIndex, endIndex){
15859         var ns = this.nodes;
15860         startIndex = startIndex || 0;
15861         endIndex = endIndex || ns.length - 1;
15862         for(var i = startIndex; i <= endIndex; i++){
15863             ns[i].nodeIndex = i;
15864         }
15865     },
15866
15867     /**
15868      * Changes the data store this view uses and refresh the view.
15869      * @param {Store} store
15870      */
15871     setStore : function(store, initial){
15872         if(!initial && this.store){
15873             this.store.un("datachanged", this.refresh);
15874             this.store.un("add", this.onAdd);
15875             this.store.un("remove", this.onRemove);
15876             this.store.un("update", this.onUpdate);
15877             this.store.un("clear", this.refresh);
15878             this.store.un("beforeload", this.onBeforeLoad);
15879             this.store.un("load", this.onLoad);
15880             this.store.un("loadexception", this.onLoad);
15881         }
15882         if(store){
15883           
15884             store.on("datachanged", this.refresh, this);
15885             store.on("add", this.onAdd, this);
15886             store.on("remove", this.onRemove, this);
15887             store.on("update", this.onUpdate, this);
15888             store.on("clear", this.refresh, this);
15889             store.on("beforeload", this.onBeforeLoad, this);
15890             store.on("load", this.onLoad, this);
15891             store.on("loadexception", this.onLoad, this);
15892         }
15893         
15894         if(store){
15895             this.refresh();
15896         }
15897     },
15898     /**
15899      * onbeforeLoad - masks the loading area.
15900      *
15901      */
15902     onBeforeLoad : function(store,opts)
15903     {
15904          //Roo.log('onBeforeLoad');   
15905         if (!opts.add) {
15906             this.el.update("");
15907         }
15908         this.el.mask(this.mask ? this.mask : "Loading" ); 
15909     },
15910     onLoad : function ()
15911     {
15912         this.el.unmask();
15913     },
15914     
15915
15916     /**
15917      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15918      * @param {HTMLElement} node
15919      * @return {HTMLElement} The template node
15920      */
15921     findItemFromChild : function(node){
15922         var el = this.dataName  ?
15923             this.el.child('.roo-tpl-' + this.dataName,true) :
15924             this.el.dom; 
15925         
15926         if(!node || node.parentNode == el){
15927                     return node;
15928             }
15929             var p = node.parentNode;
15930             while(p && p != el){
15931             if(p.parentNode == el){
15932                 return p;
15933             }
15934             p = p.parentNode;
15935         }
15936             return null;
15937     },
15938
15939     /** @ignore */
15940     onClick : function(e){
15941         var item = this.findItemFromChild(e.getTarget());
15942         if(item){
15943             var index = this.indexOf(item);
15944             if(this.onItemClick(item, index, e) !== false){
15945                 this.fireEvent("click", this, index, item, e);
15946             }
15947         }else{
15948             this.clearSelections();
15949         }
15950     },
15951
15952     /** @ignore */
15953     onContextMenu : function(e){
15954         var item = this.findItemFromChild(e.getTarget());
15955         if(item){
15956             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15957         }
15958     },
15959
15960     /** @ignore */
15961     onDblClick : function(e){
15962         var item = this.findItemFromChild(e.getTarget());
15963         if(item){
15964             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15965         }
15966     },
15967
15968     onItemClick : function(item, index, e)
15969     {
15970         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15971             return false;
15972         }
15973         if (this.toggleSelect) {
15974             var m = this.isSelected(item) ? 'unselect' : 'select';
15975             //Roo.log(m);
15976             var _t = this;
15977             _t[m](item, true, false);
15978             return true;
15979         }
15980         if(this.multiSelect || this.singleSelect){
15981             if(this.multiSelect && e.shiftKey && this.lastSelection){
15982                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15983             }else{
15984                 this.select(item, this.multiSelect && e.ctrlKey);
15985                 this.lastSelection = item;
15986             }
15987             
15988             if(!this.tickable){
15989                 e.preventDefault();
15990             }
15991             
15992         }
15993         return true;
15994     },
15995
15996     /**
15997      * Get the number of selected nodes.
15998      * @return {Number}
15999      */
16000     getSelectionCount : function(){
16001         return this.selections.length;
16002     },
16003
16004     /**
16005      * Get the currently selected nodes.
16006      * @return {Array} An array of HTMLElements
16007      */
16008     getSelectedNodes : function(){
16009         return this.selections;
16010     },
16011
16012     /**
16013      * Get the indexes of the selected nodes.
16014      * @return {Array}
16015      */
16016     getSelectedIndexes : function(){
16017         var indexes = [], s = this.selections;
16018         for(var i = 0, len = s.length; i < len; i++){
16019             indexes.push(s[i].nodeIndex);
16020         }
16021         return indexes;
16022     },
16023
16024     /**
16025      * Clear all selections
16026      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16027      */
16028     clearSelections : function(suppressEvent){
16029         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16030             this.cmp.elements = this.selections;
16031             this.cmp.removeClass(this.selectedClass);
16032             this.selections = [];
16033             if(!suppressEvent){
16034                 this.fireEvent("selectionchange", this, this.selections);
16035             }
16036         }
16037     },
16038
16039     /**
16040      * Returns true if the passed node is selected
16041      * @param {HTMLElement/Number} node The node or node index
16042      * @return {Boolean}
16043      */
16044     isSelected : function(node){
16045         var s = this.selections;
16046         if(s.length < 1){
16047             return false;
16048         }
16049         node = this.getNode(node);
16050         return s.indexOf(node) !== -1;
16051     },
16052
16053     /**
16054      * Selects nodes.
16055      * @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
16056      * @param {Boolean} keepExisting (optional) true to keep existing selections
16057      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16058      */
16059     select : function(nodeInfo, keepExisting, suppressEvent){
16060         if(nodeInfo instanceof Array){
16061             if(!keepExisting){
16062                 this.clearSelections(true);
16063             }
16064             for(var i = 0, len = nodeInfo.length; i < len; i++){
16065                 this.select(nodeInfo[i], true, true);
16066             }
16067             return;
16068         } 
16069         var node = this.getNode(nodeInfo);
16070         if(!node || this.isSelected(node)){
16071             return; // already selected.
16072         }
16073         if(!keepExisting){
16074             this.clearSelections(true);
16075         }
16076         
16077         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16078             Roo.fly(node).addClass(this.selectedClass);
16079             this.selections.push(node);
16080             if(!suppressEvent){
16081                 this.fireEvent("selectionchange", this, this.selections);
16082             }
16083         }
16084         
16085         
16086     },
16087       /**
16088      * Unselects nodes.
16089      * @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
16090      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16091      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16092      */
16093     unselect : function(nodeInfo, keepExisting, suppressEvent)
16094     {
16095         if(nodeInfo instanceof Array){
16096             Roo.each(this.selections, function(s) {
16097                 this.unselect(s, nodeInfo);
16098             }, this);
16099             return;
16100         }
16101         var node = this.getNode(nodeInfo);
16102         if(!node || !this.isSelected(node)){
16103             //Roo.log("not selected");
16104             return; // not selected.
16105         }
16106         // fireevent???
16107         var ns = [];
16108         Roo.each(this.selections, function(s) {
16109             if (s == node ) {
16110                 Roo.fly(node).removeClass(this.selectedClass);
16111
16112                 return;
16113             }
16114             ns.push(s);
16115         },this);
16116         
16117         this.selections= ns;
16118         this.fireEvent("selectionchange", this, this.selections);
16119     },
16120
16121     /**
16122      * Gets a template node.
16123      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16124      * @return {HTMLElement} The node or null if it wasn't found
16125      */
16126     getNode : function(nodeInfo){
16127         if(typeof nodeInfo == "string"){
16128             return document.getElementById(nodeInfo);
16129         }else if(typeof nodeInfo == "number"){
16130             return this.nodes[nodeInfo];
16131         }
16132         return nodeInfo;
16133     },
16134
16135     /**
16136      * Gets a range template nodes.
16137      * @param {Number} startIndex
16138      * @param {Number} endIndex
16139      * @return {Array} An array of nodes
16140      */
16141     getNodes : function(start, end){
16142         var ns = this.nodes;
16143         start = start || 0;
16144         end = typeof end == "undefined" ? ns.length - 1 : end;
16145         var nodes = [];
16146         if(start <= end){
16147             for(var i = start; i <= end; i++){
16148                 nodes.push(ns[i]);
16149             }
16150         } else{
16151             for(var i = start; i >= end; i--){
16152                 nodes.push(ns[i]);
16153             }
16154         }
16155         return nodes;
16156     },
16157
16158     /**
16159      * Finds the index of the passed node
16160      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16161      * @return {Number} The index of the node or -1
16162      */
16163     indexOf : function(node){
16164         node = this.getNode(node);
16165         if(typeof node.nodeIndex == "number"){
16166             return node.nodeIndex;
16167         }
16168         var ns = this.nodes;
16169         for(var i = 0, len = ns.length; i < len; i++){
16170             if(ns[i] == node){
16171                 return i;
16172             }
16173         }
16174         return -1;
16175     }
16176 });
16177 /*
16178  * - LGPL
16179  *
16180  * based on jquery fullcalendar
16181  * 
16182  */
16183
16184 Roo.bootstrap = Roo.bootstrap || {};
16185 /**
16186  * @class Roo.bootstrap.Calendar
16187  * @extends Roo.bootstrap.Component
16188  * Bootstrap Calendar class
16189  * @cfg {Boolean} loadMask (true|false) default false
16190  * @cfg {Object} header generate the user specific header of the calendar, default false
16191
16192  * @constructor
16193  * Create a new Container
16194  * @param {Object} config The config object
16195  */
16196
16197
16198
16199 Roo.bootstrap.Calendar = function(config){
16200     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16201      this.addEvents({
16202         /**
16203              * @event select
16204              * Fires when a date is selected
16205              * @param {DatePicker} this
16206              * @param {Date} date The selected date
16207              */
16208         'select': true,
16209         /**
16210              * @event monthchange
16211              * Fires when the displayed month changes 
16212              * @param {DatePicker} this
16213              * @param {Date} date The selected month
16214              */
16215         'monthchange': true,
16216         /**
16217              * @event evententer
16218              * Fires when mouse over an event
16219              * @param {Calendar} this
16220              * @param {event} Event
16221              */
16222         'evententer': true,
16223         /**
16224              * @event eventleave
16225              * Fires when the mouse leaves an
16226              * @param {Calendar} this
16227              * @param {event}
16228              */
16229         'eventleave': true,
16230         /**
16231              * @event eventclick
16232              * Fires when the mouse click an
16233              * @param {Calendar} this
16234              * @param {event}
16235              */
16236         'eventclick': true
16237         
16238     });
16239
16240 };
16241
16242 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16243     
16244      /**
16245      * @cfg {Number} startDay
16246      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16247      */
16248     startDay : 0,
16249     
16250     loadMask : false,
16251     
16252     header : false,
16253       
16254     getAutoCreate : function(){
16255         
16256         
16257         var fc_button = function(name, corner, style, content ) {
16258             return Roo.apply({},{
16259                 tag : 'span',
16260                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16261                          (corner.length ?
16262                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16263                             ''
16264                         ),
16265                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16266                 unselectable: 'on'
16267             });
16268         };
16269         
16270         var header = {};
16271         
16272         if(!this.header){
16273             header = {
16274                 tag : 'table',
16275                 cls : 'fc-header',
16276                 style : 'width:100%',
16277                 cn : [
16278                     {
16279                         tag: 'tr',
16280                         cn : [
16281                             {
16282                                 tag : 'td',
16283                                 cls : 'fc-header-left',
16284                                 cn : [
16285                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16286                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16287                                     { tag: 'span', cls: 'fc-header-space' },
16288                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16289
16290
16291                                 ]
16292                             },
16293
16294                             {
16295                                 tag : 'td',
16296                                 cls : 'fc-header-center',
16297                                 cn : [
16298                                     {
16299                                         tag: 'span',
16300                                         cls: 'fc-header-title',
16301                                         cn : {
16302                                             tag: 'H2',
16303                                             html : 'month / year'
16304                                         }
16305                                     }
16306
16307                                 ]
16308                             },
16309                             {
16310                                 tag : 'td',
16311                                 cls : 'fc-header-right',
16312                                 cn : [
16313                               /*      fc_button('month', 'left', '', 'month' ),
16314                                     fc_button('week', '', '', 'week' ),
16315                                     fc_button('day', 'right', '', 'day' )
16316                                 */    
16317
16318                                 ]
16319                             }
16320
16321                         ]
16322                     }
16323                 ]
16324             };
16325         }
16326         
16327         header = this.header;
16328         
16329        
16330         var cal_heads = function() {
16331             var ret = [];
16332             // fixme - handle this.
16333             
16334             for (var i =0; i < Date.dayNames.length; i++) {
16335                 var d = Date.dayNames[i];
16336                 ret.push({
16337                     tag: 'th',
16338                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16339                     html : d.substring(0,3)
16340                 });
16341                 
16342             }
16343             ret[0].cls += ' fc-first';
16344             ret[6].cls += ' fc-last';
16345             return ret;
16346         };
16347         var cal_cell = function(n) {
16348             return  {
16349                 tag: 'td',
16350                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16351                 cn : [
16352                     {
16353                         cn : [
16354                             {
16355                                 cls: 'fc-day-number',
16356                                 html: 'D'
16357                             },
16358                             {
16359                                 cls: 'fc-day-content',
16360                              
16361                                 cn : [
16362                                      {
16363                                         style: 'position: relative;' // height: 17px;
16364                                     }
16365                                 ]
16366                             }
16367                             
16368                             
16369                         ]
16370                     }
16371                 ]
16372                 
16373             }
16374         };
16375         var cal_rows = function() {
16376             
16377             var ret = [];
16378             for (var r = 0; r < 6; r++) {
16379                 var row= {
16380                     tag : 'tr',
16381                     cls : 'fc-week',
16382                     cn : []
16383                 };
16384                 
16385                 for (var i =0; i < Date.dayNames.length; i++) {
16386                     var d = Date.dayNames[i];
16387                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16388
16389                 }
16390                 row.cn[0].cls+=' fc-first';
16391                 row.cn[0].cn[0].style = 'min-height:90px';
16392                 row.cn[6].cls+=' fc-last';
16393                 ret.push(row);
16394                 
16395             }
16396             ret[0].cls += ' fc-first';
16397             ret[4].cls += ' fc-prev-last';
16398             ret[5].cls += ' fc-last';
16399             return ret;
16400             
16401         };
16402         
16403         var cal_table = {
16404             tag: 'table',
16405             cls: 'fc-border-separate',
16406             style : 'width:100%',
16407             cellspacing  : 0,
16408             cn : [
16409                 { 
16410                     tag: 'thead',
16411                     cn : [
16412                         { 
16413                             tag: 'tr',
16414                             cls : 'fc-first fc-last',
16415                             cn : cal_heads()
16416                         }
16417                     ]
16418                 },
16419                 { 
16420                     tag: 'tbody',
16421                     cn : cal_rows()
16422                 }
16423                   
16424             ]
16425         };
16426          
16427          var cfg = {
16428             cls : 'fc fc-ltr',
16429             cn : [
16430                 header,
16431                 {
16432                     cls : 'fc-content',
16433                     style : "position: relative;",
16434                     cn : [
16435                         {
16436                             cls : 'fc-view fc-view-month fc-grid',
16437                             style : 'position: relative',
16438                             unselectable : 'on',
16439                             cn : [
16440                                 {
16441                                     cls : 'fc-event-container',
16442                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16443                                 },
16444                                 cal_table
16445                             ]
16446                         }
16447                     ]
16448     
16449                 }
16450            ] 
16451             
16452         };
16453         
16454          
16455         
16456         return cfg;
16457     },
16458     
16459     
16460     initEvents : function()
16461     {
16462         if(!this.store){
16463             throw "can not find store for calendar";
16464         }
16465         
16466         var mark = {
16467             tag: "div",
16468             cls:"x-dlg-mask",
16469             style: "text-align:center",
16470             cn: [
16471                 {
16472                     tag: "div",
16473                     style: "background-color:white;width:50%;margin:250 auto",
16474                     cn: [
16475                         {
16476                             tag: "img",
16477                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16478                         },
16479                         {
16480                             tag: "span",
16481                             html: "Loading"
16482                         }
16483                         
16484                     ]
16485                 }
16486             ]
16487         };
16488         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16489         
16490         var size = this.el.select('.fc-content', true).first().getSize();
16491         this.maskEl.setSize(size.width, size.height);
16492         this.maskEl.enableDisplayMode("block");
16493         if(!this.loadMask){
16494             this.maskEl.hide();
16495         }
16496         
16497         this.store = Roo.factory(this.store, Roo.data);
16498         this.store.on('load', this.onLoad, this);
16499         this.store.on('beforeload', this.onBeforeLoad, this);
16500         
16501         this.resize();
16502         
16503         this.cells = this.el.select('.fc-day',true);
16504         //Roo.log(this.cells);
16505         this.textNodes = this.el.query('.fc-day-number');
16506         this.cells.addClassOnOver('fc-state-hover');
16507         
16508         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16509         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16510         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16511         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16512         
16513         this.on('monthchange', this.onMonthChange, this);
16514         
16515         this.update(new Date().clearTime());
16516     },
16517     
16518     resize : function() {
16519         var sz  = this.el.getSize();
16520         
16521         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16522         this.el.select('.fc-day-content div',true).setHeight(34);
16523     },
16524     
16525     
16526     // private
16527     showPrevMonth : function(e){
16528         this.update(this.activeDate.add("mo", -1));
16529     },
16530     showToday : function(e){
16531         this.update(new Date().clearTime());
16532     },
16533     // private
16534     showNextMonth : function(e){
16535         this.update(this.activeDate.add("mo", 1));
16536     },
16537
16538     // private
16539     showPrevYear : function(){
16540         this.update(this.activeDate.add("y", -1));
16541     },
16542
16543     // private
16544     showNextYear : function(){
16545         this.update(this.activeDate.add("y", 1));
16546     },
16547
16548     
16549    // private
16550     update : function(date)
16551     {
16552         var vd = this.activeDate;
16553         this.activeDate = date;
16554 //        if(vd && this.el){
16555 //            var t = date.getTime();
16556 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16557 //                Roo.log('using add remove');
16558 //                
16559 //                this.fireEvent('monthchange', this, date);
16560 //                
16561 //                this.cells.removeClass("fc-state-highlight");
16562 //                this.cells.each(function(c){
16563 //                   if(c.dateValue == t){
16564 //                       c.addClass("fc-state-highlight");
16565 //                       setTimeout(function(){
16566 //                            try{c.dom.firstChild.focus();}catch(e){}
16567 //                       }, 50);
16568 //                       return false;
16569 //                   }
16570 //                   return true;
16571 //                });
16572 //                return;
16573 //            }
16574 //        }
16575         
16576         var days = date.getDaysInMonth();
16577         
16578         var firstOfMonth = date.getFirstDateOfMonth();
16579         var startingPos = firstOfMonth.getDay()-this.startDay;
16580         
16581         if(startingPos < this.startDay){
16582             startingPos += 7;
16583         }
16584         
16585         var pm = date.add(Date.MONTH, -1);
16586         var prevStart = pm.getDaysInMonth()-startingPos;
16587 //        
16588         this.cells = this.el.select('.fc-day',true);
16589         this.textNodes = this.el.query('.fc-day-number');
16590         this.cells.addClassOnOver('fc-state-hover');
16591         
16592         var cells = this.cells.elements;
16593         var textEls = this.textNodes;
16594         
16595         Roo.each(cells, function(cell){
16596             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16597         });
16598         
16599         days += startingPos;
16600
16601         // convert everything to numbers so it's fast
16602         var day = 86400000;
16603         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16604         //Roo.log(d);
16605         //Roo.log(pm);
16606         //Roo.log(prevStart);
16607         
16608         var today = new Date().clearTime().getTime();
16609         var sel = date.clearTime().getTime();
16610         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16611         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16612         var ddMatch = this.disabledDatesRE;
16613         var ddText = this.disabledDatesText;
16614         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16615         var ddaysText = this.disabledDaysText;
16616         var format = this.format;
16617         
16618         var setCellClass = function(cal, cell){
16619             cell.row = 0;
16620             cell.events = [];
16621             cell.more = [];
16622             //Roo.log('set Cell Class');
16623             cell.title = "";
16624             var t = d.getTime();
16625             
16626             //Roo.log(d);
16627             
16628             cell.dateValue = t;
16629             if(t == today){
16630                 cell.className += " fc-today";
16631                 cell.className += " fc-state-highlight";
16632                 cell.title = cal.todayText;
16633             }
16634             if(t == sel){
16635                 // disable highlight in other month..
16636                 //cell.className += " fc-state-highlight";
16637                 
16638             }
16639             // disabling
16640             if(t < min) {
16641                 cell.className = " fc-state-disabled";
16642                 cell.title = cal.minText;
16643                 return;
16644             }
16645             if(t > max) {
16646                 cell.className = " fc-state-disabled";
16647                 cell.title = cal.maxText;
16648                 return;
16649             }
16650             if(ddays){
16651                 if(ddays.indexOf(d.getDay()) != -1){
16652                     cell.title = ddaysText;
16653                     cell.className = " fc-state-disabled";
16654                 }
16655             }
16656             if(ddMatch && format){
16657                 var fvalue = d.dateFormat(format);
16658                 if(ddMatch.test(fvalue)){
16659                     cell.title = ddText.replace("%0", fvalue);
16660                     cell.className = " fc-state-disabled";
16661                 }
16662             }
16663             
16664             if (!cell.initialClassName) {
16665                 cell.initialClassName = cell.dom.className;
16666             }
16667             
16668             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16669         };
16670
16671         var i = 0;
16672         
16673         for(; i < startingPos; i++) {
16674             textEls[i].innerHTML = (++prevStart);
16675             d.setDate(d.getDate()+1);
16676             
16677             cells[i].className = "fc-past fc-other-month";
16678             setCellClass(this, cells[i]);
16679         }
16680         
16681         var intDay = 0;
16682         
16683         for(; i < days; i++){
16684             intDay = i - startingPos + 1;
16685             textEls[i].innerHTML = (intDay);
16686             d.setDate(d.getDate()+1);
16687             
16688             cells[i].className = ''; // "x-date-active";
16689             setCellClass(this, cells[i]);
16690         }
16691         var extraDays = 0;
16692         
16693         for(; i < 42; i++) {
16694             textEls[i].innerHTML = (++extraDays);
16695             d.setDate(d.getDate()+1);
16696             
16697             cells[i].className = "fc-future fc-other-month";
16698             setCellClass(this, cells[i]);
16699         }
16700         
16701         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16702         
16703         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16704         
16705         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16706         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16707         
16708         if(totalRows != 6){
16709             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16710             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16711         }
16712         
16713         this.fireEvent('monthchange', this, date);
16714         
16715         
16716         /*
16717         if(!this.internalRender){
16718             var main = this.el.dom.firstChild;
16719             var w = main.offsetWidth;
16720             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16721             Roo.fly(main).setWidth(w);
16722             this.internalRender = true;
16723             // opera does not respect the auto grow header center column
16724             // then, after it gets a width opera refuses to recalculate
16725             // without a second pass
16726             if(Roo.isOpera && !this.secondPass){
16727                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16728                 this.secondPass = true;
16729                 this.update.defer(10, this, [date]);
16730             }
16731         }
16732         */
16733         
16734     },
16735     
16736     findCell : function(dt) {
16737         dt = dt.clearTime().getTime();
16738         var ret = false;
16739         this.cells.each(function(c){
16740             //Roo.log("check " +c.dateValue + '?=' + dt);
16741             if(c.dateValue == dt){
16742                 ret = c;
16743                 return false;
16744             }
16745             return true;
16746         });
16747         
16748         return ret;
16749     },
16750     
16751     findCells : function(ev) {
16752         var s = ev.start.clone().clearTime().getTime();
16753        // Roo.log(s);
16754         var e= ev.end.clone().clearTime().getTime();
16755        // Roo.log(e);
16756         var ret = [];
16757         this.cells.each(function(c){
16758              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16759             
16760             if(c.dateValue > e){
16761                 return ;
16762             }
16763             if(c.dateValue < s){
16764                 return ;
16765             }
16766             ret.push(c);
16767         });
16768         
16769         return ret;    
16770     },
16771     
16772 //    findBestRow: function(cells)
16773 //    {
16774 //        var ret = 0;
16775 //        
16776 //        for (var i =0 ; i < cells.length;i++) {
16777 //            ret  = Math.max(cells[i].rows || 0,ret);
16778 //        }
16779 //        return ret;
16780 //        
16781 //    },
16782     
16783     
16784     addItem : function(ev)
16785     {
16786         // look for vertical location slot in
16787         var cells = this.findCells(ev);
16788         
16789 //        ev.row = this.findBestRow(cells);
16790         
16791         // work out the location.
16792         
16793         var crow = false;
16794         var rows = [];
16795         for(var i =0; i < cells.length; i++) {
16796             
16797             cells[i].row = cells[0].row;
16798             
16799             if(i == 0){
16800                 cells[i].row = cells[i].row + 1;
16801             }
16802             
16803             if (!crow) {
16804                 crow = {
16805                     start : cells[i],
16806                     end :  cells[i]
16807                 };
16808                 continue;
16809             }
16810             if (crow.start.getY() == cells[i].getY()) {
16811                 // on same row.
16812                 crow.end = cells[i];
16813                 continue;
16814             }
16815             // different row.
16816             rows.push(crow);
16817             crow = {
16818                 start: cells[i],
16819                 end : cells[i]
16820             };
16821             
16822         }
16823         
16824         rows.push(crow);
16825         ev.els = [];
16826         ev.rows = rows;
16827         ev.cells = cells;
16828         
16829         cells[0].events.push(ev);
16830         
16831         this.calevents.push(ev);
16832     },
16833     
16834     clearEvents: function() {
16835         
16836         if(!this.calevents){
16837             return;
16838         }
16839         
16840         Roo.each(this.cells.elements, function(c){
16841             c.row = 0;
16842             c.events = [];
16843             c.more = [];
16844         });
16845         
16846         Roo.each(this.calevents, function(e) {
16847             Roo.each(e.els, function(el) {
16848                 el.un('mouseenter' ,this.onEventEnter, this);
16849                 el.un('mouseleave' ,this.onEventLeave, this);
16850                 el.remove();
16851             },this);
16852         },this);
16853         
16854         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16855             e.remove();
16856         });
16857         
16858     },
16859     
16860     renderEvents: function()
16861     {   
16862         var _this = this;
16863         
16864         this.cells.each(function(c) {
16865             
16866             if(c.row < 5){
16867                 return;
16868             }
16869             
16870             var ev = c.events;
16871             
16872             var r = 4;
16873             if(c.row != c.events.length){
16874                 r = 4 - (4 - (c.row - c.events.length));
16875             }
16876             
16877             c.events = ev.slice(0, r);
16878             c.more = ev.slice(r);
16879             
16880             if(c.more.length && c.more.length == 1){
16881                 c.events.push(c.more.pop());
16882             }
16883             
16884             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16885             
16886         });
16887             
16888         this.cells.each(function(c) {
16889             
16890             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16891             
16892             
16893             for (var e = 0; e < c.events.length; e++){
16894                 var ev = c.events[e];
16895                 var rows = ev.rows;
16896                 
16897                 for(var i = 0; i < rows.length; i++) {
16898                 
16899                     // how many rows should it span..
16900
16901                     var  cfg = {
16902                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16903                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16904
16905                         unselectable : "on",
16906                         cn : [
16907                             {
16908                                 cls: 'fc-event-inner',
16909                                 cn : [
16910     //                                {
16911     //                                  tag:'span',
16912     //                                  cls: 'fc-event-time',
16913     //                                  html : cells.length > 1 ? '' : ev.time
16914     //                                },
16915                                     {
16916                                       tag:'span',
16917                                       cls: 'fc-event-title',
16918                                       html : String.format('{0}', ev.title)
16919                                     }
16920
16921
16922                                 ]
16923                             },
16924                             {
16925                                 cls: 'ui-resizable-handle ui-resizable-e',
16926                                 html : '&nbsp;&nbsp;&nbsp'
16927                             }
16928
16929                         ]
16930                     };
16931
16932                     if (i == 0) {
16933                         cfg.cls += ' fc-event-start';
16934                     }
16935                     if ((i+1) == rows.length) {
16936                         cfg.cls += ' fc-event-end';
16937                     }
16938
16939                     var ctr = _this.el.select('.fc-event-container',true).first();
16940                     var cg = ctr.createChild(cfg);
16941
16942                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16943                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16944
16945                     var r = (c.more.length) ? 1 : 0;
16946                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16947                     cg.setWidth(ebox.right - sbox.x -2);
16948
16949                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16950                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16951                     cg.on('click', _this.onEventClick, _this, ev);
16952
16953                     ev.els.push(cg);
16954                     
16955                 }
16956                 
16957             }
16958             
16959             
16960             if(c.more.length){
16961                 var  cfg = {
16962                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16963                     style : 'position: absolute',
16964                     unselectable : "on",
16965                     cn : [
16966                         {
16967                             cls: 'fc-event-inner',
16968                             cn : [
16969                                 {
16970                                   tag:'span',
16971                                   cls: 'fc-event-title',
16972                                   html : 'More'
16973                                 }
16974
16975
16976                             ]
16977                         },
16978                         {
16979                             cls: 'ui-resizable-handle ui-resizable-e',
16980                             html : '&nbsp;&nbsp;&nbsp'
16981                         }
16982
16983                     ]
16984                 };
16985
16986                 var ctr = _this.el.select('.fc-event-container',true).first();
16987                 var cg = ctr.createChild(cfg);
16988
16989                 var sbox = c.select('.fc-day-content',true).first().getBox();
16990                 var ebox = c.select('.fc-day-content',true).first().getBox();
16991                 //Roo.log(cg);
16992                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16993                 cg.setWidth(ebox.right - sbox.x -2);
16994
16995                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16996                 
16997             }
16998             
16999         });
17000         
17001         
17002         
17003     },
17004     
17005     onEventEnter: function (e, el,event,d) {
17006         this.fireEvent('evententer', this, el, event);
17007     },
17008     
17009     onEventLeave: function (e, el,event,d) {
17010         this.fireEvent('eventleave', this, el, event);
17011     },
17012     
17013     onEventClick: function (e, el,event,d) {
17014         this.fireEvent('eventclick', this, el, event);
17015     },
17016     
17017     onMonthChange: function () {
17018         this.store.load();
17019     },
17020     
17021     onMoreEventClick: function(e, el, more)
17022     {
17023         var _this = this;
17024         
17025         this.calpopover.placement = 'right';
17026         this.calpopover.setTitle('More');
17027         
17028         this.calpopover.setContent('');
17029         
17030         var ctr = this.calpopover.el.select('.popover-content', true).first();
17031         
17032         Roo.each(more, function(m){
17033             var cfg = {
17034                 cls : 'fc-event-hori fc-event-draggable',
17035                 html : m.title
17036             };
17037             var cg = ctr.createChild(cfg);
17038             
17039             cg.on('click', _this.onEventClick, _this, m);
17040         });
17041         
17042         this.calpopover.show(el);
17043         
17044         
17045     },
17046     
17047     onLoad: function () 
17048     {   
17049         this.calevents = [];
17050         var cal = this;
17051         
17052         if(this.store.getCount() > 0){
17053             this.store.data.each(function(d){
17054                cal.addItem({
17055                     id : d.data.id,
17056                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17057                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17058                     time : d.data.start_time,
17059                     title : d.data.title,
17060                     description : d.data.description,
17061                     venue : d.data.venue
17062                 });
17063             });
17064         }
17065         
17066         this.renderEvents();
17067         
17068         if(this.calevents.length && this.loadMask){
17069             this.maskEl.hide();
17070         }
17071     },
17072     
17073     onBeforeLoad: function()
17074     {
17075         this.clearEvents();
17076         if(this.loadMask){
17077             this.maskEl.show();
17078         }
17079     }
17080 });
17081
17082  
17083  /*
17084  * - LGPL
17085  *
17086  * element
17087  * 
17088  */
17089
17090 /**
17091  * @class Roo.bootstrap.Popover
17092  * @extends Roo.bootstrap.Component
17093  * Bootstrap Popover class
17094  * @cfg {String} html contents of the popover   (or false to use children..)
17095  * @cfg {String} title of popover (or false to hide)
17096  * @cfg {String} placement how it is placed
17097  * @cfg {String} trigger click || hover (or false to trigger manually)
17098  * @cfg {String} over what (parent or false to trigger manually.)
17099  * @cfg {Number} delay - delay before showing
17100  
17101  * @constructor
17102  * Create a new Popover
17103  * @param {Object} config The config object
17104  */
17105
17106 Roo.bootstrap.Popover = function(config){
17107     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17108     
17109     this.addEvents({
17110         // raw events
17111          /**
17112          * @event show
17113          * After the popover show
17114          * 
17115          * @param {Roo.bootstrap.Popover} this
17116          */
17117         "show" : true,
17118         /**
17119          * @event hide
17120          * After the popover hide
17121          * 
17122          * @param {Roo.bootstrap.Popover} this
17123          */
17124         "hide" : true
17125     });
17126 };
17127
17128 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17129     
17130     title: 'Fill in a title',
17131     html: false,
17132     
17133     placement : 'right',
17134     trigger : 'hover', // hover
17135     
17136     delay : 0,
17137     
17138     over: 'parent',
17139     
17140     can_build_overlaid : false,
17141     
17142     getChildContainer : function()
17143     {
17144         return this.el.select('.popover-content',true).first();
17145     },
17146     
17147     getAutoCreate : function(){
17148          
17149         var cfg = {
17150            cls : 'popover roo-dynamic',
17151            style: 'display:block',
17152            cn : [
17153                 {
17154                     cls : 'arrow'
17155                 },
17156                 {
17157                     cls : 'popover-inner',
17158                     cn : [
17159                         {
17160                             tag: 'h3',
17161                             cls: 'popover-title',
17162                             html : this.title
17163                         },
17164                         {
17165                             cls : 'popover-content',
17166                             html : this.html
17167                         }
17168                     ]
17169                     
17170                 }
17171            ]
17172         };
17173         
17174         return cfg;
17175     },
17176     setTitle: function(str)
17177     {
17178         this.title = str;
17179         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17180     },
17181     setContent: function(str)
17182     {
17183         this.html = str;
17184         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17185     },
17186     // as it get's added to the bottom of the page.
17187     onRender : function(ct, position)
17188     {
17189         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17190         if(!this.el){
17191             var cfg = Roo.apply({},  this.getAutoCreate());
17192             cfg.id = Roo.id();
17193             
17194             if (this.cls) {
17195                 cfg.cls += ' ' + this.cls;
17196             }
17197             if (this.style) {
17198                 cfg.style = this.style;
17199             }
17200             //Roo.log("adding to ");
17201             this.el = Roo.get(document.body).createChild(cfg, position);
17202 //            Roo.log(this.el);
17203         }
17204         this.initEvents();
17205     },
17206     
17207     initEvents : function()
17208     {
17209         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17210         this.el.enableDisplayMode('block');
17211         this.el.hide();
17212         if (this.over === false) {
17213             return; 
17214         }
17215         if (this.triggers === false) {
17216             return;
17217         }
17218         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17219         var triggers = this.trigger ? this.trigger.split(' ') : [];
17220         Roo.each(triggers, function(trigger) {
17221         
17222             if (trigger == 'click') {
17223                 on_el.on('click', this.toggle, this);
17224             } else if (trigger != 'manual') {
17225                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17226                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17227       
17228                 on_el.on(eventIn  ,this.enter, this);
17229                 on_el.on(eventOut, this.leave, this);
17230             }
17231         }, this);
17232         
17233     },
17234     
17235     
17236     // private
17237     timeout : null,
17238     hoverState : null,
17239     
17240     toggle : function () {
17241         this.hoverState == 'in' ? this.leave() : this.enter();
17242     },
17243     
17244     enter : function () {
17245         
17246         clearTimeout(this.timeout);
17247     
17248         this.hoverState = 'in';
17249     
17250         if (!this.delay || !this.delay.show) {
17251             this.show();
17252             return;
17253         }
17254         var _t = this;
17255         this.timeout = setTimeout(function () {
17256             if (_t.hoverState == 'in') {
17257                 _t.show();
17258             }
17259         }, this.delay.show)
17260     },
17261     
17262     leave : function() {
17263         clearTimeout(this.timeout);
17264     
17265         this.hoverState = 'out';
17266     
17267         if (!this.delay || !this.delay.hide) {
17268             this.hide();
17269             return;
17270         }
17271         var _t = this;
17272         this.timeout = setTimeout(function () {
17273             if (_t.hoverState == 'out') {
17274                 _t.hide();
17275             }
17276         }, this.delay.hide)
17277     },
17278     
17279     show : function (on_el)
17280     {
17281         if (!on_el) {
17282             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17283         }
17284         
17285         // set content.
17286         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17287         if (this.html !== false) {
17288             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17289         }
17290         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17291         if (!this.title.length) {
17292             this.el.select('.popover-title',true).hide();
17293         }
17294         
17295         var placement = typeof this.placement == 'function' ?
17296             this.placement.call(this, this.el, on_el) :
17297             this.placement;
17298             
17299         var autoToken = /\s?auto?\s?/i;
17300         var autoPlace = autoToken.test(placement);
17301         if (autoPlace) {
17302             placement = placement.replace(autoToken, '') || 'top';
17303         }
17304         
17305         //this.el.detach()
17306         //this.el.setXY([0,0]);
17307         this.el.show();
17308         this.el.dom.style.display='block';
17309         this.el.addClass(placement);
17310         
17311         //this.el.appendTo(on_el);
17312         
17313         var p = this.getPosition();
17314         var box = this.el.getBox();
17315         
17316         if (autoPlace) {
17317             // fixme..
17318         }
17319         var align = Roo.bootstrap.Popover.alignment[placement];
17320         this.el.alignTo(on_el, align[0],align[1]);
17321         //var arrow = this.el.select('.arrow',true).first();
17322         //arrow.set(align[2], 
17323         
17324         this.el.addClass('in');
17325         
17326         
17327         if (this.el.hasClass('fade')) {
17328             // fade it?
17329         }
17330         
17331         this.hoverState = 'in';
17332         
17333         this.fireEvent('show', this);
17334         
17335     },
17336     hide : function()
17337     {
17338         this.el.setXY([0,0]);
17339         this.el.removeClass('in');
17340         this.el.hide();
17341         this.hoverState = null;
17342         
17343         this.fireEvent('hide', this);
17344     }
17345     
17346 });
17347
17348 Roo.bootstrap.Popover.alignment = {
17349     'left' : ['r-l', [-10,0], 'right'],
17350     'right' : ['l-r', [10,0], 'left'],
17351     'bottom' : ['t-b', [0,10], 'top'],
17352     'top' : [ 'b-t', [0,-10], 'bottom']
17353 };
17354
17355  /*
17356  * - LGPL
17357  *
17358  * Progress
17359  * 
17360  */
17361
17362 /**
17363  * @class Roo.bootstrap.Progress
17364  * @extends Roo.bootstrap.Component
17365  * Bootstrap Progress class
17366  * @cfg {Boolean} striped striped of the progress bar
17367  * @cfg {Boolean} active animated of the progress bar
17368  * 
17369  * 
17370  * @constructor
17371  * Create a new Progress
17372  * @param {Object} config The config object
17373  */
17374
17375 Roo.bootstrap.Progress = function(config){
17376     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17377 };
17378
17379 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17380     
17381     striped : false,
17382     active: false,
17383     
17384     getAutoCreate : function(){
17385         var cfg = {
17386             tag: 'div',
17387             cls: 'progress'
17388         };
17389         
17390         
17391         if(this.striped){
17392             cfg.cls += ' progress-striped';
17393         }
17394       
17395         if(this.active){
17396             cfg.cls += ' active';
17397         }
17398         
17399         
17400         return cfg;
17401     }
17402    
17403 });
17404
17405  
17406
17407  /*
17408  * - LGPL
17409  *
17410  * ProgressBar
17411  * 
17412  */
17413
17414 /**
17415  * @class Roo.bootstrap.ProgressBar
17416  * @extends Roo.bootstrap.Component
17417  * Bootstrap ProgressBar class
17418  * @cfg {Number} aria_valuenow aria-value now
17419  * @cfg {Number} aria_valuemin aria-value min
17420  * @cfg {Number} aria_valuemax aria-value max
17421  * @cfg {String} label label for the progress bar
17422  * @cfg {String} panel (success | info | warning | danger )
17423  * @cfg {String} role role of the progress bar
17424  * @cfg {String} sr_only text
17425  * 
17426  * 
17427  * @constructor
17428  * Create a new ProgressBar
17429  * @param {Object} config The config object
17430  */
17431
17432 Roo.bootstrap.ProgressBar = function(config){
17433     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17434 };
17435
17436 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17437     
17438     aria_valuenow : 0,
17439     aria_valuemin : 0,
17440     aria_valuemax : 100,
17441     label : false,
17442     panel : false,
17443     role : false,
17444     sr_only: false,
17445     
17446     getAutoCreate : function()
17447     {
17448         
17449         var cfg = {
17450             tag: 'div',
17451             cls: 'progress-bar',
17452             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17453         };
17454         
17455         if(this.sr_only){
17456             cfg.cn = {
17457                 tag: 'span',
17458                 cls: 'sr-only',
17459                 html: this.sr_only
17460             }
17461         }
17462         
17463         if(this.role){
17464             cfg.role = this.role;
17465         }
17466         
17467         if(this.aria_valuenow){
17468             cfg['aria-valuenow'] = this.aria_valuenow;
17469         }
17470         
17471         if(this.aria_valuemin){
17472             cfg['aria-valuemin'] = this.aria_valuemin;
17473         }
17474         
17475         if(this.aria_valuemax){
17476             cfg['aria-valuemax'] = this.aria_valuemax;
17477         }
17478         
17479         if(this.label && !this.sr_only){
17480             cfg.html = this.label;
17481         }
17482         
17483         if(this.panel){
17484             cfg.cls += ' progress-bar-' + this.panel;
17485         }
17486         
17487         return cfg;
17488     },
17489     
17490     update : function(aria_valuenow)
17491     {
17492         this.aria_valuenow = aria_valuenow;
17493         
17494         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17495     }
17496    
17497 });
17498
17499  
17500
17501  /*
17502  * - LGPL
17503  *
17504  * column
17505  * 
17506  */
17507
17508 /**
17509  * @class Roo.bootstrap.TabGroup
17510  * @extends Roo.bootstrap.Column
17511  * Bootstrap Column class
17512  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17513  * @cfg {Boolean} carousel true to make the group behave like a carousel
17514  * @cfg {Boolean} bullets show bullets for the panels
17515  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17516  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17517  * @cfg {Boolean} showarrow (true|false) show arrow default true
17518  * 
17519  * @constructor
17520  * Create a new TabGroup
17521  * @param {Object} config The config object
17522  */
17523
17524 Roo.bootstrap.TabGroup = function(config){
17525     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17526     if (!this.navId) {
17527         this.navId = Roo.id();
17528     }
17529     this.tabs = [];
17530     Roo.bootstrap.TabGroup.register(this);
17531     
17532 };
17533
17534 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17535     
17536     carousel : false,
17537     transition : false,
17538     bullets : 0,
17539     timer : 0,
17540     autoslide : false,
17541     slideFn : false,
17542     slideOnTouch : false,
17543     showarrow : true,
17544     
17545     getAutoCreate : function()
17546     {
17547         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17548         
17549         cfg.cls += ' tab-content';
17550         
17551         if (this.carousel) {
17552             cfg.cls += ' carousel slide';
17553             
17554             cfg.cn = [{
17555                cls : 'carousel-inner',
17556                cn : []
17557             }];
17558         
17559             if(this.bullets  && !Roo.isTouch){
17560                 
17561                 var bullets = {
17562                     cls : 'carousel-bullets',
17563                     cn : []
17564                 };
17565                
17566                 if(this.bullets_cls){
17567                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17568                 }
17569                 
17570                 bullets.cn.push({
17571                     cls : 'clear'
17572                 });
17573                 
17574                 cfg.cn[0].cn.push(bullets);
17575             }
17576             
17577             if(this.showarrow){
17578                 cfg.cn[0].cn.push({
17579                     tag : 'div',
17580                     class : 'carousel-arrow',
17581                     cn : [
17582                         {
17583                             tag : 'div',
17584                             class : 'carousel-prev',
17585                             cn : [
17586                                 {
17587                                     tag : 'i',
17588                                     class : 'fa fa-chevron-left'
17589                                 }
17590                             ]
17591                         },
17592                         {
17593                             tag : 'div',
17594                             class : 'carousel-next',
17595                             cn : [
17596                                 {
17597                                     tag : 'i',
17598                                     class : 'fa fa-chevron-right'
17599                                 }
17600                             ]
17601                         }
17602                     ]
17603                 });
17604             }
17605             
17606         }
17607         
17608         return cfg;
17609     },
17610     
17611     initEvents:  function()
17612     {
17613 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17614 //            this.el.on("touchstart", this.onTouchStart, this);
17615 //        }
17616         
17617         if(this.autoslide){
17618             var _this = this;
17619             
17620             this.slideFn = window.setInterval(function() {
17621                 _this.showPanelNext();
17622             }, this.timer);
17623         }
17624         
17625         if(this.showarrow){
17626             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17627             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17628         }
17629         
17630         
17631     },
17632     
17633 //    onTouchStart : function(e, el, o)
17634 //    {
17635 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17636 //            return;
17637 //        }
17638 //        
17639 //        this.showPanelNext();
17640 //    },
17641     
17642     
17643     getChildContainer : function()
17644     {
17645         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17646     },
17647     
17648     /**
17649     * register a Navigation item
17650     * @param {Roo.bootstrap.NavItem} the navitem to add
17651     */
17652     register : function(item)
17653     {
17654         this.tabs.push( item);
17655         item.navId = this.navId; // not really needed..
17656         this.addBullet();
17657     
17658     },
17659     
17660     getActivePanel : function()
17661     {
17662         var r = false;
17663         Roo.each(this.tabs, function(t) {
17664             if (t.active) {
17665                 r = t;
17666                 return false;
17667             }
17668             return null;
17669         });
17670         return r;
17671         
17672     },
17673     getPanelByName : function(n)
17674     {
17675         var r = false;
17676         Roo.each(this.tabs, function(t) {
17677             if (t.tabId == n) {
17678                 r = t;
17679                 return false;
17680             }
17681             return null;
17682         });
17683         return r;
17684     },
17685     indexOfPanel : function(p)
17686     {
17687         var r = false;
17688         Roo.each(this.tabs, function(t,i) {
17689             if (t.tabId == p.tabId) {
17690                 r = i;
17691                 return false;
17692             }
17693             return null;
17694         });
17695         return r;
17696     },
17697     /**
17698      * show a specific panel
17699      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17700      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17701      */
17702     showPanel : function (pan)
17703     {
17704         if(this.transition || typeof(pan) == 'undefined'){
17705             Roo.log("waiting for the transitionend");
17706             return;
17707         }
17708         
17709         if (typeof(pan) == 'number') {
17710             pan = this.tabs[pan];
17711         }
17712         
17713         if (typeof(pan) == 'string') {
17714             pan = this.getPanelByName(pan);
17715         }
17716         
17717         var cur = this.getActivePanel();
17718         
17719         if(!pan || !cur){
17720             Roo.log('pan or acitve pan is undefined');
17721             return false;
17722         }
17723         
17724         if (pan.tabId == this.getActivePanel().tabId) {
17725             return true;
17726         }
17727         
17728         if (false === cur.fireEvent('beforedeactivate')) {
17729             return false;
17730         }
17731         
17732         if(this.bullets > 0 && !Roo.isTouch){
17733             this.setActiveBullet(this.indexOfPanel(pan));
17734         }
17735         
17736         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17737             
17738             this.transition = true;
17739             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17740             var lr = dir == 'next' ? 'left' : 'right';
17741             pan.el.addClass(dir); // or prev
17742             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17743             cur.el.addClass(lr); // or right
17744             pan.el.addClass(lr);
17745             
17746             var _this = this;
17747             cur.el.on('transitionend', function() {
17748                 Roo.log("trans end?");
17749                 
17750                 pan.el.removeClass([lr,dir]);
17751                 pan.setActive(true);
17752                 
17753                 cur.el.removeClass([lr]);
17754                 cur.setActive(false);
17755                 
17756                 _this.transition = false;
17757                 
17758             }, this, { single:  true } );
17759             
17760             return true;
17761         }
17762         
17763         cur.setActive(false);
17764         pan.setActive(true);
17765         
17766         return true;
17767         
17768     },
17769     showPanelNext : function()
17770     {
17771         var i = this.indexOfPanel(this.getActivePanel());
17772         
17773         if (i >= this.tabs.length - 1 && !this.autoslide) {
17774             return;
17775         }
17776         
17777         if (i >= this.tabs.length - 1 && this.autoslide) {
17778             i = -1;
17779         }
17780         
17781         this.showPanel(this.tabs[i+1]);
17782     },
17783     
17784     showPanelPrev : function()
17785     {
17786         var i = this.indexOfPanel(this.getActivePanel());
17787         
17788         if (i  < 1 && !this.autoslide) {
17789             return;
17790         }
17791         
17792         if (i < 1 && this.autoslide) {
17793             i = this.tabs.length;
17794         }
17795         
17796         this.showPanel(this.tabs[i-1]);
17797     },
17798     
17799     
17800     addBullet: function()
17801     {
17802         if(!this.bullets || Roo.isTouch){
17803             return;
17804         }
17805         var ctr = this.el.select('.carousel-bullets',true).first();
17806         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17807         var bullet = ctr.createChild({
17808             cls : 'bullet bullet-' + i
17809         },ctr.dom.lastChild);
17810         
17811         
17812         var _this = this;
17813         
17814         bullet.on('click', (function(e, el, o, ii, t){
17815
17816             e.preventDefault();
17817
17818             this.showPanel(ii);
17819
17820             if(this.autoslide && this.slideFn){
17821                 clearInterval(this.slideFn);
17822                 this.slideFn = window.setInterval(function() {
17823                     _this.showPanelNext();
17824                 }, this.timer);
17825             }
17826
17827         }).createDelegate(this, [i, bullet], true));
17828                 
17829         
17830     },
17831      
17832     setActiveBullet : function(i)
17833     {
17834         if(Roo.isTouch){
17835             return;
17836         }
17837         
17838         Roo.each(this.el.select('.bullet', true).elements, function(el){
17839             el.removeClass('selected');
17840         });
17841
17842         var bullet = this.el.select('.bullet-' + i, true).first();
17843         
17844         if(!bullet){
17845             return;
17846         }
17847         
17848         bullet.addClass('selected');
17849     }
17850     
17851     
17852   
17853 });
17854
17855  
17856
17857  
17858  
17859 Roo.apply(Roo.bootstrap.TabGroup, {
17860     
17861     groups: {},
17862      /**
17863     * register a Navigation Group
17864     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17865     */
17866     register : function(navgrp)
17867     {
17868         this.groups[navgrp.navId] = navgrp;
17869         
17870     },
17871     /**
17872     * fetch a Navigation Group based on the navigation ID
17873     * if one does not exist , it will get created.
17874     * @param {string} the navgroup to add
17875     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17876     */
17877     get: function(navId) {
17878         if (typeof(this.groups[navId]) == 'undefined') {
17879             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17880         }
17881         return this.groups[navId] ;
17882     }
17883     
17884     
17885     
17886 });
17887
17888  /*
17889  * - LGPL
17890  *
17891  * TabPanel
17892  * 
17893  */
17894
17895 /**
17896  * @class Roo.bootstrap.TabPanel
17897  * @extends Roo.bootstrap.Component
17898  * Bootstrap TabPanel class
17899  * @cfg {Boolean} active panel active
17900  * @cfg {String} html panel content
17901  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17902  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17903  * @cfg {String} href click to link..
17904  * 
17905  * 
17906  * @constructor
17907  * Create a new TabPanel
17908  * @param {Object} config The config object
17909  */
17910
17911 Roo.bootstrap.TabPanel = function(config){
17912     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17913     this.addEvents({
17914         /**
17915              * @event changed
17916              * Fires when the active status changes
17917              * @param {Roo.bootstrap.TabPanel} this
17918              * @param {Boolean} state the new state
17919             
17920          */
17921         'changed': true,
17922         /**
17923              * @event beforedeactivate
17924              * Fires before a tab is de-activated - can be used to do validation on a form.
17925              * @param {Roo.bootstrap.TabPanel} this
17926              * @return {Boolean} false if there is an error
17927             
17928          */
17929         'beforedeactivate': true
17930      });
17931     
17932     this.tabId = this.tabId || Roo.id();
17933   
17934 };
17935
17936 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17937     
17938     active: false,
17939     html: false,
17940     tabId: false,
17941     navId : false,
17942     href : '',
17943     
17944     getAutoCreate : function(){
17945         var cfg = {
17946             tag: 'div',
17947             // item is needed for carousel - not sure if it has any effect otherwise
17948             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17949             html: this.html || ''
17950         };
17951         
17952         if(this.active){
17953             cfg.cls += ' active';
17954         }
17955         
17956         if(this.tabId){
17957             cfg.tabId = this.tabId;
17958         }
17959         
17960         
17961         return cfg;
17962     },
17963     
17964     initEvents:  function()
17965     {
17966         var p = this.parent();
17967         
17968         this.navId = this.navId || p.navId;
17969         
17970         if (typeof(this.navId) != 'undefined') {
17971             // not really needed.. but just in case.. parent should be a NavGroup.
17972             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17973             
17974             tg.register(this);
17975             
17976             var i = tg.tabs.length - 1;
17977             
17978             if(this.active && tg.bullets > 0 && i < tg.bullets){
17979                 tg.setActiveBullet(i);
17980             }
17981         }
17982         
17983         this.el.on('click', this.onClick, this);
17984         
17985         if(Roo.isTouch){
17986             this.el.on("touchstart", this.onTouchStart, this);
17987             this.el.on("touchmove", this.onTouchMove, this);
17988             this.el.on("touchend", this.onTouchEnd, this);
17989         }
17990         
17991     },
17992     
17993     onRender : function(ct, position)
17994     {
17995         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17996     },
17997     
17998     setActive : function(state)
17999     {
18000         Roo.log("panel - set active " + this.tabId + "=" + state);
18001         
18002         this.active = state;
18003         if (!state) {
18004             this.el.removeClass('active');
18005             
18006         } else  if (!this.el.hasClass('active')) {
18007             this.el.addClass('active');
18008         }
18009         
18010         this.fireEvent('changed', this, state);
18011     },
18012     
18013     onClick : function(e)
18014     {
18015         e.preventDefault();
18016         
18017         if(!this.href.length){
18018             return;
18019         }
18020         
18021         window.location.href = this.href;
18022     },
18023     
18024     startX : 0,
18025     startY : 0,
18026     endX : 0,
18027     endY : 0,
18028     swiping : false,
18029     
18030     onTouchStart : function(e)
18031     {
18032         this.swiping = false;
18033         
18034         this.startX = e.browserEvent.touches[0].clientX;
18035         this.startY = e.browserEvent.touches[0].clientY;
18036     },
18037     
18038     onTouchMove : function(e)
18039     {
18040         this.swiping = true;
18041         
18042         this.endX = e.browserEvent.touches[0].clientX;
18043         this.endY = e.browserEvent.touches[0].clientY;
18044     },
18045     
18046     onTouchEnd : function(e)
18047     {
18048         if(!this.swiping){
18049             this.onClick(e);
18050             return;
18051         }
18052         
18053         var tabGroup = this.parent();
18054         
18055         if(this.endX > this.startX){ // swiping right
18056             tabGroup.showPanelPrev();
18057             return;
18058         }
18059         
18060         if(this.startX > this.endX){ // swiping left
18061             tabGroup.showPanelNext();
18062             return;
18063         }
18064     }
18065     
18066     
18067 });
18068  
18069
18070  
18071
18072  /*
18073  * - LGPL
18074  *
18075  * DateField
18076  * 
18077  */
18078
18079 /**
18080  * @class Roo.bootstrap.DateField
18081  * @extends Roo.bootstrap.Input
18082  * Bootstrap DateField class
18083  * @cfg {Number} weekStart default 0
18084  * @cfg {String} viewMode default empty, (months|years)
18085  * @cfg {String} minViewMode default empty, (months|years)
18086  * @cfg {Number} startDate default -Infinity
18087  * @cfg {Number} endDate default Infinity
18088  * @cfg {Boolean} todayHighlight default false
18089  * @cfg {Boolean} todayBtn default false
18090  * @cfg {Boolean} calendarWeeks default false
18091  * @cfg {Object} daysOfWeekDisabled default empty
18092  * @cfg {Boolean} singleMode default false (true | false)
18093  * 
18094  * @cfg {Boolean} keyboardNavigation default true
18095  * @cfg {String} language default en
18096  * 
18097  * @constructor
18098  * Create a new DateField
18099  * @param {Object} config The config object
18100  */
18101
18102 Roo.bootstrap.DateField = function(config){
18103     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18104      this.addEvents({
18105             /**
18106              * @event show
18107              * Fires when this field show.
18108              * @param {Roo.bootstrap.DateField} this
18109              * @param {Mixed} date The date value
18110              */
18111             show : true,
18112             /**
18113              * @event show
18114              * Fires when this field hide.
18115              * @param {Roo.bootstrap.DateField} this
18116              * @param {Mixed} date The date value
18117              */
18118             hide : true,
18119             /**
18120              * @event select
18121              * Fires when select a date.
18122              * @param {Roo.bootstrap.DateField} this
18123              * @param {Mixed} date The date value
18124              */
18125             select : true,
18126             /**
18127              * @event beforeselect
18128              * Fires when before select a date.
18129              * @param {Roo.bootstrap.DateField} this
18130              * @param {Mixed} date The date value
18131              */
18132             beforeselect : true
18133         });
18134 };
18135
18136 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18137     
18138     /**
18139      * @cfg {String} format
18140      * The default date format string which can be overriden for localization support.  The format must be
18141      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18142      */
18143     format : "m/d/y",
18144     /**
18145      * @cfg {String} altFormats
18146      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18147      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18148      */
18149     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18150     
18151     weekStart : 0,
18152     
18153     viewMode : '',
18154     
18155     minViewMode : '',
18156     
18157     todayHighlight : false,
18158     
18159     todayBtn: false,
18160     
18161     language: 'en',
18162     
18163     keyboardNavigation: true,
18164     
18165     calendarWeeks: false,
18166     
18167     startDate: -Infinity,
18168     
18169     endDate: Infinity,
18170     
18171     daysOfWeekDisabled: [],
18172     
18173     _events: [],
18174     
18175     singleMode : false,
18176     
18177     UTCDate: function()
18178     {
18179         return new Date(Date.UTC.apply(Date, arguments));
18180     },
18181     
18182     UTCToday: function()
18183     {
18184         var today = new Date();
18185         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18186     },
18187     
18188     getDate: function() {
18189             var d = this.getUTCDate();
18190             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18191     },
18192     
18193     getUTCDate: function() {
18194             return this.date;
18195     },
18196     
18197     setDate: function(d) {
18198             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18199     },
18200     
18201     setUTCDate: function(d) {
18202             this.date = d;
18203             this.setValue(this.formatDate(this.date));
18204     },
18205         
18206     onRender: function(ct, position)
18207     {
18208         
18209         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18210         
18211         this.language = this.language || 'en';
18212         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18213         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18214         
18215         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18216         this.format = this.format || 'm/d/y';
18217         this.isInline = false;
18218         this.isInput = true;
18219         this.component = this.el.select('.add-on', true).first() || false;
18220         this.component = (this.component && this.component.length === 0) ? false : this.component;
18221         this.hasInput = this.component && this.inputEl().length;
18222         
18223         if (typeof(this.minViewMode === 'string')) {
18224             switch (this.minViewMode) {
18225                 case 'months':
18226                     this.minViewMode = 1;
18227                     break;
18228                 case 'years':
18229                     this.minViewMode = 2;
18230                     break;
18231                 default:
18232                     this.minViewMode = 0;
18233                     break;
18234             }
18235         }
18236         
18237         if (typeof(this.viewMode === 'string')) {
18238             switch (this.viewMode) {
18239                 case 'months':
18240                     this.viewMode = 1;
18241                     break;
18242                 case 'years':
18243                     this.viewMode = 2;
18244                     break;
18245                 default:
18246                     this.viewMode = 0;
18247                     break;
18248             }
18249         }
18250                 
18251         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18252         
18253 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18254         
18255         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18256         
18257         this.picker().on('mousedown', this.onMousedown, this);
18258         this.picker().on('click', this.onClick, this);
18259         
18260         this.picker().addClass('datepicker-dropdown');
18261         
18262         this.startViewMode = this.viewMode;
18263         
18264         if(this.singleMode){
18265             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18266                 v.setVisibilityMode(Roo.Element.DISPLAY);
18267                 v.hide();
18268             });
18269             
18270             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18271                 v.setStyle('width', '189px');
18272             });
18273         }
18274         
18275         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18276             if(!this.calendarWeeks){
18277                 v.remove();
18278                 return;
18279             }
18280             
18281             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18282             v.attr('colspan', function(i, val){
18283                 return parseInt(val) + 1;
18284             });
18285         });
18286                         
18287         
18288         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18289         
18290         this.setStartDate(this.startDate);
18291         this.setEndDate(this.endDate);
18292         
18293         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18294         
18295         this.fillDow();
18296         this.fillMonths();
18297         this.update();
18298         this.showMode();
18299         
18300         if(this.isInline) {
18301             this.show();
18302         }
18303     },
18304     
18305     picker : function()
18306     {
18307         return this.pickerEl;
18308 //        return this.el.select('.datepicker', true).first();
18309     },
18310     
18311     fillDow: function()
18312     {
18313         var dowCnt = this.weekStart;
18314         
18315         var dow = {
18316             tag: 'tr',
18317             cn: [
18318                 
18319             ]
18320         };
18321         
18322         if(this.calendarWeeks){
18323             dow.cn.push({
18324                 tag: 'th',
18325                 cls: 'cw',
18326                 html: '&nbsp;'
18327             })
18328         }
18329         
18330         while (dowCnt < this.weekStart + 7) {
18331             dow.cn.push({
18332                 tag: 'th',
18333                 cls: 'dow',
18334                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18335             });
18336         }
18337         
18338         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18339     },
18340     
18341     fillMonths: function()
18342     {    
18343         var i = 0;
18344         var months = this.picker().select('>.datepicker-months td', true).first();
18345         
18346         months.dom.innerHTML = '';
18347         
18348         while (i < 12) {
18349             var month = {
18350                 tag: 'span',
18351                 cls: 'month',
18352                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18353             };
18354             
18355             months.createChild(month);
18356         }
18357         
18358     },
18359     
18360     update: function()
18361     {
18362         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;
18363         
18364         if (this.date < this.startDate) {
18365             this.viewDate = new Date(this.startDate);
18366         } else if (this.date > this.endDate) {
18367             this.viewDate = new Date(this.endDate);
18368         } else {
18369             this.viewDate = new Date(this.date);
18370         }
18371         
18372         this.fill();
18373     },
18374     
18375     fill: function() 
18376     {
18377         var d = new Date(this.viewDate),
18378                 year = d.getUTCFullYear(),
18379                 month = d.getUTCMonth(),
18380                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18381                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18382                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18383                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18384                 currentDate = this.date && this.date.valueOf(),
18385                 today = this.UTCToday();
18386         
18387         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18388         
18389 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18390         
18391 //        this.picker.select('>tfoot th.today').
18392 //                                              .text(dates[this.language].today)
18393 //                                              .toggle(this.todayBtn !== false);
18394     
18395         this.updateNavArrows();
18396         this.fillMonths();
18397                                                 
18398         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18399         
18400         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18401          
18402         prevMonth.setUTCDate(day);
18403         
18404         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18405         
18406         var nextMonth = new Date(prevMonth);
18407         
18408         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18409         
18410         nextMonth = nextMonth.valueOf();
18411         
18412         var fillMonths = false;
18413         
18414         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18415         
18416         while(prevMonth.valueOf() < nextMonth) {
18417             var clsName = '';
18418             
18419             if (prevMonth.getUTCDay() === this.weekStart) {
18420                 if(fillMonths){
18421                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18422                 }
18423                     
18424                 fillMonths = {
18425                     tag: 'tr',
18426                     cn: []
18427                 };
18428                 
18429                 if(this.calendarWeeks){
18430                     // ISO 8601: First week contains first thursday.
18431                     // ISO also states week starts on Monday, but we can be more abstract here.
18432                     var
18433                     // Start of current week: based on weekstart/current date
18434                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18435                     // Thursday of this week
18436                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18437                     // First Thursday of year, year from thursday
18438                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18439                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18440                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18441                     
18442                     fillMonths.cn.push({
18443                         tag: 'td',
18444                         cls: 'cw',
18445                         html: calWeek
18446                     });
18447                 }
18448             }
18449             
18450             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18451                 clsName += ' old';
18452             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18453                 clsName += ' new';
18454             }
18455             if (this.todayHighlight &&
18456                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18457                 prevMonth.getUTCMonth() == today.getMonth() &&
18458                 prevMonth.getUTCDate() == today.getDate()) {
18459                 clsName += ' today';
18460             }
18461             
18462             if (currentDate && prevMonth.valueOf() === currentDate) {
18463                 clsName += ' active';
18464             }
18465             
18466             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18467                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18468                     clsName += ' disabled';
18469             }
18470             
18471             fillMonths.cn.push({
18472                 tag: 'td',
18473                 cls: 'day ' + clsName,
18474                 html: prevMonth.getDate()
18475             });
18476             
18477             prevMonth.setDate(prevMonth.getDate()+1);
18478         }
18479           
18480         var currentYear = this.date && this.date.getUTCFullYear();
18481         var currentMonth = this.date && this.date.getUTCMonth();
18482         
18483         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18484         
18485         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18486             v.removeClass('active');
18487             
18488             if(currentYear === year && k === currentMonth){
18489                 v.addClass('active');
18490             }
18491             
18492             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18493                 v.addClass('disabled');
18494             }
18495             
18496         });
18497         
18498         
18499         year = parseInt(year/10, 10) * 10;
18500         
18501         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18502         
18503         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18504         
18505         year -= 1;
18506         for (var i = -1; i < 11; i++) {
18507             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18508                 tag: 'span',
18509                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18510                 html: year
18511             });
18512             
18513             year += 1;
18514         }
18515     },
18516     
18517     showMode: function(dir) 
18518     {
18519         if (dir) {
18520             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18521         }
18522         
18523         Roo.each(this.picker().select('>div',true).elements, function(v){
18524             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18525             v.hide();
18526         });
18527         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18528     },
18529     
18530     place: function()
18531     {
18532         if(this.isInline) {
18533             return;
18534         }
18535         
18536         this.picker().removeClass(['bottom', 'top']);
18537         
18538         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18539             /*
18540              * place to the top of element!
18541              *
18542              */
18543             
18544             this.picker().addClass('top');
18545             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18546             
18547             return;
18548         }
18549         
18550         this.picker().addClass('bottom');
18551         
18552         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18553     },
18554     
18555     parseDate : function(value)
18556     {
18557         if(!value || value instanceof Date){
18558             return value;
18559         }
18560         var v = Date.parseDate(value, this.format);
18561         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18562             v = Date.parseDate(value, 'Y-m-d');
18563         }
18564         if(!v && this.altFormats){
18565             if(!this.altFormatsArray){
18566                 this.altFormatsArray = this.altFormats.split("|");
18567             }
18568             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18569                 v = Date.parseDate(value, this.altFormatsArray[i]);
18570             }
18571         }
18572         return v;
18573     },
18574     
18575     formatDate : function(date, fmt)
18576     {   
18577         return (!date || !(date instanceof Date)) ?
18578         date : date.dateFormat(fmt || this.format);
18579     },
18580     
18581     onFocus : function()
18582     {
18583         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18584         this.show();
18585     },
18586     
18587     onBlur : function()
18588     {
18589         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18590         
18591         var d = this.inputEl().getValue();
18592         
18593         this.setValue(d);
18594                 
18595         this.hide();
18596     },
18597     
18598     show : function()
18599     {
18600         this.picker().show();
18601         this.update();
18602         this.place();
18603         
18604         this.fireEvent('show', this, this.date);
18605     },
18606     
18607     hide : function()
18608     {
18609         if(this.isInline) {
18610             return;
18611         }
18612         this.picker().hide();
18613         this.viewMode = this.startViewMode;
18614         this.showMode();
18615         
18616         this.fireEvent('hide', this, this.date);
18617         
18618     },
18619     
18620     onMousedown: function(e)
18621     {
18622         e.stopPropagation();
18623         e.preventDefault();
18624     },
18625     
18626     keyup: function(e)
18627     {
18628         Roo.bootstrap.DateField.superclass.keyup.call(this);
18629         this.update();
18630     },
18631
18632     setValue: function(v)
18633     {
18634         if(this.fireEvent('beforeselect', this, v) !== false){
18635             var d = new Date(this.parseDate(v) ).clearTime();
18636         
18637             if(isNaN(d.getTime())){
18638                 this.date = this.viewDate = '';
18639                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18640                 return;
18641             }
18642
18643             v = this.formatDate(d);
18644
18645             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18646
18647             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18648
18649             this.update();
18650
18651             this.fireEvent('select', this, this.date);
18652         }
18653     },
18654     
18655     getValue: function()
18656     {
18657         return this.formatDate(this.date);
18658     },
18659     
18660     fireKey: function(e)
18661     {
18662         if (!this.picker().isVisible()){
18663             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18664                 this.show();
18665             }
18666             return;
18667         }
18668         
18669         var dateChanged = false,
18670         dir, day, month,
18671         newDate, newViewDate;
18672         
18673         switch(e.keyCode){
18674             case 27: // escape
18675                 this.hide();
18676                 e.preventDefault();
18677                 break;
18678             case 37: // left
18679             case 39: // right
18680                 if (!this.keyboardNavigation) {
18681                     break;
18682                 }
18683                 dir = e.keyCode == 37 ? -1 : 1;
18684                 
18685                 if (e.ctrlKey){
18686                     newDate = this.moveYear(this.date, dir);
18687                     newViewDate = this.moveYear(this.viewDate, dir);
18688                 } else if (e.shiftKey){
18689                     newDate = this.moveMonth(this.date, dir);
18690                     newViewDate = this.moveMonth(this.viewDate, dir);
18691                 } else {
18692                     newDate = new Date(this.date);
18693                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18694                     newViewDate = new Date(this.viewDate);
18695                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18696                 }
18697                 if (this.dateWithinRange(newDate)){
18698                     this.date = newDate;
18699                     this.viewDate = newViewDate;
18700                     this.setValue(this.formatDate(this.date));
18701 //                    this.update();
18702                     e.preventDefault();
18703                     dateChanged = true;
18704                 }
18705                 break;
18706             case 38: // up
18707             case 40: // down
18708                 if (!this.keyboardNavigation) {
18709                     break;
18710                 }
18711                 dir = e.keyCode == 38 ? -1 : 1;
18712                 if (e.ctrlKey){
18713                     newDate = this.moveYear(this.date, dir);
18714                     newViewDate = this.moveYear(this.viewDate, dir);
18715                 } else if (e.shiftKey){
18716                     newDate = this.moveMonth(this.date, dir);
18717                     newViewDate = this.moveMonth(this.viewDate, dir);
18718                 } else {
18719                     newDate = new Date(this.date);
18720                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18721                     newViewDate = new Date(this.viewDate);
18722                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18723                 }
18724                 if (this.dateWithinRange(newDate)){
18725                     this.date = newDate;
18726                     this.viewDate = newViewDate;
18727                     this.setValue(this.formatDate(this.date));
18728 //                    this.update();
18729                     e.preventDefault();
18730                     dateChanged = true;
18731                 }
18732                 break;
18733             case 13: // enter
18734                 this.setValue(this.formatDate(this.date));
18735                 this.hide();
18736                 e.preventDefault();
18737                 break;
18738             case 9: // tab
18739                 this.setValue(this.formatDate(this.date));
18740                 this.hide();
18741                 break;
18742             case 16: // shift
18743             case 17: // ctrl
18744             case 18: // alt
18745                 break;
18746             default :
18747                 this.hide();
18748                 
18749         }
18750     },
18751     
18752     
18753     onClick: function(e) 
18754     {
18755         e.stopPropagation();
18756         e.preventDefault();
18757         
18758         var target = e.getTarget();
18759         
18760         if(target.nodeName.toLowerCase() === 'i'){
18761             target = Roo.get(target).dom.parentNode;
18762         }
18763         
18764         var nodeName = target.nodeName;
18765         var className = target.className;
18766         var html = target.innerHTML;
18767         //Roo.log(nodeName);
18768         
18769         switch(nodeName.toLowerCase()) {
18770             case 'th':
18771                 switch(className) {
18772                     case 'switch':
18773                         this.showMode(1);
18774                         break;
18775                     case 'prev':
18776                     case 'next':
18777                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18778                         switch(this.viewMode){
18779                                 case 0:
18780                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18781                                         break;
18782                                 case 1:
18783                                 case 2:
18784                                         this.viewDate = this.moveYear(this.viewDate, dir);
18785                                         break;
18786                         }
18787                         this.fill();
18788                         break;
18789                     case 'today':
18790                         var date = new Date();
18791                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18792 //                        this.fill()
18793                         this.setValue(this.formatDate(this.date));
18794                         
18795                         this.hide();
18796                         break;
18797                 }
18798                 break;
18799             case 'span':
18800                 if (className.indexOf('disabled') < 0) {
18801                     this.viewDate.setUTCDate(1);
18802                     if (className.indexOf('month') > -1) {
18803                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18804                     } else {
18805                         var year = parseInt(html, 10) || 0;
18806                         this.viewDate.setUTCFullYear(year);
18807                         
18808                     }
18809                     
18810                     if(this.singleMode){
18811                         this.setValue(this.formatDate(this.viewDate));
18812                         this.hide();
18813                         return;
18814                     }
18815                     
18816                     this.showMode(-1);
18817                     this.fill();
18818                 }
18819                 break;
18820                 
18821             case 'td':
18822                 //Roo.log(className);
18823                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18824                     var day = parseInt(html, 10) || 1;
18825                     var year = this.viewDate.getUTCFullYear(),
18826                         month = this.viewDate.getUTCMonth();
18827
18828                     if (className.indexOf('old') > -1) {
18829                         if(month === 0 ){
18830                             month = 11;
18831                             year -= 1;
18832                         }else{
18833                             month -= 1;
18834                         }
18835                     } else if (className.indexOf('new') > -1) {
18836                         if (month == 11) {
18837                             month = 0;
18838                             year += 1;
18839                         } else {
18840                             month += 1;
18841                         }
18842                     }
18843                     //Roo.log([year,month,day]);
18844                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18845                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18846 //                    this.fill();
18847                     //Roo.log(this.formatDate(this.date));
18848                     this.setValue(this.formatDate(this.date));
18849                     this.hide();
18850                 }
18851                 break;
18852         }
18853     },
18854     
18855     setStartDate: function(startDate)
18856     {
18857         this.startDate = startDate || -Infinity;
18858         if (this.startDate !== -Infinity) {
18859             this.startDate = this.parseDate(this.startDate);
18860         }
18861         this.update();
18862         this.updateNavArrows();
18863     },
18864
18865     setEndDate: function(endDate)
18866     {
18867         this.endDate = endDate || Infinity;
18868         if (this.endDate !== Infinity) {
18869             this.endDate = this.parseDate(this.endDate);
18870         }
18871         this.update();
18872         this.updateNavArrows();
18873     },
18874     
18875     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18876     {
18877         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18878         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18879             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18880         }
18881         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18882             return parseInt(d, 10);
18883         });
18884         this.update();
18885         this.updateNavArrows();
18886     },
18887     
18888     updateNavArrows: function() 
18889     {
18890         if(this.singleMode){
18891             return;
18892         }
18893         
18894         var d = new Date(this.viewDate),
18895         year = d.getUTCFullYear(),
18896         month = d.getUTCMonth();
18897         
18898         Roo.each(this.picker().select('.prev', true).elements, function(v){
18899             v.show();
18900             switch (this.viewMode) {
18901                 case 0:
18902
18903                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18904                         v.hide();
18905                     }
18906                     break;
18907                 case 1:
18908                 case 2:
18909                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18910                         v.hide();
18911                     }
18912                     break;
18913             }
18914         });
18915         
18916         Roo.each(this.picker().select('.next', true).elements, function(v){
18917             v.show();
18918             switch (this.viewMode) {
18919                 case 0:
18920
18921                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18922                         v.hide();
18923                     }
18924                     break;
18925                 case 1:
18926                 case 2:
18927                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18928                         v.hide();
18929                     }
18930                     break;
18931             }
18932         })
18933     },
18934     
18935     moveMonth: function(date, dir)
18936     {
18937         if (!dir) {
18938             return date;
18939         }
18940         var new_date = new Date(date.valueOf()),
18941         day = new_date.getUTCDate(),
18942         month = new_date.getUTCMonth(),
18943         mag = Math.abs(dir),
18944         new_month, test;
18945         dir = dir > 0 ? 1 : -1;
18946         if (mag == 1){
18947             test = dir == -1
18948             // If going back one month, make sure month is not current month
18949             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18950             ? function(){
18951                 return new_date.getUTCMonth() == month;
18952             }
18953             // If going forward one month, make sure month is as expected
18954             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18955             : function(){
18956                 return new_date.getUTCMonth() != new_month;
18957             };
18958             new_month = month + dir;
18959             new_date.setUTCMonth(new_month);
18960             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18961             if (new_month < 0 || new_month > 11) {
18962                 new_month = (new_month + 12) % 12;
18963             }
18964         } else {
18965             // For magnitudes >1, move one month at a time...
18966             for (var i=0; i<mag; i++) {
18967                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18968                 new_date = this.moveMonth(new_date, dir);
18969             }
18970             // ...then reset the day, keeping it in the new month
18971             new_month = new_date.getUTCMonth();
18972             new_date.setUTCDate(day);
18973             test = function(){
18974                 return new_month != new_date.getUTCMonth();
18975             };
18976         }
18977         // Common date-resetting loop -- if date is beyond end of month, make it
18978         // end of month
18979         while (test()){
18980             new_date.setUTCDate(--day);
18981             new_date.setUTCMonth(new_month);
18982         }
18983         return new_date;
18984     },
18985
18986     moveYear: function(date, dir)
18987     {
18988         return this.moveMonth(date, dir*12);
18989     },
18990
18991     dateWithinRange: function(date)
18992     {
18993         return date >= this.startDate && date <= this.endDate;
18994     },
18995
18996     
18997     remove: function() 
18998     {
18999         this.picker().remove();
19000     },
19001     
19002     validateValue : function(value)
19003     {
19004         if(value.length < 1)  {
19005             if(this.allowBlank){
19006                 return true;
19007             }
19008             return false;
19009         }
19010         
19011         if(value.length < this.minLength){
19012             return false;
19013         }
19014         if(value.length > this.maxLength){
19015             return false;
19016         }
19017         if(this.vtype){
19018             var vt = Roo.form.VTypes;
19019             if(!vt[this.vtype](value, this)){
19020                 return false;
19021             }
19022         }
19023         if(typeof this.validator == "function"){
19024             var msg = this.validator(value);
19025             if(msg !== true){
19026                 return false;
19027             }
19028         }
19029         
19030         if(this.regex && !this.regex.test(value)){
19031             return false;
19032         }
19033         
19034         if(typeof(this.parseDate(value)) == 'undefined'){
19035             return false;
19036         }
19037         
19038         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19039             return false;
19040         }      
19041         
19042         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19043             return false;
19044         } 
19045         
19046         
19047         return true;
19048     }
19049    
19050 });
19051
19052 Roo.apply(Roo.bootstrap.DateField,  {
19053     
19054     head : {
19055         tag: 'thead',
19056         cn: [
19057         {
19058             tag: 'tr',
19059             cn: [
19060             {
19061                 tag: 'th',
19062                 cls: 'prev',
19063                 html: '<i class="fa fa-arrow-left"/>'
19064             },
19065             {
19066                 tag: 'th',
19067                 cls: 'switch',
19068                 colspan: '5'
19069             },
19070             {
19071                 tag: 'th',
19072                 cls: 'next',
19073                 html: '<i class="fa fa-arrow-right"/>'
19074             }
19075
19076             ]
19077         }
19078         ]
19079     },
19080     
19081     content : {
19082         tag: 'tbody',
19083         cn: [
19084         {
19085             tag: 'tr',
19086             cn: [
19087             {
19088                 tag: 'td',
19089                 colspan: '7'
19090             }
19091             ]
19092         }
19093         ]
19094     },
19095     
19096     footer : {
19097         tag: 'tfoot',
19098         cn: [
19099         {
19100             tag: 'tr',
19101             cn: [
19102             {
19103                 tag: 'th',
19104                 colspan: '7',
19105                 cls: 'today'
19106             }
19107                     
19108             ]
19109         }
19110         ]
19111     },
19112     
19113     dates:{
19114         en: {
19115             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19116             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19117             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19118             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19119             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19120             today: "Today"
19121         }
19122     },
19123     
19124     modes: [
19125     {
19126         clsName: 'days',
19127         navFnc: 'Month',
19128         navStep: 1
19129     },
19130     {
19131         clsName: 'months',
19132         navFnc: 'FullYear',
19133         navStep: 1
19134     },
19135     {
19136         clsName: 'years',
19137         navFnc: 'FullYear',
19138         navStep: 10
19139     }]
19140 });
19141
19142 Roo.apply(Roo.bootstrap.DateField,  {
19143   
19144     template : {
19145         tag: 'div',
19146         cls: 'datepicker dropdown-menu roo-dynamic',
19147         cn: [
19148         {
19149             tag: 'div',
19150             cls: 'datepicker-days',
19151             cn: [
19152             {
19153                 tag: 'table',
19154                 cls: 'table-condensed',
19155                 cn:[
19156                 Roo.bootstrap.DateField.head,
19157                 {
19158                     tag: 'tbody'
19159                 },
19160                 Roo.bootstrap.DateField.footer
19161                 ]
19162             }
19163             ]
19164         },
19165         {
19166             tag: 'div',
19167             cls: 'datepicker-months',
19168             cn: [
19169             {
19170                 tag: 'table',
19171                 cls: 'table-condensed',
19172                 cn:[
19173                 Roo.bootstrap.DateField.head,
19174                 Roo.bootstrap.DateField.content,
19175                 Roo.bootstrap.DateField.footer
19176                 ]
19177             }
19178             ]
19179         },
19180         {
19181             tag: 'div',
19182             cls: 'datepicker-years',
19183             cn: [
19184             {
19185                 tag: 'table',
19186                 cls: 'table-condensed',
19187                 cn:[
19188                 Roo.bootstrap.DateField.head,
19189                 Roo.bootstrap.DateField.content,
19190                 Roo.bootstrap.DateField.footer
19191                 ]
19192             }
19193             ]
19194         }
19195         ]
19196     }
19197 });
19198
19199  
19200
19201  /*
19202  * - LGPL
19203  *
19204  * TimeField
19205  * 
19206  */
19207
19208 /**
19209  * @class Roo.bootstrap.TimeField
19210  * @extends Roo.bootstrap.Input
19211  * Bootstrap DateField class
19212  * 
19213  * 
19214  * @constructor
19215  * Create a new TimeField
19216  * @param {Object} config The config object
19217  */
19218
19219 Roo.bootstrap.TimeField = function(config){
19220     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19221     this.addEvents({
19222             /**
19223              * @event show
19224              * Fires when this field show.
19225              * @param {Roo.bootstrap.DateField} thisthis
19226              * @param {Mixed} date The date value
19227              */
19228             show : true,
19229             /**
19230              * @event show
19231              * Fires when this field hide.
19232              * @param {Roo.bootstrap.DateField} this
19233              * @param {Mixed} date The date value
19234              */
19235             hide : true,
19236             /**
19237              * @event select
19238              * Fires when select a date.
19239              * @param {Roo.bootstrap.DateField} this
19240              * @param {Mixed} date The date value
19241              */
19242             select : true
19243         });
19244 };
19245
19246 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19247     
19248     /**
19249      * @cfg {String} format
19250      * The default time format string which can be overriden for localization support.  The format must be
19251      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19252      */
19253     format : "H:i",
19254        
19255     onRender: function(ct, position)
19256     {
19257         
19258         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19259                 
19260         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19261         
19262         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19263         
19264         this.pop = this.picker().select('>.datepicker-time',true).first();
19265         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19266         
19267         this.picker().on('mousedown', this.onMousedown, this);
19268         this.picker().on('click', this.onClick, this);
19269         
19270         this.picker().addClass('datepicker-dropdown');
19271     
19272         this.fillTime();
19273         this.update();
19274             
19275         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19276         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19277         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19278         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19279         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19280         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19281
19282     },
19283     
19284     fireKey: function(e){
19285         if (!this.picker().isVisible()){
19286             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19287                 this.show();
19288             }
19289             return;
19290         }
19291
19292         e.preventDefault();
19293         
19294         switch(e.keyCode){
19295             case 27: // escape
19296                 this.hide();
19297                 break;
19298             case 37: // left
19299             case 39: // right
19300                 this.onTogglePeriod();
19301                 break;
19302             case 38: // up
19303                 this.onIncrementMinutes();
19304                 break;
19305             case 40: // down
19306                 this.onDecrementMinutes();
19307                 break;
19308             case 13: // enter
19309             case 9: // tab
19310                 this.setTime();
19311                 break;
19312         }
19313     },
19314     
19315     onClick: function(e) {
19316         e.stopPropagation();
19317         e.preventDefault();
19318     },
19319     
19320     picker : function()
19321     {
19322         return this.el.select('.datepicker', true).first();
19323     },
19324     
19325     fillTime: function()
19326     {    
19327         var time = this.pop.select('tbody', true).first();
19328         
19329         time.dom.innerHTML = '';
19330         
19331         time.createChild({
19332             tag: 'tr',
19333             cn: [
19334                 {
19335                     tag: 'td',
19336                     cn: [
19337                         {
19338                             tag: 'a',
19339                             href: '#',
19340                             cls: 'btn',
19341                             cn: [
19342                                 {
19343                                     tag: 'span',
19344                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19345                                 }
19346                             ]
19347                         } 
19348                     ]
19349                 },
19350                 {
19351                     tag: 'td',
19352                     cls: 'separator'
19353                 },
19354                 {
19355                     tag: 'td',
19356                     cn: [
19357                         {
19358                             tag: 'a',
19359                             href: '#',
19360                             cls: 'btn',
19361                             cn: [
19362                                 {
19363                                     tag: 'span',
19364                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19365                                 }
19366                             ]
19367                         }
19368                     ]
19369                 },
19370                 {
19371                     tag: 'td',
19372                     cls: 'separator'
19373                 }
19374             ]
19375         });
19376         
19377         time.createChild({
19378             tag: 'tr',
19379             cn: [
19380                 {
19381                     tag: 'td',
19382                     cn: [
19383                         {
19384                             tag: 'span',
19385                             cls: 'timepicker-hour',
19386                             html: '00'
19387                         }  
19388                     ]
19389                 },
19390                 {
19391                     tag: 'td',
19392                     cls: 'separator',
19393                     html: ':'
19394                 },
19395                 {
19396                     tag: 'td',
19397                     cn: [
19398                         {
19399                             tag: 'span',
19400                             cls: 'timepicker-minute',
19401                             html: '00'
19402                         }  
19403                     ]
19404                 },
19405                 {
19406                     tag: 'td',
19407                     cls: 'separator'
19408                 },
19409                 {
19410                     tag: 'td',
19411                     cn: [
19412                         {
19413                             tag: 'button',
19414                             type: 'button',
19415                             cls: 'btn btn-primary period',
19416                             html: 'AM'
19417                             
19418                         }
19419                     ]
19420                 }
19421             ]
19422         });
19423         
19424         time.createChild({
19425             tag: 'tr',
19426             cn: [
19427                 {
19428                     tag: 'td',
19429                     cn: [
19430                         {
19431                             tag: 'a',
19432                             href: '#',
19433                             cls: 'btn',
19434                             cn: [
19435                                 {
19436                                     tag: 'span',
19437                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19438                                 }
19439                             ]
19440                         }
19441                     ]
19442                 },
19443                 {
19444                     tag: 'td',
19445                     cls: 'separator'
19446                 },
19447                 {
19448                     tag: 'td',
19449                     cn: [
19450                         {
19451                             tag: 'a',
19452                             href: '#',
19453                             cls: 'btn',
19454                             cn: [
19455                                 {
19456                                     tag: 'span',
19457                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19458                                 }
19459                             ]
19460                         }
19461                     ]
19462                 },
19463                 {
19464                     tag: 'td',
19465                     cls: 'separator'
19466                 }
19467             ]
19468         });
19469         
19470     },
19471     
19472     update: function()
19473     {
19474         
19475         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19476         
19477         this.fill();
19478     },
19479     
19480     fill: function() 
19481     {
19482         var hours = this.time.getHours();
19483         var minutes = this.time.getMinutes();
19484         var period = 'AM';
19485         
19486         if(hours > 11){
19487             period = 'PM';
19488         }
19489         
19490         if(hours == 0){
19491             hours = 12;
19492         }
19493         
19494         
19495         if(hours > 12){
19496             hours = hours - 12;
19497         }
19498         
19499         if(hours < 10){
19500             hours = '0' + hours;
19501         }
19502         
19503         if(minutes < 10){
19504             minutes = '0' + minutes;
19505         }
19506         
19507         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19508         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19509         this.pop.select('button', true).first().dom.innerHTML = period;
19510         
19511     },
19512     
19513     place: function()
19514     {   
19515         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19516         
19517         var cls = ['bottom'];
19518         
19519         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19520             cls.pop();
19521             cls.push('top');
19522         }
19523         
19524         cls.push('right');
19525         
19526         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19527             cls.pop();
19528             cls.push('left');
19529         }
19530         
19531         this.picker().addClass(cls.join('-'));
19532         
19533         var _this = this;
19534         
19535         Roo.each(cls, function(c){
19536             if(c == 'bottom'){
19537                 _this.picker().setTop(_this.inputEl().getHeight());
19538                 return;
19539             }
19540             if(c == 'top'){
19541                 _this.picker().setTop(0 - _this.picker().getHeight());
19542                 return;
19543             }
19544             
19545             if(c == 'left'){
19546                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19547                 return;
19548             }
19549             if(c == 'right'){
19550                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19551                 return;
19552             }
19553         });
19554         
19555     },
19556   
19557     onFocus : function()
19558     {
19559         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19560         this.show();
19561     },
19562     
19563     onBlur : function()
19564     {
19565         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19566         this.hide();
19567     },
19568     
19569     show : function()
19570     {
19571         this.picker().show();
19572         this.pop.show();
19573         this.update();
19574         this.place();
19575         
19576         this.fireEvent('show', this, this.date);
19577     },
19578     
19579     hide : function()
19580     {
19581         this.picker().hide();
19582         this.pop.hide();
19583         
19584         this.fireEvent('hide', this, this.date);
19585     },
19586     
19587     setTime : function()
19588     {
19589         this.hide();
19590         this.setValue(this.time.format(this.format));
19591         
19592         this.fireEvent('select', this, this.date);
19593         
19594         
19595     },
19596     
19597     onMousedown: function(e){
19598         e.stopPropagation();
19599         e.preventDefault();
19600     },
19601     
19602     onIncrementHours: function()
19603     {
19604         Roo.log('onIncrementHours');
19605         this.time = this.time.add(Date.HOUR, 1);
19606         this.update();
19607         
19608     },
19609     
19610     onDecrementHours: function()
19611     {
19612         Roo.log('onDecrementHours');
19613         this.time = this.time.add(Date.HOUR, -1);
19614         this.update();
19615     },
19616     
19617     onIncrementMinutes: function()
19618     {
19619         Roo.log('onIncrementMinutes');
19620         this.time = this.time.add(Date.MINUTE, 1);
19621         this.update();
19622     },
19623     
19624     onDecrementMinutes: function()
19625     {
19626         Roo.log('onDecrementMinutes');
19627         this.time = this.time.add(Date.MINUTE, -1);
19628         this.update();
19629     },
19630     
19631     onTogglePeriod: function()
19632     {
19633         Roo.log('onTogglePeriod');
19634         this.time = this.time.add(Date.HOUR, 12);
19635         this.update();
19636     }
19637     
19638    
19639 });
19640
19641 Roo.apply(Roo.bootstrap.TimeField,  {
19642     
19643     content : {
19644         tag: 'tbody',
19645         cn: [
19646             {
19647                 tag: 'tr',
19648                 cn: [
19649                 {
19650                     tag: 'td',
19651                     colspan: '7'
19652                 }
19653                 ]
19654             }
19655         ]
19656     },
19657     
19658     footer : {
19659         tag: 'tfoot',
19660         cn: [
19661             {
19662                 tag: 'tr',
19663                 cn: [
19664                 {
19665                     tag: 'th',
19666                     colspan: '7',
19667                     cls: '',
19668                     cn: [
19669                         {
19670                             tag: 'button',
19671                             cls: 'btn btn-info ok',
19672                             html: 'OK'
19673                         }
19674                     ]
19675                 }
19676
19677                 ]
19678             }
19679         ]
19680     }
19681 });
19682
19683 Roo.apply(Roo.bootstrap.TimeField,  {
19684   
19685     template : {
19686         tag: 'div',
19687         cls: 'datepicker dropdown-menu',
19688         cn: [
19689             {
19690                 tag: 'div',
19691                 cls: 'datepicker-time',
19692                 cn: [
19693                 {
19694                     tag: 'table',
19695                     cls: 'table-condensed',
19696                     cn:[
19697                     Roo.bootstrap.TimeField.content,
19698                     Roo.bootstrap.TimeField.footer
19699                     ]
19700                 }
19701                 ]
19702             }
19703         ]
19704     }
19705 });
19706
19707  
19708
19709  /*
19710  * - LGPL
19711  *
19712  * MonthField
19713  * 
19714  */
19715
19716 /**
19717  * @class Roo.bootstrap.MonthField
19718  * @extends Roo.bootstrap.Input
19719  * Bootstrap MonthField class
19720  * 
19721  * @cfg {String} language default en
19722  * 
19723  * @constructor
19724  * Create a new MonthField
19725  * @param {Object} config The config object
19726  */
19727
19728 Roo.bootstrap.MonthField = function(config){
19729     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19730     
19731     this.addEvents({
19732         /**
19733          * @event show
19734          * Fires when this field show.
19735          * @param {Roo.bootstrap.MonthField} this
19736          * @param {Mixed} date The date value
19737          */
19738         show : true,
19739         /**
19740          * @event show
19741          * Fires when this field hide.
19742          * @param {Roo.bootstrap.MonthField} this
19743          * @param {Mixed} date The date value
19744          */
19745         hide : true,
19746         /**
19747          * @event select
19748          * Fires when select a date.
19749          * @param {Roo.bootstrap.MonthField} this
19750          * @param {String} oldvalue The old value
19751          * @param {String} newvalue The new value
19752          */
19753         select : true
19754     });
19755 };
19756
19757 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19758     
19759     onRender: function(ct, position)
19760     {
19761         
19762         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19763         
19764         this.language = this.language || 'en';
19765         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19766         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19767         
19768         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19769         this.isInline = false;
19770         this.isInput = true;
19771         this.component = this.el.select('.add-on', true).first() || false;
19772         this.component = (this.component && this.component.length === 0) ? false : this.component;
19773         this.hasInput = this.component && this.inputEL().length;
19774         
19775         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19776         
19777         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19778         
19779         this.picker().on('mousedown', this.onMousedown, this);
19780         this.picker().on('click', this.onClick, this);
19781         
19782         this.picker().addClass('datepicker-dropdown');
19783         
19784         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19785             v.setStyle('width', '189px');
19786         });
19787         
19788         this.fillMonths();
19789         
19790         this.update();
19791         
19792         if(this.isInline) {
19793             this.show();
19794         }
19795         
19796     },
19797     
19798     setValue: function(v, suppressEvent)
19799     {   
19800         var o = this.getValue();
19801         
19802         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19803         
19804         this.update();
19805
19806         if(suppressEvent !== true){
19807             this.fireEvent('select', this, o, v);
19808         }
19809         
19810     },
19811     
19812     getValue: function()
19813     {
19814         return this.value;
19815     },
19816     
19817     onClick: function(e) 
19818     {
19819         e.stopPropagation();
19820         e.preventDefault();
19821         
19822         var target = e.getTarget();
19823         
19824         if(target.nodeName.toLowerCase() === 'i'){
19825             target = Roo.get(target).dom.parentNode;
19826         }
19827         
19828         var nodeName = target.nodeName;
19829         var className = target.className;
19830         var html = target.innerHTML;
19831         
19832         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19833             return;
19834         }
19835         
19836         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19837         
19838         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19839         
19840         this.hide();
19841                         
19842     },
19843     
19844     picker : function()
19845     {
19846         return this.pickerEl;
19847     },
19848     
19849     fillMonths: function()
19850     {    
19851         var i = 0;
19852         var months = this.picker().select('>.datepicker-months td', true).first();
19853         
19854         months.dom.innerHTML = '';
19855         
19856         while (i < 12) {
19857             var month = {
19858                 tag: 'span',
19859                 cls: 'month',
19860                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19861             };
19862             
19863             months.createChild(month);
19864         }
19865         
19866     },
19867     
19868     update: function()
19869     {
19870         var _this = this;
19871         
19872         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19873             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19874         }
19875         
19876         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19877             e.removeClass('active');
19878             
19879             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19880                 e.addClass('active');
19881             }
19882         })
19883     },
19884     
19885     place: function()
19886     {
19887         if(this.isInline) {
19888             return;
19889         }
19890         
19891         this.picker().removeClass(['bottom', 'top']);
19892         
19893         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19894             /*
19895              * place to the top of element!
19896              *
19897              */
19898             
19899             this.picker().addClass('top');
19900             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19901             
19902             return;
19903         }
19904         
19905         this.picker().addClass('bottom');
19906         
19907         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19908     },
19909     
19910     onFocus : function()
19911     {
19912         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19913         this.show();
19914     },
19915     
19916     onBlur : function()
19917     {
19918         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19919         
19920         var d = this.inputEl().getValue();
19921         
19922         this.setValue(d);
19923                 
19924         this.hide();
19925     },
19926     
19927     show : function()
19928     {
19929         this.picker().show();
19930         this.picker().select('>.datepicker-months', true).first().show();
19931         this.update();
19932         this.place();
19933         
19934         this.fireEvent('show', this, this.date);
19935     },
19936     
19937     hide : function()
19938     {
19939         if(this.isInline) {
19940             return;
19941         }
19942         this.picker().hide();
19943         this.fireEvent('hide', this, this.date);
19944         
19945     },
19946     
19947     onMousedown: function(e)
19948     {
19949         e.stopPropagation();
19950         e.preventDefault();
19951     },
19952     
19953     keyup: function(e)
19954     {
19955         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19956         this.update();
19957     },
19958
19959     fireKey: function(e)
19960     {
19961         if (!this.picker().isVisible()){
19962             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19963                 this.show();
19964             }
19965             return;
19966         }
19967         
19968         var dir;
19969         
19970         switch(e.keyCode){
19971             case 27: // escape
19972                 this.hide();
19973                 e.preventDefault();
19974                 break;
19975             case 37: // left
19976             case 39: // right
19977                 dir = e.keyCode == 37 ? -1 : 1;
19978                 
19979                 this.vIndex = this.vIndex + dir;
19980                 
19981                 if(this.vIndex < 0){
19982                     this.vIndex = 0;
19983                 }
19984                 
19985                 if(this.vIndex > 11){
19986                     this.vIndex = 11;
19987                 }
19988                 
19989                 if(isNaN(this.vIndex)){
19990                     this.vIndex = 0;
19991                 }
19992                 
19993                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19994                 
19995                 break;
19996             case 38: // up
19997             case 40: // down
19998                 
19999                 dir = e.keyCode == 38 ? -1 : 1;
20000                 
20001                 this.vIndex = this.vIndex + dir * 4;
20002                 
20003                 if(this.vIndex < 0){
20004                     this.vIndex = 0;
20005                 }
20006                 
20007                 if(this.vIndex > 11){
20008                     this.vIndex = 11;
20009                 }
20010                 
20011                 if(isNaN(this.vIndex)){
20012                     this.vIndex = 0;
20013                 }
20014                 
20015                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20016                 break;
20017                 
20018             case 13: // enter
20019                 
20020                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20021                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20022                 }
20023                 
20024                 this.hide();
20025                 e.preventDefault();
20026                 break;
20027             case 9: // tab
20028                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20029                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20030                 }
20031                 this.hide();
20032                 break;
20033             case 16: // shift
20034             case 17: // ctrl
20035             case 18: // alt
20036                 break;
20037             default :
20038                 this.hide();
20039                 
20040         }
20041     },
20042     
20043     remove: function() 
20044     {
20045         this.picker().remove();
20046     }
20047    
20048 });
20049
20050 Roo.apply(Roo.bootstrap.MonthField,  {
20051     
20052     content : {
20053         tag: 'tbody',
20054         cn: [
20055         {
20056             tag: 'tr',
20057             cn: [
20058             {
20059                 tag: 'td',
20060                 colspan: '7'
20061             }
20062             ]
20063         }
20064         ]
20065     },
20066     
20067     dates:{
20068         en: {
20069             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20070             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20071         }
20072     }
20073 });
20074
20075 Roo.apply(Roo.bootstrap.MonthField,  {
20076   
20077     template : {
20078         tag: 'div',
20079         cls: 'datepicker dropdown-menu roo-dynamic',
20080         cn: [
20081             {
20082                 tag: 'div',
20083                 cls: 'datepicker-months',
20084                 cn: [
20085                 {
20086                     tag: 'table',
20087                     cls: 'table-condensed',
20088                     cn:[
20089                         Roo.bootstrap.DateField.content
20090                     ]
20091                 }
20092                 ]
20093             }
20094         ]
20095     }
20096 });
20097
20098  
20099
20100  
20101  /*
20102  * - LGPL
20103  *
20104  * CheckBox
20105  * 
20106  */
20107
20108 /**
20109  * @class Roo.bootstrap.CheckBox
20110  * @extends Roo.bootstrap.Input
20111  * Bootstrap CheckBox class
20112  * 
20113  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20114  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20115  * @cfg {String} boxLabel The text that appears beside the checkbox
20116  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20117  * @cfg {Boolean} checked initnal the element
20118  * @cfg {Boolean} inline inline the element (default false)
20119  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20120  * 
20121  * @constructor
20122  * Create a new CheckBox
20123  * @param {Object} config The config object
20124  */
20125
20126 Roo.bootstrap.CheckBox = function(config){
20127     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20128    
20129     this.addEvents({
20130         /**
20131         * @event check
20132         * Fires when the element is checked or unchecked.
20133         * @param {Roo.bootstrap.CheckBox} this This input
20134         * @param {Boolean} checked The new checked value
20135         */
20136        check : true
20137     });
20138     
20139 };
20140
20141 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20142   
20143     inputType: 'checkbox',
20144     inputValue: 1,
20145     valueOff: 0,
20146     boxLabel: false,
20147     checked: false,
20148     weight : false,
20149     inline: false,
20150     
20151     getAutoCreate : function()
20152     {
20153         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20154         
20155         var id = Roo.id();
20156         
20157         var cfg = {};
20158         
20159         cfg.cls = 'form-group ' + this.inputType; //input-group
20160         
20161         if(this.inline){
20162             cfg.cls += ' ' + this.inputType + '-inline';
20163         }
20164         
20165         var input =  {
20166             tag: 'input',
20167             id : id,
20168             type : this.inputType,
20169             value : this.inputValue,
20170             cls : 'roo-' + this.inputType, //'form-box',
20171             placeholder : this.placeholder || ''
20172             
20173         };
20174         
20175         if(this.inputType != 'radio'){
20176             var hidden =  {
20177                 tag: 'input',
20178                 type : 'hidden',
20179                 cls : 'roo-hidden-value',
20180                 value : this.checked ? this.valueOff : this.inputValue
20181             };
20182         }
20183         
20184             
20185         if (this.weight) { // Validity check?
20186             cfg.cls += " " + this.inputType + "-" + this.weight;
20187         }
20188         
20189         if (this.disabled) {
20190             input.disabled=true;
20191         }
20192         
20193         if(this.checked){
20194             input.checked = this.checked;
20195             
20196         }
20197         
20198         
20199         if (this.name) {
20200             
20201             input.name = this.name;
20202             
20203             if(this.inputType != 'radio'){
20204                 hidden.name = this.name;
20205                 input.name = '_hidden_' + this.name;
20206             }
20207         }
20208         
20209         if (this.size) {
20210             input.cls += ' input-' + this.size;
20211         }
20212         
20213         var settings=this;
20214         
20215         ['xs','sm','md','lg'].map(function(size){
20216             if (settings[size]) {
20217                 cfg.cls += ' col-' + size + '-' + settings[size];
20218             }
20219         });
20220         
20221         var inputblock = input;
20222          
20223         if (this.before || this.after) {
20224             
20225             inputblock = {
20226                 cls : 'input-group',
20227                 cn :  [] 
20228             };
20229             
20230             if (this.before) {
20231                 inputblock.cn.push({
20232                     tag :'span',
20233                     cls : 'input-group-addon',
20234                     html : this.before
20235                 });
20236             }
20237             
20238             inputblock.cn.push(input);
20239             
20240             if(this.inputType != 'radio'){
20241                 inputblock.cn.push(hidden);
20242             }
20243             
20244             if (this.after) {
20245                 inputblock.cn.push({
20246                     tag :'span',
20247                     cls : 'input-group-addon',
20248                     html : this.after
20249                 });
20250             }
20251             
20252         }
20253         
20254         if (align ==='left' && this.fieldLabel.length) {
20255 //                Roo.log("left and has label");
20256             cfg.cn = [
20257                 {
20258                     tag: 'label',
20259                     'for' :  id,
20260                     cls : 'control-label',
20261                     html : this.fieldLabel
20262
20263                 },
20264                 {
20265                     cls : "", 
20266                     cn: [
20267                         inputblock
20268                     ]
20269                 }
20270             ];
20271             
20272             if(this.labelWidth > 12){
20273                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20274             }
20275             
20276             if(this.labelWidth < 13 && this.labelmd == 0){
20277                 this.labelmd = this.labelWidth;
20278             }
20279             
20280             if(this.labellg > 0){
20281                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20282                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20283             }
20284             
20285             if(this.labelmd > 0){
20286                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20287                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20288             }
20289             
20290             if(this.labelsm > 0){
20291                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20292                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20293             }
20294             
20295             if(this.labelxs > 0){
20296                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20297                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20298             }
20299             
20300         } else if ( this.fieldLabel.length) {
20301 //                Roo.log(" label");
20302                 cfg.cn = [
20303                    
20304                     {
20305                         tag: this.boxLabel ? 'span' : 'label',
20306                         'for': id,
20307                         cls: 'control-label box-input-label',
20308                         //cls : 'input-group-addon',
20309                         html : this.fieldLabel
20310                         
20311                     },
20312                     
20313                     inputblock
20314                     
20315                 ];
20316
20317         } else {
20318             
20319 //                Roo.log(" no label && no align");
20320                 cfg.cn = [  inputblock ] ;
20321                 
20322                 
20323         }
20324         
20325         if(this.boxLabel){
20326              var boxLabelCfg = {
20327                 tag: 'label',
20328                 //'for': id, // box label is handled by onclick - so no for...
20329                 cls: 'box-label',
20330                 html: this.boxLabel
20331             };
20332             
20333             if(this.tooltip){
20334                 boxLabelCfg.tooltip = this.tooltip;
20335             }
20336              
20337             cfg.cn.push(boxLabelCfg);
20338         }
20339         
20340         if(this.inputType != 'radio'){
20341             cfg.cn.push(hidden);
20342         }
20343         
20344         return cfg;
20345         
20346     },
20347     
20348     /**
20349      * return the real input element.
20350      */
20351     inputEl: function ()
20352     {
20353         return this.el.select('input.roo-' + this.inputType,true).first();
20354     },
20355     hiddenEl: function ()
20356     {
20357         return this.el.select('input.roo-hidden-value',true).first();
20358     },
20359     
20360     labelEl: function()
20361     {
20362         return this.el.select('label.control-label',true).first();
20363     },
20364     /* depricated... */
20365     
20366     label: function()
20367     {
20368         return this.labelEl();
20369     },
20370     
20371     boxLabelEl: function()
20372     {
20373         return this.el.select('label.box-label',true).first();
20374     },
20375     
20376     initEvents : function()
20377     {
20378 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20379         
20380         this.inputEl().on('click', this.onClick,  this);
20381         
20382         if (this.boxLabel) { 
20383             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20384         }
20385         
20386         this.startValue = this.getValue();
20387         
20388         if(this.groupId){
20389             Roo.bootstrap.CheckBox.register(this);
20390         }
20391     },
20392     
20393     onClick : function()
20394     {   
20395         this.setChecked(!this.checked);
20396     },
20397     
20398     setChecked : function(state,suppressEvent)
20399     {
20400         this.startValue = this.getValue();
20401
20402         if(this.inputType == 'radio'){
20403             
20404             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20405                 e.dom.checked = false;
20406             });
20407             
20408             this.inputEl().dom.checked = true;
20409             
20410             this.inputEl().dom.value = this.inputValue;
20411             
20412             if(suppressEvent !== true){
20413                 this.fireEvent('check', this, true);
20414             }
20415             
20416             this.validate();
20417             
20418             return;
20419         }
20420         
20421         this.checked = state;
20422         
20423         this.inputEl().dom.checked = state;
20424         
20425         
20426         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20427         
20428         if(suppressEvent !== true){
20429             this.fireEvent('check', this, state);
20430         }
20431         
20432         this.validate();
20433     },
20434     
20435     getValue : function()
20436     {
20437         if(this.inputType == 'radio'){
20438             return this.getGroupValue();
20439         }
20440         
20441         return this.hiddenEl().dom.value;
20442         
20443     },
20444     
20445     getGroupValue : function()
20446     {
20447         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20448             return '';
20449         }
20450         
20451         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20452     },
20453     
20454     setValue : function(v,suppressEvent)
20455     {
20456         if(this.inputType == 'radio'){
20457             this.setGroupValue(v, suppressEvent);
20458             return;
20459         }
20460         
20461         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20462         
20463         this.validate();
20464     },
20465     
20466     setGroupValue : function(v, suppressEvent)
20467     {
20468         this.startValue = this.getValue();
20469         
20470         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20471             e.dom.checked = false;
20472             
20473             if(e.dom.value == v){
20474                 e.dom.checked = true;
20475             }
20476         });
20477         
20478         if(suppressEvent !== true){
20479             this.fireEvent('check', this, true);
20480         }
20481
20482         this.validate();
20483         
20484         return;
20485     },
20486     
20487     validate : function()
20488     {
20489         if(
20490                 this.disabled || 
20491                 (this.inputType == 'radio' && this.validateRadio()) ||
20492                 (this.inputType == 'checkbox' && this.validateCheckbox())
20493         ){
20494             this.markValid();
20495             return true;
20496         }
20497         
20498         this.markInvalid();
20499         return false;
20500     },
20501     
20502     validateRadio : function()
20503     {
20504         if(this.allowBlank){
20505             return true;
20506         }
20507         
20508         var valid = false;
20509         
20510         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20511             if(!e.dom.checked){
20512                 return;
20513             }
20514             
20515             valid = true;
20516             
20517             return false;
20518         });
20519         
20520         return valid;
20521     },
20522     
20523     validateCheckbox : function()
20524     {
20525         if(!this.groupId){
20526             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20527             //return (this.getValue() == this.inputValue) ? true : false;
20528         }
20529         
20530         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20531         
20532         if(!group){
20533             return false;
20534         }
20535         
20536         var r = false;
20537         
20538         for(var i in group){
20539             if(r){
20540                 break;
20541             }
20542             
20543             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20544         }
20545         
20546         return r;
20547     },
20548     
20549     /**
20550      * Mark this field as valid
20551      */
20552     markValid : function()
20553     {
20554         var _this = this;
20555         
20556         this.fireEvent('valid', this);
20557         
20558         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20559         
20560         if(this.groupId){
20561             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20562         }
20563         
20564         if(label){
20565             label.markValid();
20566         }
20567
20568         if(this.inputType == 'radio'){
20569             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20570                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20571                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20572             });
20573             
20574             return;
20575         }
20576
20577         if(!this.groupId){
20578             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20579             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20580             return;
20581         }
20582         
20583         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20584         
20585         if(!group){
20586             return;
20587         }
20588         
20589         for(var i in group){
20590             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20591             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20592         }
20593     },
20594     
20595      /**
20596      * Mark this field as invalid
20597      * @param {String} msg The validation message
20598      */
20599     markInvalid : function(msg)
20600     {
20601         if(this.allowBlank){
20602             return;
20603         }
20604         
20605         var _this = this;
20606         
20607         this.fireEvent('invalid', this, msg);
20608         
20609         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20610         
20611         if(this.groupId){
20612             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20613         }
20614         
20615         if(label){
20616             label.markInvalid();
20617         }
20618             
20619         if(this.inputType == 'radio'){
20620             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20621                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20622                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20623             });
20624             
20625             return;
20626         }
20627         
20628         if(!this.groupId){
20629             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20630             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20631             return;
20632         }
20633         
20634         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20635         
20636         if(!group){
20637             return;
20638         }
20639         
20640         for(var i in group){
20641             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20642             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20643         }
20644         
20645     },
20646     
20647     clearInvalid : function()
20648     {
20649         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20650         
20651         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20652         
20653         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20654         
20655         if (label) {
20656             label.iconEl.removeClass(label.validClass);
20657             label.iconEl.removeClass(label.invalidClass);
20658         }
20659     },
20660     
20661     disable : function()
20662     {
20663         if(this.inputType != 'radio'){
20664             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20665             return;
20666         }
20667         
20668         var _this = this;
20669         
20670         if(this.rendered){
20671             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20672                 _this.getActionEl().addClass(this.disabledClass);
20673                 e.dom.disabled = true;
20674             });
20675         }
20676         
20677         this.disabled = true;
20678         this.fireEvent("disable", this);
20679         return this;
20680     },
20681
20682     enable : function()
20683     {
20684         if(this.inputType != 'radio'){
20685             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20686             return;
20687         }
20688         
20689         var _this = this;
20690         
20691         if(this.rendered){
20692             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20693                 _this.getActionEl().removeClass(this.disabledClass);
20694                 e.dom.disabled = false;
20695             });
20696         }
20697         
20698         this.disabled = false;
20699         this.fireEvent("enable", this);
20700         return this;
20701     }
20702
20703 });
20704
20705 Roo.apply(Roo.bootstrap.CheckBox, {
20706     
20707     groups: {},
20708     
20709      /**
20710     * register a CheckBox Group
20711     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20712     */
20713     register : function(checkbox)
20714     {
20715         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20716             this.groups[checkbox.groupId] = {};
20717         }
20718         
20719         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20720             return;
20721         }
20722         
20723         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20724         
20725     },
20726     /**
20727     * fetch a CheckBox Group based on the group ID
20728     * @param {string} the group ID
20729     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20730     */
20731     get: function(groupId) {
20732         if (typeof(this.groups[groupId]) == 'undefined') {
20733             return false;
20734         }
20735         
20736         return this.groups[groupId] ;
20737     }
20738     
20739     
20740 });
20741 /*
20742  * - LGPL
20743  *
20744  * RadioItem
20745  * 
20746  */
20747
20748 /**
20749  * @class Roo.bootstrap.Radio
20750  * @extends Roo.bootstrap.Component
20751  * Bootstrap Radio class
20752  * @cfg {String} boxLabel - the label associated
20753  * @cfg {String} value - the value of radio
20754  * 
20755  * @constructor
20756  * Create a new Radio
20757  * @param {Object} config The config object
20758  */
20759 Roo.bootstrap.Radio = function(config){
20760     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20761     
20762 };
20763
20764 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20765     
20766     boxLabel : '',
20767     
20768     value : '',
20769     
20770     getAutoCreate : function()
20771     {
20772         var cfg = {
20773             tag : 'div',
20774             cls : 'form-group radio',
20775             cn : [
20776                 {
20777                     tag : 'label',
20778                     cls : 'box-label',
20779                     html : this.boxLabel
20780                 }
20781             ]
20782         };
20783         
20784         return cfg;
20785     },
20786     
20787     initEvents : function() 
20788     {
20789         this.parent().register(this);
20790         
20791         this.el.on('click', this.onClick, this);
20792         
20793     },
20794     
20795     onClick : function()
20796     {
20797         this.setChecked(true);
20798     },
20799     
20800     setChecked : function(state, suppressEvent)
20801     {
20802         this.parent().setValue(this.value, suppressEvent);
20803         
20804     },
20805     
20806     setBoxLabel : function(v)
20807     {
20808         this.boxLabel = v;
20809         
20810         if(this.rendered){
20811             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20812         }
20813     }
20814     
20815 });
20816  
20817
20818  /*
20819  * - LGPL
20820  *
20821  * Input
20822  * 
20823  */
20824
20825 /**
20826  * @class Roo.bootstrap.SecurePass
20827  * @extends Roo.bootstrap.Input
20828  * Bootstrap SecurePass class
20829  *
20830  * 
20831  * @constructor
20832  * Create a new SecurePass
20833  * @param {Object} config The config object
20834  */
20835  
20836 Roo.bootstrap.SecurePass = function (config) {
20837     // these go here, so the translation tool can replace them..
20838     this.errors = {
20839         PwdEmpty: "Please type a password, and then retype it to confirm.",
20840         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20841         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20842         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20843         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20844         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20845         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20846         TooWeak: "Your password is Too Weak."
20847     },
20848     this.meterLabel = "Password strength:";
20849     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20850     this.meterClass = [
20851         "roo-password-meter-tooweak", 
20852         "roo-password-meter-weak", 
20853         "roo-password-meter-medium", 
20854         "roo-password-meter-strong", 
20855         "roo-password-meter-grey"
20856     ];
20857     
20858     this.errors = {};
20859     
20860     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20861 }
20862
20863 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20864     /**
20865      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20866      * {
20867      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20868      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20869      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20870      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20871      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20872      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20873      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20874      * })
20875      */
20876     // private
20877     
20878     meterWidth: 300,
20879     errorMsg :'',    
20880     errors: false,
20881     imageRoot: '/',
20882     /**
20883      * @cfg {String/Object} Label for the strength meter (defaults to
20884      * 'Password strength:')
20885      */
20886     // private
20887     meterLabel: '',
20888     /**
20889      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20890      * ['Weak', 'Medium', 'Strong'])
20891      */
20892     // private    
20893     pwdStrengths: false,    
20894     // private
20895     strength: 0,
20896     // private
20897     _lastPwd: null,
20898     // private
20899     kCapitalLetter: 0,
20900     kSmallLetter: 1,
20901     kDigit: 2,
20902     kPunctuation: 3,
20903     
20904     insecure: false,
20905     // private
20906     initEvents: function ()
20907     {
20908         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20909
20910         if (this.el.is('input[type=password]') && Roo.isSafari) {
20911             this.el.on('keydown', this.SafariOnKeyDown, this);
20912         }
20913
20914         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20915     },
20916     // private
20917     onRender: function (ct, position)
20918     {
20919         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20920         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20921         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20922
20923         this.trigger.createChild({
20924                    cn: [
20925                     {
20926                     //id: 'PwdMeter',
20927                     tag: 'div',
20928                     cls: 'roo-password-meter-grey col-xs-12',
20929                     style: {
20930                         //width: 0,
20931                         //width: this.meterWidth + 'px'                                                
20932                         }
20933                     },
20934                     {                            
20935                          cls: 'roo-password-meter-text'                          
20936                     }
20937                 ]            
20938         });
20939
20940          
20941         if (this.hideTrigger) {
20942             this.trigger.setDisplayed(false);
20943         }
20944         this.setSize(this.width || '', this.height || '');
20945     },
20946     // private
20947     onDestroy: function ()
20948     {
20949         if (this.trigger) {
20950             this.trigger.removeAllListeners();
20951             this.trigger.remove();
20952         }
20953         if (this.wrap) {
20954             this.wrap.remove();
20955         }
20956         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20957     },
20958     // private
20959     checkStrength: function ()
20960     {
20961         var pwd = this.inputEl().getValue();
20962         if (pwd == this._lastPwd) {
20963             return;
20964         }
20965
20966         var strength;
20967         if (this.ClientSideStrongPassword(pwd)) {
20968             strength = 3;
20969         } else if (this.ClientSideMediumPassword(pwd)) {
20970             strength = 2;
20971         } else if (this.ClientSideWeakPassword(pwd)) {
20972             strength = 1;
20973         } else {
20974             strength = 0;
20975         }
20976         
20977         Roo.log('strength1: ' + strength);
20978         
20979         //var pm = this.trigger.child('div/div/div').dom;
20980         var pm = this.trigger.child('div/div');
20981         pm.removeClass(this.meterClass);
20982         pm.addClass(this.meterClass[strength]);
20983                 
20984         
20985         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20986                 
20987         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20988         
20989         this._lastPwd = pwd;
20990     },
20991     reset: function ()
20992     {
20993         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20994         
20995         this._lastPwd = '';
20996         
20997         var pm = this.trigger.child('div/div');
20998         pm.removeClass(this.meterClass);
20999         pm.addClass('roo-password-meter-grey');        
21000         
21001         
21002         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21003         
21004         pt.innerHTML = '';
21005         this.inputEl().dom.type='password';
21006     },
21007     // private
21008     validateValue: function (value)
21009     {
21010         
21011         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21012             return false;
21013         }
21014         if (value.length == 0) {
21015             if (this.allowBlank) {
21016                 this.clearInvalid();
21017                 return true;
21018             }
21019
21020             this.markInvalid(this.errors.PwdEmpty);
21021             this.errorMsg = this.errors.PwdEmpty;
21022             return false;
21023         }
21024         
21025         if(this.insecure){
21026             return true;
21027         }
21028         
21029         if ('[\x21-\x7e]*'.match(value)) {
21030             this.markInvalid(this.errors.PwdBadChar);
21031             this.errorMsg = this.errors.PwdBadChar;
21032             return false;
21033         }
21034         if (value.length < 6) {
21035             this.markInvalid(this.errors.PwdShort);
21036             this.errorMsg = this.errors.PwdShort;
21037             return false;
21038         }
21039         if (value.length > 16) {
21040             this.markInvalid(this.errors.PwdLong);
21041             this.errorMsg = this.errors.PwdLong;
21042             return false;
21043         }
21044         var strength;
21045         if (this.ClientSideStrongPassword(value)) {
21046             strength = 3;
21047         } else if (this.ClientSideMediumPassword(value)) {
21048             strength = 2;
21049         } else if (this.ClientSideWeakPassword(value)) {
21050             strength = 1;
21051         } else {
21052             strength = 0;
21053         }
21054
21055         
21056         if (strength < 2) {
21057             //this.markInvalid(this.errors.TooWeak);
21058             this.errorMsg = this.errors.TooWeak;
21059             //return false;
21060         }
21061         
21062         
21063         console.log('strength2: ' + strength);
21064         
21065         //var pm = this.trigger.child('div/div/div').dom;
21066         
21067         var pm = this.trigger.child('div/div');
21068         pm.removeClass(this.meterClass);
21069         pm.addClass(this.meterClass[strength]);
21070                 
21071         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21072                 
21073         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21074         
21075         this.errorMsg = ''; 
21076         return true;
21077     },
21078     // private
21079     CharacterSetChecks: function (type)
21080     {
21081         this.type = type;
21082         this.fResult = false;
21083     },
21084     // private
21085     isctype: function (character, type)
21086     {
21087         switch (type) {  
21088             case this.kCapitalLetter:
21089                 if (character >= 'A' && character <= 'Z') {
21090                     return true;
21091                 }
21092                 break;
21093             
21094             case this.kSmallLetter:
21095                 if (character >= 'a' && character <= 'z') {
21096                     return true;
21097                 }
21098                 break;
21099             
21100             case this.kDigit:
21101                 if (character >= '0' && character <= '9') {
21102                     return true;
21103                 }
21104                 break;
21105             
21106             case this.kPunctuation:
21107                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21108                     return true;
21109                 }
21110                 break;
21111             
21112             default:
21113                 return false;
21114         }
21115
21116     },
21117     // private
21118     IsLongEnough: function (pwd, size)
21119     {
21120         return !(pwd == null || isNaN(size) || pwd.length < size);
21121     },
21122     // private
21123     SpansEnoughCharacterSets: function (word, nb)
21124     {
21125         if (!this.IsLongEnough(word, nb))
21126         {
21127             return false;
21128         }
21129
21130         var characterSetChecks = new Array(
21131             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21132             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21133         );
21134         
21135         for (var index = 0; index < word.length; ++index) {
21136             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21137                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21138                     characterSetChecks[nCharSet].fResult = true;
21139                     break;
21140                 }
21141             }
21142         }
21143
21144         var nCharSets = 0;
21145         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21146             if (characterSetChecks[nCharSet].fResult) {
21147                 ++nCharSets;
21148             }
21149         }
21150
21151         if (nCharSets < nb) {
21152             return false;
21153         }
21154         return true;
21155     },
21156     // private
21157     ClientSideStrongPassword: function (pwd)
21158     {
21159         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21160     },
21161     // private
21162     ClientSideMediumPassword: function (pwd)
21163     {
21164         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21165     },
21166     // private
21167     ClientSideWeakPassword: function (pwd)
21168     {
21169         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21170     }
21171           
21172 })//<script type="text/javascript">
21173
21174 /*
21175  * Based  Ext JS Library 1.1.1
21176  * Copyright(c) 2006-2007, Ext JS, LLC.
21177  * LGPL
21178  *
21179  */
21180  
21181 /**
21182  * @class Roo.HtmlEditorCore
21183  * @extends Roo.Component
21184  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21185  *
21186  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21187  */
21188
21189 Roo.HtmlEditorCore = function(config){
21190     
21191     
21192     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21193     
21194     
21195     this.addEvents({
21196         /**
21197          * @event initialize
21198          * Fires when the editor is fully initialized (including the iframe)
21199          * @param {Roo.HtmlEditorCore} this
21200          */
21201         initialize: true,
21202         /**
21203          * @event activate
21204          * Fires when the editor is first receives the focus. Any insertion must wait
21205          * until after this event.
21206          * @param {Roo.HtmlEditorCore} this
21207          */
21208         activate: true,
21209          /**
21210          * @event beforesync
21211          * Fires before the textarea is updated with content from the editor iframe. Return false
21212          * to cancel the sync.
21213          * @param {Roo.HtmlEditorCore} this
21214          * @param {String} html
21215          */
21216         beforesync: true,
21217          /**
21218          * @event beforepush
21219          * Fires before the iframe editor is updated with content from the textarea. Return false
21220          * to cancel the push.
21221          * @param {Roo.HtmlEditorCore} this
21222          * @param {String} html
21223          */
21224         beforepush: true,
21225          /**
21226          * @event sync
21227          * Fires when the textarea is updated with content from the editor iframe.
21228          * @param {Roo.HtmlEditorCore} this
21229          * @param {String} html
21230          */
21231         sync: true,
21232          /**
21233          * @event push
21234          * Fires when the iframe editor is updated with content from the textarea.
21235          * @param {Roo.HtmlEditorCore} this
21236          * @param {String} html
21237          */
21238         push: true,
21239         
21240         /**
21241          * @event editorevent
21242          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21243          * @param {Roo.HtmlEditorCore} this
21244          */
21245         editorevent: true
21246         
21247     });
21248     
21249     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21250     
21251     // defaults : white / black...
21252     this.applyBlacklists();
21253     
21254     
21255     
21256 };
21257
21258
21259 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21260
21261
21262      /**
21263      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21264      */
21265     
21266     owner : false,
21267     
21268      /**
21269      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21270      *                        Roo.resizable.
21271      */
21272     resizable : false,
21273      /**
21274      * @cfg {Number} height (in pixels)
21275      */   
21276     height: 300,
21277    /**
21278      * @cfg {Number} width (in pixels)
21279      */   
21280     width: 500,
21281     
21282     /**
21283      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21284      * 
21285      */
21286     stylesheets: false,
21287     
21288     // id of frame..
21289     frameId: false,
21290     
21291     // private properties
21292     validationEvent : false,
21293     deferHeight: true,
21294     initialized : false,
21295     activated : false,
21296     sourceEditMode : false,
21297     onFocus : Roo.emptyFn,
21298     iframePad:3,
21299     hideMode:'offsets',
21300     
21301     clearUp: true,
21302     
21303     // blacklist + whitelisted elements..
21304     black: false,
21305     white: false,
21306      
21307     bodyCls : '',
21308
21309     /**
21310      * Protected method that will not generally be called directly. It
21311      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21312      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21313      */
21314     getDocMarkup : function(){
21315         // body styles..
21316         var st = '';
21317         
21318         // inherit styels from page...?? 
21319         if (this.stylesheets === false) {
21320             
21321             Roo.get(document.head).select('style').each(function(node) {
21322                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21323             });
21324             
21325             Roo.get(document.head).select('link').each(function(node) { 
21326                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21327             });
21328             
21329         } else if (!this.stylesheets.length) {
21330                 // simple..
21331                 st = '<style type="text/css">' +
21332                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21333                    '</style>';
21334         } else { 
21335             st = '<style type="text/css">' +
21336                     this.stylesheets +
21337                 '</style>';
21338         }
21339         
21340         st +=  '<style type="text/css">' +
21341             'IMG { cursor: pointer } ' +
21342         '</style>';
21343
21344         var cls = 'roo-htmleditor-body';
21345         
21346         if(this.bodyCls.length){
21347             cls += ' ' + this.bodyCls;
21348         }
21349         
21350         return '<html><head>' + st  +
21351             //<style type="text/css">' +
21352             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21353             //'</style>' +
21354             ' </head><body class="' +  cls + '"></body></html>';
21355     },
21356
21357     // private
21358     onRender : function(ct, position)
21359     {
21360         var _t = this;
21361         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21362         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21363         
21364         
21365         this.el.dom.style.border = '0 none';
21366         this.el.dom.setAttribute('tabIndex', -1);
21367         this.el.addClass('x-hidden hide');
21368         
21369         
21370         
21371         if(Roo.isIE){ // fix IE 1px bogus margin
21372             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21373         }
21374        
21375         
21376         this.frameId = Roo.id();
21377         
21378          
21379         
21380         var iframe = this.owner.wrap.createChild({
21381             tag: 'iframe',
21382             cls: 'form-control', // bootstrap..
21383             id: this.frameId,
21384             name: this.frameId,
21385             frameBorder : 'no',
21386             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21387         }, this.el
21388         );
21389         
21390         
21391         this.iframe = iframe.dom;
21392
21393          this.assignDocWin();
21394         
21395         this.doc.designMode = 'on';
21396        
21397         this.doc.open();
21398         this.doc.write(this.getDocMarkup());
21399         this.doc.close();
21400
21401         
21402         var task = { // must defer to wait for browser to be ready
21403             run : function(){
21404                 //console.log("run task?" + this.doc.readyState);
21405                 this.assignDocWin();
21406                 if(this.doc.body || this.doc.readyState == 'complete'){
21407                     try {
21408                         this.doc.designMode="on";
21409                     } catch (e) {
21410                         return;
21411                     }
21412                     Roo.TaskMgr.stop(task);
21413                     this.initEditor.defer(10, this);
21414                 }
21415             },
21416             interval : 10,
21417             duration: 10000,
21418             scope: this
21419         };
21420         Roo.TaskMgr.start(task);
21421
21422     },
21423
21424     // private
21425     onResize : function(w, h)
21426     {
21427          Roo.log('resize: ' +w + ',' + h );
21428         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21429         if(!this.iframe){
21430             return;
21431         }
21432         if(typeof w == 'number'){
21433             
21434             this.iframe.style.width = w + 'px';
21435         }
21436         if(typeof h == 'number'){
21437             
21438             this.iframe.style.height = h + 'px';
21439             if(this.doc){
21440                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21441             }
21442         }
21443         
21444     },
21445
21446     /**
21447      * Toggles the editor between standard and source edit mode.
21448      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21449      */
21450     toggleSourceEdit : function(sourceEditMode){
21451         
21452         this.sourceEditMode = sourceEditMode === true;
21453         
21454         if(this.sourceEditMode){
21455  
21456             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21457             
21458         }else{
21459             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21460             //this.iframe.className = '';
21461             this.deferFocus();
21462         }
21463         //this.setSize(this.owner.wrap.getSize());
21464         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21465     },
21466
21467     
21468   
21469
21470     /**
21471      * Protected method that will not generally be called directly. If you need/want
21472      * custom HTML cleanup, this is the method you should override.
21473      * @param {String} html The HTML to be cleaned
21474      * return {String} The cleaned HTML
21475      */
21476     cleanHtml : function(html){
21477         html = String(html);
21478         if(html.length > 5){
21479             if(Roo.isSafari){ // strip safari nonsense
21480                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21481             }
21482         }
21483         if(html == '&nbsp;'){
21484             html = '';
21485         }
21486         return html;
21487     },
21488
21489     /**
21490      * HTML Editor -> Textarea
21491      * Protected method that will not generally be called directly. Syncs the contents
21492      * of the editor iframe with the textarea.
21493      */
21494     syncValue : function(){
21495         if(this.initialized){
21496             var bd = (this.doc.body || this.doc.documentElement);
21497             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21498             var html = bd.innerHTML;
21499             if(Roo.isSafari){
21500                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21501                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21502                 if(m && m[1]){
21503                     html = '<div style="'+m[0]+'">' + html + '</div>';
21504                 }
21505             }
21506             html = this.cleanHtml(html);
21507             // fix up the special chars.. normaly like back quotes in word...
21508             // however we do not want to do this with chinese..
21509             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21510                 var cc = b.charCodeAt();
21511                 if (
21512                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21513                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21514                     (cc >= 0xf900 && cc < 0xfb00 )
21515                 ) {
21516                         return b;
21517                 }
21518                 return "&#"+cc+";" 
21519             });
21520             if(this.owner.fireEvent('beforesync', this, html) !== false){
21521                 this.el.dom.value = html;
21522                 this.owner.fireEvent('sync', this, html);
21523             }
21524         }
21525     },
21526
21527     /**
21528      * Protected method that will not generally be called directly. Pushes the value of the textarea
21529      * into the iframe editor.
21530      */
21531     pushValue : function(){
21532         if(this.initialized){
21533             var v = this.el.dom.value.trim();
21534             
21535 //            if(v.length < 1){
21536 //                v = '&#160;';
21537 //            }
21538             
21539             if(this.owner.fireEvent('beforepush', this, v) !== false){
21540                 var d = (this.doc.body || this.doc.documentElement);
21541                 d.innerHTML = v;
21542                 this.cleanUpPaste();
21543                 this.el.dom.value = d.innerHTML;
21544                 this.owner.fireEvent('push', this, v);
21545             }
21546         }
21547     },
21548
21549     // private
21550     deferFocus : function(){
21551         this.focus.defer(10, this);
21552     },
21553
21554     // doc'ed in Field
21555     focus : function(){
21556         if(this.win && !this.sourceEditMode){
21557             this.win.focus();
21558         }else{
21559             this.el.focus();
21560         }
21561     },
21562     
21563     assignDocWin: function()
21564     {
21565         var iframe = this.iframe;
21566         
21567          if(Roo.isIE){
21568             this.doc = iframe.contentWindow.document;
21569             this.win = iframe.contentWindow;
21570         } else {
21571 //            if (!Roo.get(this.frameId)) {
21572 //                return;
21573 //            }
21574 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21575 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21576             
21577             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21578                 return;
21579             }
21580             
21581             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21582             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21583         }
21584     },
21585     
21586     // private
21587     initEditor : function(){
21588         //console.log("INIT EDITOR");
21589         this.assignDocWin();
21590         
21591         
21592         
21593         this.doc.designMode="on";
21594         this.doc.open();
21595         this.doc.write(this.getDocMarkup());
21596         this.doc.close();
21597         
21598         var dbody = (this.doc.body || this.doc.documentElement);
21599         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21600         // this copies styles from the containing element into thsi one..
21601         // not sure why we need all of this..
21602         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21603         
21604         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21605         //ss['background-attachment'] = 'fixed'; // w3c
21606         dbody.bgProperties = 'fixed'; // ie
21607         //Roo.DomHelper.applyStyles(dbody, ss);
21608         Roo.EventManager.on(this.doc, {
21609             //'mousedown': this.onEditorEvent,
21610             'mouseup': this.onEditorEvent,
21611             'dblclick': this.onEditorEvent,
21612             'click': this.onEditorEvent,
21613             'keyup': this.onEditorEvent,
21614             buffer:100,
21615             scope: this
21616         });
21617         if(Roo.isGecko){
21618             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21619         }
21620         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21621             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21622         }
21623         this.initialized = true;
21624
21625         this.owner.fireEvent('initialize', this);
21626         this.pushValue();
21627     },
21628
21629     // private
21630     onDestroy : function(){
21631         
21632         
21633         
21634         if(this.rendered){
21635             
21636             //for (var i =0; i < this.toolbars.length;i++) {
21637             //    // fixme - ask toolbars for heights?
21638             //    this.toolbars[i].onDestroy();
21639            // }
21640             
21641             //this.wrap.dom.innerHTML = '';
21642             //this.wrap.remove();
21643         }
21644     },
21645
21646     // private
21647     onFirstFocus : function(){
21648         
21649         this.assignDocWin();
21650         
21651         
21652         this.activated = true;
21653          
21654     
21655         if(Roo.isGecko){ // prevent silly gecko errors
21656             this.win.focus();
21657             var s = this.win.getSelection();
21658             if(!s.focusNode || s.focusNode.nodeType != 3){
21659                 var r = s.getRangeAt(0);
21660                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21661                 r.collapse(true);
21662                 this.deferFocus();
21663             }
21664             try{
21665                 this.execCmd('useCSS', true);
21666                 this.execCmd('styleWithCSS', false);
21667             }catch(e){}
21668         }
21669         this.owner.fireEvent('activate', this);
21670     },
21671
21672     // private
21673     adjustFont: function(btn){
21674         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21675         //if(Roo.isSafari){ // safari
21676         //    adjust *= 2;
21677        // }
21678         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21679         if(Roo.isSafari){ // safari
21680             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21681             v =  (v < 10) ? 10 : v;
21682             v =  (v > 48) ? 48 : v;
21683             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21684             
21685         }
21686         
21687         
21688         v = Math.max(1, v+adjust);
21689         
21690         this.execCmd('FontSize', v  );
21691     },
21692
21693     onEditorEvent : function(e)
21694     {
21695         this.owner.fireEvent('editorevent', this, e);
21696       //  this.updateToolbar();
21697         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21698     },
21699
21700     insertTag : function(tg)
21701     {
21702         // could be a bit smarter... -> wrap the current selected tRoo..
21703         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21704             
21705             range = this.createRange(this.getSelection());
21706             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21707             wrappingNode.appendChild(range.extractContents());
21708             range.insertNode(wrappingNode);
21709
21710             return;
21711             
21712             
21713             
21714         }
21715         this.execCmd("formatblock",   tg);
21716         
21717     },
21718     
21719     insertText : function(txt)
21720     {
21721         
21722         
21723         var range = this.createRange();
21724         range.deleteContents();
21725                //alert(Sender.getAttribute('label'));
21726                
21727         range.insertNode(this.doc.createTextNode(txt));
21728     } ,
21729     
21730      
21731
21732     /**
21733      * Executes a Midas editor command on the editor document and performs necessary focus and
21734      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21735      * @param {String} cmd The Midas command
21736      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21737      */
21738     relayCmd : function(cmd, value){
21739         this.win.focus();
21740         this.execCmd(cmd, value);
21741         this.owner.fireEvent('editorevent', this);
21742         //this.updateToolbar();
21743         this.owner.deferFocus();
21744     },
21745
21746     /**
21747      * Executes a Midas editor command directly on the editor document.
21748      * For visual commands, you should use {@link #relayCmd} instead.
21749      * <b>This should only be called after the editor is initialized.</b>
21750      * @param {String} cmd The Midas command
21751      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21752      */
21753     execCmd : function(cmd, value){
21754         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21755         this.syncValue();
21756     },
21757  
21758  
21759    
21760     /**
21761      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21762      * to insert tRoo.
21763      * @param {String} text | dom node.. 
21764      */
21765     insertAtCursor : function(text)
21766     {
21767         
21768         if(!this.activated){
21769             return;
21770         }
21771         /*
21772         if(Roo.isIE){
21773             this.win.focus();
21774             var r = this.doc.selection.createRange();
21775             if(r){
21776                 r.collapse(true);
21777                 r.pasteHTML(text);
21778                 this.syncValue();
21779                 this.deferFocus();
21780             
21781             }
21782             return;
21783         }
21784         */
21785         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21786             this.win.focus();
21787             
21788             
21789             // from jquery ui (MIT licenced)
21790             var range, node;
21791             var win = this.win;
21792             
21793             if (win.getSelection && win.getSelection().getRangeAt) {
21794                 range = win.getSelection().getRangeAt(0);
21795                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21796                 range.insertNode(node);
21797             } else if (win.document.selection && win.document.selection.createRange) {
21798                 // no firefox support
21799                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21800                 win.document.selection.createRange().pasteHTML(txt);
21801             } else {
21802                 // no firefox support
21803                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21804                 this.execCmd('InsertHTML', txt);
21805             } 
21806             
21807             this.syncValue();
21808             
21809             this.deferFocus();
21810         }
21811     },
21812  // private
21813     mozKeyPress : function(e){
21814         if(e.ctrlKey){
21815             var c = e.getCharCode(), cmd;
21816           
21817             if(c > 0){
21818                 c = String.fromCharCode(c).toLowerCase();
21819                 switch(c){
21820                     case 'b':
21821                         cmd = 'bold';
21822                         break;
21823                     case 'i':
21824                         cmd = 'italic';
21825                         break;
21826                     
21827                     case 'u':
21828                         cmd = 'underline';
21829                         break;
21830                     
21831                     case 'v':
21832                         this.cleanUpPaste.defer(100, this);
21833                         return;
21834                         
21835                 }
21836                 if(cmd){
21837                     this.win.focus();
21838                     this.execCmd(cmd);
21839                     this.deferFocus();
21840                     e.preventDefault();
21841                 }
21842                 
21843             }
21844         }
21845     },
21846
21847     // private
21848     fixKeys : function(){ // load time branching for fastest keydown performance
21849         if(Roo.isIE){
21850             return function(e){
21851                 var k = e.getKey(), r;
21852                 if(k == e.TAB){
21853                     e.stopEvent();
21854                     r = this.doc.selection.createRange();
21855                     if(r){
21856                         r.collapse(true);
21857                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21858                         this.deferFocus();
21859                     }
21860                     return;
21861                 }
21862                 
21863                 if(k == e.ENTER){
21864                     r = this.doc.selection.createRange();
21865                     if(r){
21866                         var target = r.parentElement();
21867                         if(!target || target.tagName.toLowerCase() != 'li'){
21868                             e.stopEvent();
21869                             r.pasteHTML('<br />');
21870                             r.collapse(false);
21871                             r.select();
21872                         }
21873                     }
21874                 }
21875                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21876                     this.cleanUpPaste.defer(100, this);
21877                     return;
21878                 }
21879                 
21880                 
21881             };
21882         }else if(Roo.isOpera){
21883             return function(e){
21884                 var k = e.getKey();
21885                 if(k == e.TAB){
21886                     e.stopEvent();
21887                     this.win.focus();
21888                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21889                     this.deferFocus();
21890                 }
21891                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21892                     this.cleanUpPaste.defer(100, this);
21893                     return;
21894                 }
21895                 
21896             };
21897         }else if(Roo.isSafari){
21898             return function(e){
21899                 var k = e.getKey();
21900                 
21901                 if(k == e.TAB){
21902                     e.stopEvent();
21903                     this.execCmd('InsertText','\t');
21904                     this.deferFocus();
21905                     return;
21906                 }
21907                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21908                     this.cleanUpPaste.defer(100, this);
21909                     return;
21910                 }
21911                 
21912              };
21913         }
21914     }(),
21915     
21916     getAllAncestors: function()
21917     {
21918         var p = this.getSelectedNode();
21919         var a = [];
21920         if (!p) {
21921             a.push(p); // push blank onto stack..
21922             p = this.getParentElement();
21923         }
21924         
21925         
21926         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21927             a.push(p);
21928             p = p.parentNode;
21929         }
21930         a.push(this.doc.body);
21931         return a;
21932     },
21933     lastSel : false,
21934     lastSelNode : false,
21935     
21936     
21937     getSelection : function() 
21938     {
21939         this.assignDocWin();
21940         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21941     },
21942     
21943     getSelectedNode: function() 
21944     {
21945         // this may only work on Gecko!!!
21946         
21947         // should we cache this!!!!
21948         
21949         
21950         
21951          
21952         var range = this.createRange(this.getSelection()).cloneRange();
21953         
21954         if (Roo.isIE) {
21955             var parent = range.parentElement();
21956             while (true) {
21957                 var testRange = range.duplicate();
21958                 testRange.moveToElementText(parent);
21959                 if (testRange.inRange(range)) {
21960                     break;
21961                 }
21962                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21963                     break;
21964                 }
21965                 parent = parent.parentElement;
21966             }
21967             return parent;
21968         }
21969         
21970         // is ancestor a text element.
21971         var ac =  range.commonAncestorContainer;
21972         if (ac.nodeType == 3) {
21973             ac = ac.parentNode;
21974         }
21975         
21976         var ar = ac.childNodes;
21977          
21978         var nodes = [];
21979         var other_nodes = [];
21980         var has_other_nodes = false;
21981         for (var i=0;i<ar.length;i++) {
21982             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21983                 continue;
21984             }
21985             // fullly contained node.
21986             
21987             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21988                 nodes.push(ar[i]);
21989                 continue;
21990             }
21991             
21992             // probably selected..
21993             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21994                 other_nodes.push(ar[i]);
21995                 continue;
21996             }
21997             // outer..
21998             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21999                 continue;
22000             }
22001             
22002             
22003             has_other_nodes = true;
22004         }
22005         if (!nodes.length && other_nodes.length) {
22006             nodes= other_nodes;
22007         }
22008         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22009             return false;
22010         }
22011         
22012         return nodes[0];
22013     },
22014     createRange: function(sel)
22015     {
22016         // this has strange effects when using with 
22017         // top toolbar - not sure if it's a great idea.
22018         //this.editor.contentWindow.focus();
22019         if (typeof sel != "undefined") {
22020             try {
22021                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22022             } catch(e) {
22023                 return this.doc.createRange();
22024             }
22025         } else {
22026             return this.doc.createRange();
22027         }
22028     },
22029     getParentElement: function()
22030     {
22031         
22032         this.assignDocWin();
22033         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22034         
22035         var range = this.createRange(sel);
22036          
22037         try {
22038             var p = range.commonAncestorContainer;
22039             while (p.nodeType == 3) { // text node
22040                 p = p.parentNode;
22041             }
22042             return p;
22043         } catch (e) {
22044             return null;
22045         }
22046     
22047     },
22048     /***
22049      *
22050      * Range intersection.. the hard stuff...
22051      *  '-1' = before
22052      *  '0' = hits..
22053      *  '1' = after.
22054      *         [ -- selected range --- ]
22055      *   [fail]                        [fail]
22056      *
22057      *    basically..
22058      *      if end is before start or  hits it. fail.
22059      *      if start is after end or hits it fail.
22060      *
22061      *   if either hits (but other is outside. - then it's not 
22062      *   
22063      *    
22064      **/
22065     
22066     
22067     // @see http://www.thismuchiknow.co.uk/?p=64.
22068     rangeIntersectsNode : function(range, node)
22069     {
22070         var nodeRange = node.ownerDocument.createRange();
22071         try {
22072             nodeRange.selectNode(node);
22073         } catch (e) {
22074             nodeRange.selectNodeContents(node);
22075         }
22076     
22077         var rangeStartRange = range.cloneRange();
22078         rangeStartRange.collapse(true);
22079     
22080         var rangeEndRange = range.cloneRange();
22081         rangeEndRange.collapse(false);
22082     
22083         var nodeStartRange = nodeRange.cloneRange();
22084         nodeStartRange.collapse(true);
22085     
22086         var nodeEndRange = nodeRange.cloneRange();
22087         nodeEndRange.collapse(false);
22088     
22089         return rangeStartRange.compareBoundaryPoints(
22090                  Range.START_TO_START, nodeEndRange) == -1 &&
22091                rangeEndRange.compareBoundaryPoints(
22092                  Range.START_TO_START, nodeStartRange) == 1;
22093         
22094          
22095     },
22096     rangeCompareNode : function(range, node)
22097     {
22098         var nodeRange = node.ownerDocument.createRange();
22099         try {
22100             nodeRange.selectNode(node);
22101         } catch (e) {
22102             nodeRange.selectNodeContents(node);
22103         }
22104         
22105         
22106         range.collapse(true);
22107     
22108         nodeRange.collapse(true);
22109      
22110         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22111         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22112          
22113         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22114         
22115         var nodeIsBefore   =  ss == 1;
22116         var nodeIsAfter    = ee == -1;
22117         
22118         if (nodeIsBefore && nodeIsAfter) {
22119             return 0; // outer
22120         }
22121         if (!nodeIsBefore && nodeIsAfter) {
22122             return 1; //right trailed.
22123         }
22124         
22125         if (nodeIsBefore && !nodeIsAfter) {
22126             return 2;  // left trailed.
22127         }
22128         // fully contined.
22129         return 3;
22130     },
22131
22132     // private? - in a new class?
22133     cleanUpPaste :  function()
22134     {
22135         // cleans up the whole document..
22136         Roo.log('cleanuppaste');
22137         
22138         this.cleanUpChildren(this.doc.body);
22139         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22140         if (clean != this.doc.body.innerHTML) {
22141             this.doc.body.innerHTML = clean;
22142         }
22143         
22144     },
22145     
22146     cleanWordChars : function(input) {// change the chars to hex code
22147         var he = Roo.HtmlEditorCore;
22148         
22149         var output = input;
22150         Roo.each(he.swapCodes, function(sw) { 
22151             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22152             
22153             output = output.replace(swapper, sw[1]);
22154         });
22155         
22156         return output;
22157     },
22158     
22159     
22160     cleanUpChildren : function (n)
22161     {
22162         if (!n.childNodes.length) {
22163             return;
22164         }
22165         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22166            this.cleanUpChild(n.childNodes[i]);
22167         }
22168     },
22169     
22170     
22171         
22172     
22173     cleanUpChild : function (node)
22174     {
22175         var ed = this;
22176         //console.log(node);
22177         if (node.nodeName == "#text") {
22178             // clean up silly Windows -- stuff?
22179             return; 
22180         }
22181         if (node.nodeName == "#comment") {
22182             node.parentNode.removeChild(node);
22183             // clean up silly Windows -- stuff?
22184             return; 
22185         }
22186         var lcname = node.tagName.toLowerCase();
22187         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22188         // whitelist of tags..
22189         
22190         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22191             // remove node.
22192             node.parentNode.removeChild(node);
22193             return;
22194             
22195         }
22196         
22197         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22198         
22199         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22200         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22201         
22202         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22203         //    remove_keep_children = true;
22204         //}
22205         
22206         if (remove_keep_children) {
22207             this.cleanUpChildren(node);
22208             // inserts everything just before this node...
22209             while (node.childNodes.length) {
22210                 var cn = node.childNodes[0];
22211                 node.removeChild(cn);
22212                 node.parentNode.insertBefore(cn, node);
22213             }
22214             node.parentNode.removeChild(node);
22215             return;
22216         }
22217         
22218         if (!node.attributes || !node.attributes.length) {
22219             this.cleanUpChildren(node);
22220             return;
22221         }
22222         
22223         function cleanAttr(n,v)
22224         {
22225             
22226             if (v.match(/^\./) || v.match(/^\//)) {
22227                 return;
22228             }
22229             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22230                 return;
22231             }
22232             if (v.match(/^#/)) {
22233                 return;
22234             }
22235 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22236             node.removeAttribute(n);
22237             
22238         }
22239         
22240         var cwhite = this.cwhite;
22241         var cblack = this.cblack;
22242             
22243         function cleanStyle(n,v)
22244         {
22245             if (v.match(/expression/)) { //XSS?? should we even bother..
22246                 node.removeAttribute(n);
22247                 return;
22248             }
22249             
22250             var parts = v.split(/;/);
22251             var clean = [];
22252             
22253             Roo.each(parts, function(p) {
22254                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22255                 if (!p.length) {
22256                     return true;
22257                 }
22258                 var l = p.split(':').shift().replace(/\s+/g,'');
22259                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22260                 
22261                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22262 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22263                     //node.removeAttribute(n);
22264                     return true;
22265                 }
22266                 //Roo.log()
22267                 // only allow 'c whitelisted system attributes'
22268                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22269 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22270                     //node.removeAttribute(n);
22271                     return true;
22272                 }
22273                 
22274                 
22275                  
22276                 
22277                 clean.push(p);
22278                 return true;
22279             });
22280             if (clean.length) { 
22281                 node.setAttribute(n, clean.join(';'));
22282             } else {
22283                 node.removeAttribute(n);
22284             }
22285             
22286         }
22287         
22288         
22289         for (var i = node.attributes.length-1; i > -1 ; i--) {
22290             var a = node.attributes[i];
22291             //console.log(a);
22292             
22293             if (a.name.toLowerCase().substr(0,2)=='on')  {
22294                 node.removeAttribute(a.name);
22295                 continue;
22296             }
22297             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22298                 node.removeAttribute(a.name);
22299                 continue;
22300             }
22301             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22302                 cleanAttr(a.name,a.value); // fixme..
22303                 continue;
22304             }
22305             if (a.name == 'style') {
22306                 cleanStyle(a.name,a.value);
22307                 continue;
22308             }
22309             /// clean up MS crap..
22310             // tecnically this should be a list of valid class'es..
22311             
22312             
22313             if (a.name == 'class') {
22314                 if (a.value.match(/^Mso/)) {
22315                     node.className = '';
22316                 }
22317                 
22318                 if (a.value.match(/^body$/)) {
22319                     node.className = '';
22320                 }
22321                 continue;
22322             }
22323             
22324             // style cleanup!?
22325             // class cleanup?
22326             
22327         }
22328         
22329         
22330         this.cleanUpChildren(node);
22331         
22332         
22333     },
22334     
22335     /**
22336      * Clean up MS wordisms...
22337      */
22338     cleanWord : function(node)
22339     {
22340         
22341         
22342         if (!node) {
22343             this.cleanWord(this.doc.body);
22344             return;
22345         }
22346         if (node.nodeName == "#text") {
22347             // clean up silly Windows -- stuff?
22348             return; 
22349         }
22350         if (node.nodeName == "#comment") {
22351             node.parentNode.removeChild(node);
22352             // clean up silly Windows -- stuff?
22353             return; 
22354         }
22355         
22356         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22357             node.parentNode.removeChild(node);
22358             return;
22359         }
22360         
22361         // remove - but keep children..
22362         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22363             while (node.childNodes.length) {
22364                 var cn = node.childNodes[0];
22365                 node.removeChild(cn);
22366                 node.parentNode.insertBefore(cn, node);
22367             }
22368             node.parentNode.removeChild(node);
22369             this.iterateChildren(node, this.cleanWord);
22370             return;
22371         }
22372         // clean styles
22373         if (node.className.length) {
22374             
22375             var cn = node.className.split(/\W+/);
22376             var cna = [];
22377             Roo.each(cn, function(cls) {
22378                 if (cls.match(/Mso[a-zA-Z]+/)) {
22379                     return;
22380                 }
22381                 cna.push(cls);
22382             });
22383             node.className = cna.length ? cna.join(' ') : '';
22384             if (!cna.length) {
22385                 node.removeAttribute("class");
22386             }
22387         }
22388         
22389         if (node.hasAttribute("lang")) {
22390             node.removeAttribute("lang");
22391         }
22392         
22393         if (node.hasAttribute("style")) {
22394             
22395             var styles = node.getAttribute("style").split(";");
22396             var nstyle = [];
22397             Roo.each(styles, function(s) {
22398                 if (!s.match(/:/)) {
22399                     return;
22400                 }
22401                 var kv = s.split(":");
22402                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22403                     return;
22404                 }
22405                 // what ever is left... we allow.
22406                 nstyle.push(s);
22407             });
22408             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22409             if (!nstyle.length) {
22410                 node.removeAttribute('style');
22411             }
22412         }
22413         this.iterateChildren(node, this.cleanWord);
22414         
22415         
22416         
22417     },
22418     /**
22419      * iterateChildren of a Node, calling fn each time, using this as the scole..
22420      * @param {DomNode} node node to iterate children of.
22421      * @param {Function} fn method of this class to call on each item.
22422      */
22423     iterateChildren : function(node, fn)
22424     {
22425         if (!node.childNodes.length) {
22426                 return;
22427         }
22428         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22429            fn.call(this, node.childNodes[i])
22430         }
22431     },
22432     
22433     
22434     /**
22435      * cleanTableWidths.
22436      *
22437      * Quite often pasting from word etc.. results in tables with column and widths.
22438      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22439      *
22440      */
22441     cleanTableWidths : function(node)
22442     {
22443          
22444          
22445         if (!node) {
22446             this.cleanTableWidths(this.doc.body);
22447             return;
22448         }
22449         
22450         // ignore list...
22451         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22452             return; 
22453         }
22454         Roo.log(node.tagName);
22455         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22456             this.iterateChildren(node, this.cleanTableWidths);
22457             return;
22458         }
22459         if (node.hasAttribute('width')) {
22460             node.removeAttribute('width');
22461         }
22462         
22463          
22464         if (node.hasAttribute("style")) {
22465             // pretty basic...
22466             
22467             var styles = node.getAttribute("style").split(";");
22468             var nstyle = [];
22469             Roo.each(styles, function(s) {
22470                 if (!s.match(/:/)) {
22471                     return;
22472                 }
22473                 var kv = s.split(":");
22474                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22475                     return;
22476                 }
22477                 // what ever is left... we allow.
22478                 nstyle.push(s);
22479             });
22480             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22481             if (!nstyle.length) {
22482                 node.removeAttribute('style');
22483             }
22484         }
22485         
22486         this.iterateChildren(node, this.cleanTableWidths);
22487         
22488         
22489     },
22490     
22491     
22492     
22493     
22494     domToHTML : function(currentElement, depth, nopadtext) {
22495         
22496         depth = depth || 0;
22497         nopadtext = nopadtext || false;
22498     
22499         if (!currentElement) {
22500             return this.domToHTML(this.doc.body);
22501         }
22502         
22503         //Roo.log(currentElement);
22504         var j;
22505         var allText = false;
22506         var nodeName = currentElement.nodeName;
22507         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22508         
22509         if  (nodeName == '#text') {
22510             
22511             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22512         }
22513         
22514         
22515         var ret = '';
22516         if (nodeName != 'BODY') {
22517              
22518             var i = 0;
22519             // Prints the node tagName, such as <A>, <IMG>, etc
22520             if (tagName) {
22521                 var attr = [];
22522                 for(i = 0; i < currentElement.attributes.length;i++) {
22523                     // quoting?
22524                     var aname = currentElement.attributes.item(i).name;
22525                     if (!currentElement.attributes.item(i).value.length) {
22526                         continue;
22527                     }
22528                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22529                 }
22530                 
22531                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22532             } 
22533             else {
22534                 
22535                 // eack
22536             }
22537         } else {
22538             tagName = false;
22539         }
22540         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22541             return ret;
22542         }
22543         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22544             nopadtext = true;
22545         }
22546         
22547         
22548         // Traverse the tree
22549         i = 0;
22550         var currentElementChild = currentElement.childNodes.item(i);
22551         var allText = true;
22552         var innerHTML  = '';
22553         lastnode = '';
22554         while (currentElementChild) {
22555             // Formatting code (indent the tree so it looks nice on the screen)
22556             var nopad = nopadtext;
22557             if (lastnode == 'SPAN') {
22558                 nopad  = true;
22559             }
22560             // text
22561             if  (currentElementChild.nodeName == '#text') {
22562                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22563                 toadd = nopadtext ? toadd : toadd.trim();
22564                 if (!nopad && toadd.length > 80) {
22565                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22566                 }
22567                 innerHTML  += toadd;
22568                 
22569                 i++;
22570                 currentElementChild = currentElement.childNodes.item(i);
22571                 lastNode = '';
22572                 continue;
22573             }
22574             allText = false;
22575             
22576             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22577                 
22578             // Recursively traverse the tree structure of the child node
22579             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22580             lastnode = currentElementChild.nodeName;
22581             i++;
22582             currentElementChild=currentElement.childNodes.item(i);
22583         }
22584         
22585         ret += innerHTML;
22586         
22587         if (!allText) {
22588                 // The remaining code is mostly for formatting the tree
22589             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22590         }
22591         
22592         
22593         if (tagName) {
22594             ret+= "</"+tagName+">";
22595         }
22596         return ret;
22597         
22598     },
22599         
22600     applyBlacklists : function()
22601     {
22602         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22603         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22604         
22605         this.white = [];
22606         this.black = [];
22607         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22608             if (b.indexOf(tag) > -1) {
22609                 return;
22610             }
22611             this.white.push(tag);
22612             
22613         }, this);
22614         
22615         Roo.each(w, function(tag) {
22616             if (b.indexOf(tag) > -1) {
22617                 return;
22618             }
22619             if (this.white.indexOf(tag) > -1) {
22620                 return;
22621             }
22622             this.white.push(tag);
22623             
22624         }, this);
22625         
22626         
22627         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22628             if (w.indexOf(tag) > -1) {
22629                 return;
22630             }
22631             this.black.push(tag);
22632             
22633         }, this);
22634         
22635         Roo.each(b, function(tag) {
22636             if (w.indexOf(tag) > -1) {
22637                 return;
22638             }
22639             if (this.black.indexOf(tag) > -1) {
22640                 return;
22641             }
22642             this.black.push(tag);
22643             
22644         }, this);
22645         
22646         
22647         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22648         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22649         
22650         this.cwhite = [];
22651         this.cblack = [];
22652         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22653             if (b.indexOf(tag) > -1) {
22654                 return;
22655             }
22656             this.cwhite.push(tag);
22657             
22658         }, this);
22659         
22660         Roo.each(w, function(tag) {
22661             if (b.indexOf(tag) > -1) {
22662                 return;
22663             }
22664             if (this.cwhite.indexOf(tag) > -1) {
22665                 return;
22666             }
22667             this.cwhite.push(tag);
22668             
22669         }, this);
22670         
22671         
22672         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22673             if (w.indexOf(tag) > -1) {
22674                 return;
22675             }
22676             this.cblack.push(tag);
22677             
22678         }, this);
22679         
22680         Roo.each(b, function(tag) {
22681             if (w.indexOf(tag) > -1) {
22682                 return;
22683             }
22684             if (this.cblack.indexOf(tag) > -1) {
22685                 return;
22686             }
22687             this.cblack.push(tag);
22688             
22689         }, this);
22690     },
22691     
22692     setStylesheets : function(stylesheets)
22693     {
22694         if(typeof(stylesheets) == 'string'){
22695             Roo.get(this.iframe.contentDocument.head).createChild({
22696                 tag : 'link',
22697                 rel : 'stylesheet',
22698                 type : 'text/css',
22699                 href : stylesheets
22700             });
22701             
22702             return;
22703         }
22704         var _this = this;
22705      
22706         Roo.each(stylesheets, function(s) {
22707             if(!s.length){
22708                 return;
22709             }
22710             
22711             Roo.get(_this.iframe.contentDocument.head).createChild({
22712                 tag : 'link',
22713                 rel : 'stylesheet',
22714                 type : 'text/css',
22715                 href : s
22716             });
22717         });
22718
22719         
22720     },
22721     
22722     removeStylesheets : function()
22723     {
22724         var _this = this;
22725         
22726         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22727             s.remove();
22728         });
22729     },
22730     
22731     setStyle : function(style)
22732     {
22733         Roo.get(this.iframe.contentDocument.head).createChild({
22734             tag : 'style',
22735             type : 'text/css',
22736             html : style
22737         });
22738
22739         return;
22740     }
22741     
22742     // hide stuff that is not compatible
22743     /**
22744      * @event blur
22745      * @hide
22746      */
22747     /**
22748      * @event change
22749      * @hide
22750      */
22751     /**
22752      * @event focus
22753      * @hide
22754      */
22755     /**
22756      * @event specialkey
22757      * @hide
22758      */
22759     /**
22760      * @cfg {String} fieldClass @hide
22761      */
22762     /**
22763      * @cfg {String} focusClass @hide
22764      */
22765     /**
22766      * @cfg {String} autoCreate @hide
22767      */
22768     /**
22769      * @cfg {String} inputType @hide
22770      */
22771     /**
22772      * @cfg {String} invalidClass @hide
22773      */
22774     /**
22775      * @cfg {String} invalidText @hide
22776      */
22777     /**
22778      * @cfg {String} msgFx @hide
22779      */
22780     /**
22781      * @cfg {String} validateOnBlur @hide
22782      */
22783 });
22784
22785 Roo.HtmlEditorCore.white = [
22786         'area', 'br', 'img', 'input', 'hr', 'wbr',
22787         
22788        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22789        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22790        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22791        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22792        'table',   'ul',         'xmp', 
22793        
22794        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22795       'thead',   'tr', 
22796      
22797       'dir', 'menu', 'ol', 'ul', 'dl',
22798        
22799       'embed',  'object'
22800 ];
22801
22802
22803 Roo.HtmlEditorCore.black = [
22804     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22805         'applet', // 
22806         'base',   'basefont', 'bgsound', 'blink',  'body', 
22807         'frame',  'frameset', 'head',    'html',   'ilayer', 
22808         'iframe', 'layer',  'link',     'meta',    'object',   
22809         'script', 'style' ,'title',  'xml' // clean later..
22810 ];
22811 Roo.HtmlEditorCore.clean = [
22812     'script', 'style', 'title', 'xml'
22813 ];
22814 Roo.HtmlEditorCore.remove = [
22815     'font'
22816 ];
22817 // attributes..
22818
22819 Roo.HtmlEditorCore.ablack = [
22820     'on'
22821 ];
22822     
22823 Roo.HtmlEditorCore.aclean = [ 
22824     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22825 ];
22826
22827 // protocols..
22828 Roo.HtmlEditorCore.pwhite= [
22829         'http',  'https',  'mailto'
22830 ];
22831
22832 // white listed style attributes.
22833 Roo.HtmlEditorCore.cwhite= [
22834       //  'text-align', /// default is to allow most things..
22835       
22836          
22837 //        'font-size'//??
22838 ];
22839
22840 // black listed style attributes.
22841 Roo.HtmlEditorCore.cblack= [
22842       //  'font-size' -- this can be set by the project 
22843 ];
22844
22845
22846 Roo.HtmlEditorCore.swapCodes   =[ 
22847     [    8211, "--" ], 
22848     [    8212, "--" ], 
22849     [    8216,  "'" ],  
22850     [    8217, "'" ],  
22851     [    8220, '"' ],  
22852     [    8221, '"' ],  
22853     [    8226, "*" ],  
22854     [    8230, "..." ]
22855 ]; 
22856
22857     /*
22858  * - LGPL
22859  *
22860  * HtmlEditor
22861  * 
22862  */
22863
22864 /**
22865  * @class Roo.bootstrap.HtmlEditor
22866  * @extends Roo.bootstrap.TextArea
22867  * Bootstrap HtmlEditor class
22868
22869  * @constructor
22870  * Create a new HtmlEditor
22871  * @param {Object} config The config object
22872  */
22873
22874 Roo.bootstrap.HtmlEditor = function(config){
22875     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22876     if (!this.toolbars) {
22877         this.toolbars = [];
22878     }
22879     
22880     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22881     this.addEvents({
22882             /**
22883              * @event initialize
22884              * Fires when the editor is fully initialized (including the iframe)
22885              * @param {HtmlEditor} this
22886              */
22887             initialize: true,
22888             /**
22889              * @event activate
22890              * Fires when the editor is first receives the focus. Any insertion must wait
22891              * until after this event.
22892              * @param {HtmlEditor} this
22893              */
22894             activate: true,
22895              /**
22896              * @event beforesync
22897              * Fires before the textarea is updated with content from the editor iframe. Return false
22898              * to cancel the sync.
22899              * @param {HtmlEditor} this
22900              * @param {String} html
22901              */
22902             beforesync: true,
22903              /**
22904              * @event beforepush
22905              * Fires before the iframe editor is updated with content from the textarea. Return false
22906              * to cancel the push.
22907              * @param {HtmlEditor} this
22908              * @param {String} html
22909              */
22910             beforepush: true,
22911              /**
22912              * @event sync
22913              * Fires when the textarea is updated with content from the editor iframe.
22914              * @param {HtmlEditor} this
22915              * @param {String} html
22916              */
22917             sync: true,
22918              /**
22919              * @event push
22920              * Fires when the iframe editor is updated with content from the textarea.
22921              * @param {HtmlEditor} this
22922              * @param {String} html
22923              */
22924             push: true,
22925              /**
22926              * @event editmodechange
22927              * Fires when the editor switches edit modes
22928              * @param {HtmlEditor} this
22929              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22930              */
22931             editmodechange: true,
22932             /**
22933              * @event editorevent
22934              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22935              * @param {HtmlEditor} this
22936              */
22937             editorevent: true,
22938             /**
22939              * @event firstfocus
22940              * Fires when on first focus - needed by toolbars..
22941              * @param {HtmlEditor} this
22942              */
22943             firstfocus: true,
22944             /**
22945              * @event autosave
22946              * Auto save the htmlEditor value as a file into Events
22947              * @param {HtmlEditor} this
22948              */
22949             autosave: true,
22950             /**
22951              * @event savedpreview
22952              * preview the saved version of htmlEditor
22953              * @param {HtmlEditor} this
22954              */
22955             savedpreview: true
22956         });
22957 };
22958
22959
22960 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22961     
22962     
22963       /**
22964      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22965      */
22966     toolbars : false,
22967     
22968      /**
22969     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22970     */
22971     btns : [],
22972    
22973      /**
22974      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22975      *                        Roo.resizable.
22976      */
22977     resizable : false,
22978      /**
22979      * @cfg {Number} height (in pixels)
22980      */   
22981     height: 300,
22982    /**
22983      * @cfg {Number} width (in pixels)
22984      */   
22985     width: false,
22986     
22987     /**
22988      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22989      * 
22990      */
22991     stylesheets: false,
22992     
22993     // id of frame..
22994     frameId: false,
22995     
22996     // private properties
22997     validationEvent : false,
22998     deferHeight: true,
22999     initialized : false,
23000     activated : false,
23001     
23002     onFocus : Roo.emptyFn,
23003     iframePad:3,
23004     hideMode:'offsets',
23005     
23006     tbContainer : false,
23007     
23008     bodyCls : '',
23009     
23010     toolbarContainer :function() {
23011         return this.wrap.select('.x-html-editor-tb',true).first();
23012     },
23013
23014     /**
23015      * Protected method that will not generally be called directly. It
23016      * is called when the editor creates its toolbar. Override this method if you need to
23017      * add custom toolbar buttons.
23018      * @param {HtmlEditor} editor
23019      */
23020     createToolbar : function(){
23021         Roo.log('renewing');
23022         Roo.log("create toolbars");
23023         
23024         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23025         this.toolbars[0].render(this.toolbarContainer());
23026         
23027         return;
23028         
23029 //        if (!editor.toolbars || !editor.toolbars.length) {
23030 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23031 //        }
23032 //        
23033 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23034 //            editor.toolbars[i] = Roo.factory(
23035 //                    typeof(editor.toolbars[i]) == 'string' ?
23036 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23037 //                Roo.bootstrap.HtmlEditor);
23038 //            editor.toolbars[i].init(editor);
23039 //        }
23040     },
23041
23042      
23043     // private
23044     onRender : function(ct, position)
23045     {
23046        // Roo.log("Call onRender: " + this.xtype);
23047         var _t = this;
23048         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23049       
23050         this.wrap = this.inputEl().wrap({
23051             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23052         });
23053         
23054         this.editorcore.onRender(ct, position);
23055          
23056         if (this.resizable) {
23057             this.resizeEl = new Roo.Resizable(this.wrap, {
23058                 pinned : true,
23059                 wrap: true,
23060                 dynamic : true,
23061                 minHeight : this.height,
23062                 height: this.height,
23063                 handles : this.resizable,
23064                 width: this.width,
23065                 listeners : {
23066                     resize : function(r, w, h) {
23067                         _t.onResize(w,h); // -something
23068                     }
23069                 }
23070             });
23071             
23072         }
23073         this.createToolbar(this);
23074        
23075         
23076         if(!this.width && this.resizable){
23077             this.setSize(this.wrap.getSize());
23078         }
23079         if (this.resizeEl) {
23080             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23081             // should trigger onReize..
23082         }
23083         
23084     },
23085
23086     // private
23087     onResize : function(w, h)
23088     {
23089         Roo.log('resize: ' +w + ',' + h );
23090         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23091         var ew = false;
23092         var eh = false;
23093         
23094         if(this.inputEl() ){
23095             if(typeof w == 'number'){
23096                 var aw = w - this.wrap.getFrameWidth('lr');
23097                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23098                 ew = aw;
23099             }
23100             if(typeof h == 'number'){
23101                  var tbh = -11;  // fixme it needs to tool bar size!
23102                 for (var i =0; i < this.toolbars.length;i++) {
23103                     // fixme - ask toolbars for heights?
23104                     tbh += this.toolbars[i].el.getHeight();
23105                     //if (this.toolbars[i].footer) {
23106                     //    tbh += this.toolbars[i].footer.el.getHeight();
23107                     //}
23108                 }
23109               
23110                 
23111                 
23112                 
23113                 
23114                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23115                 ah -= 5; // knock a few pixes off for look..
23116                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23117                 var eh = ah;
23118             }
23119         }
23120         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23121         this.editorcore.onResize(ew,eh);
23122         
23123     },
23124
23125     /**
23126      * Toggles the editor between standard and source edit mode.
23127      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23128      */
23129     toggleSourceEdit : function(sourceEditMode)
23130     {
23131         this.editorcore.toggleSourceEdit(sourceEditMode);
23132         
23133         if(this.editorcore.sourceEditMode){
23134             Roo.log('editor - showing textarea');
23135             
23136 //            Roo.log('in');
23137 //            Roo.log(this.syncValue());
23138             this.syncValue();
23139             this.inputEl().removeClass(['hide', 'x-hidden']);
23140             this.inputEl().dom.removeAttribute('tabIndex');
23141             this.inputEl().focus();
23142         }else{
23143             Roo.log('editor - hiding textarea');
23144 //            Roo.log('out')
23145 //            Roo.log(this.pushValue()); 
23146             this.pushValue();
23147             
23148             this.inputEl().addClass(['hide', 'x-hidden']);
23149             this.inputEl().dom.setAttribute('tabIndex', -1);
23150             //this.deferFocus();
23151         }
23152          
23153         if(this.resizable){
23154             this.setSize(this.wrap.getSize());
23155         }
23156         
23157         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23158     },
23159  
23160     // private (for BoxComponent)
23161     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23162
23163     // private (for BoxComponent)
23164     getResizeEl : function(){
23165         return this.wrap;
23166     },
23167
23168     // private (for BoxComponent)
23169     getPositionEl : function(){
23170         return this.wrap;
23171     },
23172
23173     // private
23174     initEvents : function(){
23175         this.originalValue = this.getValue();
23176     },
23177
23178 //    /**
23179 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23180 //     * @method
23181 //     */
23182 //    markInvalid : Roo.emptyFn,
23183 //    /**
23184 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23185 //     * @method
23186 //     */
23187 //    clearInvalid : Roo.emptyFn,
23188
23189     setValue : function(v){
23190         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23191         this.editorcore.pushValue();
23192     },
23193
23194      
23195     // private
23196     deferFocus : function(){
23197         this.focus.defer(10, this);
23198     },
23199
23200     // doc'ed in Field
23201     focus : function(){
23202         this.editorcore.focus();
23203         
23204     },
23205       
23206
23207     // private
23208     onDestroy : function(){
23209         
23210         
23211         
23212         if(this.rendered){
23213             
23214             for (var i =0; i < this.toolbars.length;i++) {
23215                 // fixme - ask toolbars for heights?
23216                 this.toolbars[i].onDestroy();
23217             }
23218             
23219             this.wrap.dom.innerHTML = '';
23220             this.wrap.remove();
23221         }
23222     },
23223
23224     // private
23225     onFirstFocus : function(){
23226         //Roo.log("onFirstFocus");
23227         this.editorcore.onFirstFocus();
23228          for (var i =0; i < this.toolbars.length;i++) {
23229             this.toolbars[i].onFirstFocus();
23230         }
23231         
23232     },
23233     
23234     // private
23235     syncValue : function()
23236     {   
23237         this.editorcore.syncValue();
23238     },
23239     
23240     pushValue : function()
23241     {   
23242         this.editorcore.pushValue();
23243     }
23244      
23245     
23246     // hide stuff that is not compatible
23247     /**
23248      * @event blur
23249      * @hide
23250      */
23251     /**
23252      * @event change
23253      * @hide
23254      */
23255     /**
23256      * @event focus
23257      * @hide
23258      */
23259     /**
23260      * @event specialkey
23261      * @hide
23262      */
23263     /**
23264      * @cfg {String} fieldClass @hide
23265      */
23266     /**
23267      * @cfg {String} focusClass @hide
23268      */
23269     /**
23270      * @cfg {String} autoCreate @hide
23271      */
23272     /**
23273      * @cfg {String} inputType @hide
23274      */
23275     /**
23276      * @cfg {String} invalidClass @hide
23277      */
23278     /**
23279      * @cfg {String} invalidText @hide
23280      */
23281     /**
23282      * @cfg {String} msgFx @hide
23283      */
23284     /**
23285      * @cfg {String} validateOnBlur @hide
23286      */
23287 });
23288  
23289     
23290    
23291    
23292    
23293       
23294 Roo.namespace('Roo.bootstrap.htmleditor');
23295 /**
23296  * @class Roo.bootstrap.HtmlEditorToolbar1
23297  * Basic Toolbar
23298  * 
23299  * Usage:
23300  *
23301  new Roo.bootstrap.HtmlEditor({
23302     ....
23303     toolbars : [
23304         new Roo.bootstrap.HtmlEditorToolbar1({
23305             disable : { fonts: 1 , format: 1, ..., ... , ...],
23306             btns : [ .... ]
23307         })
23308     }
23309      
23310  * 
23311  * @cfg {Object} disable List of elements to disable..
23312  * @cfg {Array} btns List of additional buttons.
23313  * 
23314  * 
23315  * NEEDS Extra CSS? 
23316  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23317  */
23318  
23319 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23320 {
23321     
23322     Roo.apply(this, config);
23323     
23324     // default disabled, based on 'good practice'..
23325     this.disable = this.disable || {};
23326     Roo.applyIf(this.disable, {
23327         fontSize : true,
23328         colors : true,
23329         specialElements : true
23330     });
23331     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23332     
23333     this.editor = config.editor;
23334     this.editorcore = config.editor.editorcore;
23335     
23336     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23337     
23338     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23339     // dont call parent... till later.
23340 }
23341 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23342      
23343     bar : true,
23344     
23345     editor : false,
23346     editorcore : false,
23347     
23348     
23349     formats : [
23350         "p" ,  
23351         "h1","h2","h3","h4","h5","h6", 
23352         "pre", "code", 
23353         "abbr", "acronym", "address", "cite", "samp", "var",
23354         'div','span'
23355     ],
23356     
23357     onRender : function(ct, position)
23358     {
23359        // Roo.log("Call onRender: " + this.xtype);
23360         
23361        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23362        Roo.log(this.el);
23363        this.el.dom.style.marginBottom = '0';
23364        var _this = this;
23365        var editorcore = this.editorcore;
23366        var editor= this.editor;
23367        
23368        var children = [];
23369        var btn = function(id,cmd , toggle, handler, html){
23370        
23371             var  event = toggle ? 'toggle' : 'click';
23372        
23373             var a = {
23374                 size : 'sm',
23375                 xtype: 'Button',
23376                 xns: Roo.bootstrap,
23377                 glyphicon : id,
23378                 cmd : id || cmd,
23379                 enableToggle:toggle !== false,
23380                 html : html || '',
23381                 pressed : toggle ? false : null,
23382                 listeners : {}
23383             };
23384             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23385                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23386             };
23387             children.push(a);
23388             return a;
23389        }
23390        
23391     //    var cb_box = function...
23392         
23393         var style = {
23394                 xtype: 'Button',
23395                 size : 'sm',
23396                 xns: Roo.bootstrap,
23397                 glyphicon : 'font',
23398                 //html : 'submit'
23399                 menu : {
23400                     xtype: 'Menu',
23401                     xns: Roo.bootstrap,
23402                     items:  []
23403                 }
23404         };
23405         Roo.each(this.formats, function(f) {
23406             style.menu.items.push({
23407                 xtype :'MenuItem',
23408                 xns: Roo.bootstrap,
23409                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23410                 tagname : f,
23411                 listeners : {
23412                     click : function()
23413                     {
23414                         editorcore.insertTag(this.tagname);
23415                         editor.focus();
23416                     }
23417                 }
23418                 
23419             });
23420         });
23421         children.push(style);   
23422         
23423         btn('bold',false,true);
23424         btn('italic',false,true);
23425         btn('align-left', 'justifyleft',true);
23426         btn('align-center', 'justifycenter',true);
23427         btn('align-right' , 'justifyright',true);
23428         btn('link', false, false, function(btn) {
23429             //Roo.log("create link?");
23430             var url = prompt(this.createLinkText, this.defaultLinkValue);
23431             if(url && url != 'http:/'+'/'){
23432                 this.editorcore.relayCmd('createlink', url);
23433             }
23434         }),
23435         btn('list','insertunorderedlist',true);
23436         btn('pencil', false,true, function(btn){
23437                 Roo.log(this);
23438                 this.toggleSourceEdit(btn.pressed);
23439         });
23440         
23441         if (this.editor.btns.length > 0) {
23442             for (var i = 0; i<this.editor.btns.length; i++) {
23443                 children.push(this.editor.btns[i]);
23444             }
23445         }
23446         
23447         /*
23448         var cog = {
23449                 xtype: 'Button',
23450                 size : 'sm',
23451                 xns: Roo.bootstrap,
23452                 glyphicon : 'cog',
23453                 //html : 'submit'
23454                 menu : {
23455                     xtype: 'Menu',
23456                     xns: Roo.bootstrap,
23457                     items:  []
23458                 }
23459         };
23460         
23461         cog.menu.items.push({
23462             xtype :'MenuItem',
23463             xns: Roo.bootstrap,
23464             html : Clean styles,
23465             tagname : f,
23466             listeners : {
23467                 click : function()
23468                 {
23469                     editorcore.insertTag(this.tagname);
23470                     editor.focus();
23471                 }
23472             }
23473             
23474         });
23475        */
23476         
23477          
23478        this.xtype = 'NavSimplebar';
23479         
23480         for(var i=0;i< children.length;i++) {
23481             
23482             this.buttons.add(this.addxtypeChild(children[i]));
23483             
23484         }
23485         
23486         editor.on('editorevent', this.updateToolbar, this);
23487     },
23488     onBtnClick : function(id)
23489     {
23490        this.editorcore.relayCmd(id);
23491        this.editorcore.focus();
23492     },
23493     
23494     /**
23495      * Protected method that will not generally be called directly. It triggers
23496      * a toolbar update by reading the markup state of the current selection in the editor.
23497      */
23498     updateToolbar: function(){
23499
23500         if(!this.editorcore.activated){
23501             this.editor.onFirstFocus(); // is this neeed?
23502             return;
23503         }
23504
23505         var btns = this.buttons; 
23506         var doc = this.editorcore.doc;
23507         btns.get('bold').setActive(doc.queryCommandState('bold'));
23508         btns.get('italic').setActive(doc.queryCommandState('italic'));
23509         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23510         
23511         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23512         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23513         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23514         
23515         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23516         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23517          /*
23518         
23519         var ans = this.editorcore.getAllAncestors();
23520         if (this.formatCombo) {
23521             
23522             
23523             var store = this.formatCombo.store;
23524             this.formatCombo.setValue("");
23525             for (var i =0; i < ans.length;i++) {
23526                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23527                     // select it..
23528                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23529                     break;
23530                 }
23531             }
23532         }
23533         
23534         
23535         
23536         // hides menus... - so this cant be on a menu...
23537         Roo.bootstrap.MenuMgr.hideAll();
23538         */
23539         Roo.bootstrap.MenuMgr.hideAll();
23540         //this.editorsyncValue();
23541     },
23542     onFirstFocus: function() {
23543         this.buttons.each(function(item){
23544            item.enable();
23545         });
23546     },
23547     toggleSourceEdit : function(sourceEditMode){
23548         
23549           
23550         if(sourceEditMode){
23551             Roo.log("disabling buttons");
23552            this.buttons.each( function(item){
23553                 if(item.cmd != 'pencil'){
23554                     item.disable();
23555                 }
23556             });
23557           
23558         }else{
23559             Roo.log("enabling buttons");
23560             if(this.editorcore.initialized){
23561                 this.buttons.each( function(item){
23562                     item.enable();
23563                 });
23564             }
23565             
23566         }
23567         Roo.log("calling toggole on editor");
23568         // tell the editor that it's been pressed..
23569         this.editor.toggleSourceEdit(sourceEditMode);
23570        
23571     }
23572 });
23573
23574
23575
23576
23577
23578 /**
23579  * @class Roo.bootstrap.Table.AbstractSelectionModel
23580  * @extends Roo.util.Observable
23581  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23582  * implemented by descendant classes.  This class should not be directly instantiated.
23583  * @constructor
23584  */
23585 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23586     this.locked = false;
23587     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23588 };
23589
23590
23591 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23592     /** @ignore Called by the grid automatically. Do not call directly. */
23593     init : function(grid){
23594         this.grid = grid;
23595         this.initEvents();
23596     },
23597
23598     /**
23599      * Locks the selections.
23600      */
23601     lock : function(){
23602         this.locked = true;
23603     },
23604
23605     /**
23606      * Unlocks the selections.
23607      */
23608     unlock : function(){
23609         this.locked = false;
23610     },
23611
23612     /**
23613      * Returns true if the selections are locked.
23614      * @return {Boolean}
23615      */
23616     isLocked : function(){
23617         return this.locked;
23618     }
23619 });
23620 /**
23621  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23622  * @class Roo.bootstrap.Table.RowSelectionModel
23623  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23624  * It supports multiple selections and keyboard selection/navigation. 
23625  * @constructor
23626  * @param {Object} config
23627  */
23628
23629 Roo.bootstrap.Table.RowSelectionModel = function(config){
23630     Roo.apply(this, config);
23631     this.selections = new Roo.util.MixedCollection(false, function(o){
23632         return o.id;
23633     });
23634
23635     this.last = false;
23636     this.lastActive = false;
23637
23638     this.addEvents({
23639         /**
23640              * @event selectionchange
23641              * Fires when the selection changes
23642              * @param {SelectionModel} this
23643              */
23644             "selectionchange" : true,
23645         /**
23646              * @event afterselectionchange
23647              * Fires after the selection changes (eg. by key press or clicking)
23648              * @param {SelectionModel} this
23649              */
23650             "afterselectionchange" : true,
23651         /**
23652              * @event beforerowselect
23653              * Fires when a row is selected being selected, return false to cancel.
23654              * @param {SelectionModel} this
23655              * @param {Number} rowIndex The selected index
23656              * @param {Boolean} keepExisting False if other selections will be cleared
23657              */
23658             "beforerowselect" : true,
23659         /**
23660              * @event rowselect
23661              * Fires when a row is selected.
23662              * @param {SelectionModel} this
23663              * @param {Number} rowIndex The selected index
23664              * @param {Roo.data.Record} r The record
23665              */
23666             "rowselect" : true,
23667         /**
23668              * @event rowdeselect
23669              * Fires when a row is deselected.
23670              * @param {SelectionModel} this
23671              * @param {Number} rowIndex The selected index
23672              */
23673         "rowdeselect" : true
23674     });
23675     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23676     this.locked = false;
23677  };
23678
23679 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23680     /**
23681      * @cfg {Boolean} singleSelect
23682      * True to allow selection of only one row at a time (defaults to false)
23683      */
23684     singleSelect : false,
23685
23686     // private
23687     initEvents : function()
23688     {
23689
23690         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23691         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23692         //}else{ // allow click to work like normal
23693          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23694         //}
23695         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23696         this.grid.on("rowclick", this.handleMouseDown, this);
23697         
23698         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23699             "up" : function(e){
23700                 if(!e.shiftKey){
23701                     this.selectPrevious(e.shiftKey);
23702                 }else if(this.last !== false && this.lastActive !== false){
23703                     var last = this.last;
23704                     this.selectRange(this.last,  this.lastActive-1);
23705                     this.grid.getView().focusRow(this.lastActive);
23706                     if(last !== false){
23707                         this.last = last;
23708                     }
23709                 }else{
23710                     this.selectFirstRow();
23711                 }
23712                 this.fireEvent("afterselectionchange", this);
23713             },
23714             "down" : function(e){
23715                 if(!e.shiftKey){
23716                     this.selectNext(e.shiftKey);
23717                 }else if(this.last !== false && this.lastActive !== false){
23718                     var last = this.last;
23719                     this.selectRange(this.last,  this.lastActive+1);
23720                     this.grid.getView().focusRow(this.lastActive);
23721                     if(last !== false){
23722                         this.last = last;
23723                     }
23724                 }else{
23725                     this.selectFirstRow();
23726                 }
23727                 this.fireEvent("afterselectionchange", this);
23728             },
23729             scope: this
23730         });
23731         this.grid.store.on('load', function(){
23732             this.selections.clear();
23733         },this);
23734         /*
23735         var view = this.grid.view;
23736         view.on("refresh", this.onRefresh, this);
23737         view.on("rowupdated", this.onRowUpdated, this);
23738         view.on("rowremoved", this.onRemove, this);
23739         */
23740     },
23741
23742     // private
23743     onRefresh : function()
23744     {
23745         var ds = this.grid.store, i, v = this.grid.view;
23746         var s = this.selections;
23747         s.each(function(r){
23748             if((i = ds.indexOfId(r.id)) != -1){
23749                 v.onRowSelect(i);
23750             }else{
23751                 s.remove(r);
23752             }
23753         });
23754     },
23755
23756     // private
23757     onRemove : function(v, index, r){
23758         this.selections.remove(r);
23759     },
23760
23761     // private
23762     onRowUpdated : function(v, index, r){
23763         if(this.isSelected(r)){
23764             v.onRowSelect(index);
23765         }
23766     },
23767
23768     /**
23769      * Select records.
23770      * @param {Array} records The records to select
23771      * @param {Boolean} keepExisting (optional) True to keep existing selections
23772      */
23773     selectRecords : function(records, keepExisting)
23774     {
23775         if(!keepExisting){
23776             this.clearSelections();
23777         }
23778             var ds = this.grid.store;
23779         for(var i = 0, len = records.length; i < len; i++){
23780             this.selectRow(ds.indexOf(records[i]), true);
23781         }
23782     },
23783
23784     /**
23785      * Gets the number of selected rows.
23786      * @return {Number}
23787      */
23788     getCount : function(){
23789         return this.selections.length;
23790     },
23791
23792     /**
23793      * Selects the first row in the grid.
23794      */
23795     selectFirstRow : function(){
23796         this.selectRow(0);
23797     },
23798
23799     /**
23800      * Select the last row.
23801      * @param {Boolean} keepExisting (optional) True to keep existing selections
23802      */
23803     selectLastRow : function(keepExisting){
23804         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23805         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23806     },
23807
23808     /**
23809      * Selects the row immediately following the last selected row.
23810      * @param {Boolean} keepExisting (optional) True to keep existing selections
23811      */
23812     selectNext : function(keepExisting)
23813     {
23814             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23815             this.selectRow(this.last+1, keepExisting);
23816             this.grid.getView().focusRow(this.last);
23817         }
23818     },
23819
23820     /**
23821      * Selects the row that precedes the last selected row.
23822      * @param {Boolean} keepExisting (optional) True to keep existing selections
23823      */
23824     selectPrevious : function(keepExisting){
23825         if(this.last){
23826             this.selectRow(this.last-1, keepExisting);
23827             this.grid.getView().focusRow(this.last);
23828         }
23829     },
23830
23831     /**
23832      * Returns the selected records
23833      * @return {Array} Array of selected records
23834      */
23835     getSelections : function(){
23836         return [].concat(this.selections.items);
23837     },
23838
23839     /**
23840      * Returns the first selected record.
23841      * @return {Record}
23842      */
23843     getSelected : function(){
23844         return this.selections.itemAt(0);
23845     },
23846
23847
23848     /**
23849      * Clears all selections.
23850      */
23851     clearSelections : function(fast)
23852     {
23853         if(this.locked) {
23854             return;
23855         }
23856         if(fast !== true){
23857                 var ds = this.grid.store;
23858             var s = this.selections;
23859             s.each(function(r){
23860                 this.deselectRow(ds.indexOfId(r.id));
23861             }, this);
23862             s.clear();
23863         }else{
23864             this.selections.clear();
23865         }
23866         this.last = false;
23867     },
23868
23869
23870     /**
23871      * Selects all rows.
23872      */
23873     selectAll : function(){
23874         if(this.locked) {
23875             return;
23876         }
23877         this.selections.clear();
23878         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23879             this.selectRow(i, true);
23880         }
23881     },
23882
23883     /**
23884      * Returns True if there is a selection.
23885      * @return {Boolean}
23886      */
23887     hasSelection : function(){
23888         return this.selections.length > 0;
23889     },
23890
23891     /**
23892      * Returns True if the specified row is selected.
23893      * @param {Number/Record} record The record or index of the record to check
23894      * @return {Boolean}
23895      */
23896     isSelected : function(index){
23897             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23898         return (r && this.selections.key(r.id) ? true : false);
23899     },
23900
23901     /**
23902      * Returns True if the specified record id is selected.
23903      * @param {String} id The id of record to check
23904      * @return {Boolean}
23905      */
23906     isIdSelected : function(id){
23907         return (this.selections.key(id) ? true : false);
23908     },
23909
23910
23911     // private
23912     handleMouseDBClick : function(e, t){
23913         
23914     },
23915     // private
23916     handleMouseDown : function(e, t)
23917     {
23918             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23919         if(this.isLocked() || rowIndex < 0 ){
23920             return;
23921         };
23922         if(e.shiftKey && this.last !== false){
23923             var last = this.last;
23924             this.selectRange(last, rowIndex, e.ctrlKey);
23925             this.last = last; // reset the last
23926             t.focus();
23927     
23928         }else{
23929             var isSelected = this.isSelected(rowIndex);
23930             //Roo.log("select row:" + rowIndex);
23931             if(isSelected){
23932                 this.deselectRow(rowIndex);
23933             } else {
23934                         this.selectRow(rowIndex, true);
23935             }
23936     
23937             /*
23938                 if(e.button !== 0 && isSelected){
23939                 alert('rowIndex 2: ' + rowIndex);
23940                     view.focusRow(rowIndex);
23941                 }else if(e.ctrlKey && isSelected){
23942                     this.deselectRow(rowIndex);
23943                 }else if(!isSelected){
23944                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23945                     view.focusRow(rowIndex);
23946                 }
23947             */
23948         }
23949         this.fireEvent("afterselectionchange", this);
23950     },
23951     // private
23952     handleDragableRowClick :  function(grid, rowIndex, e) 
23953     {
23954         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23955             this.selectRow(rowIndex, false);
23956             grid.view.focusRow(rowIndex);
23957              this.fireEvent("afterselectionchange", this);
23958         }
23959     },
23960     
23961     /**
23962      * Selects multiple rows.
23963      * @param {Array} rows Array of the indexes of the row to select
23964      * @param {Boolean} keepExisting (optional) True to keep existing selections
23965      */
23966     selectRows : function(rows, keepExisting){
23967         if(!keepExisting){
23968             this.clearSelections();
23969         }
23970         for(var i = 0, len = rows.length; i < len; i++){
23971             this.selectRow(rows[i], true);
23972         }
23973     },
23974
23975     /**
23976      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23977      * @param {Number} startRow The index of the first row in the range
23978      * @param {Number} endRow The index of the last row in the range
23979      * @param {Boolean} keepExisting (optional) True to retain existing selections
23980      */
23981     selectRange : function(startRow, endRow, keepExisting){
23982         if(this.locked) {
23983             return;
23984         }
23985         if(!keepExisting){
23986             this.clearSelections();
23987         }
23988         if(startRow <= endRow){
23989             for(var i = startRow; i <= endRow; i++){
23990                 this.selectRow(i, true);
23991             }
23992         }else{
23993             for(var i = startRow; i >= endRow; i--){
23994                 this.selectRow(i, true);
23995             }
23996         }
23997     },
23998
23999     /**
24000      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24001      * @param {Number} startRow The index of the first row in the range
24002      * @param {Number} endRow The index of the last row in the range
24003      */
24004     deselectRange : function(startRow, endRow, preventViewNotify){
24005         if(this.locked) {
24006             return;
24007         }
24008         for(var i = startRow; i <= endRow; i++){
24009             this.deselectRow(i, preventViewNotify);
24010         }
24011     },
24012
24013     /**
24014      * Selects a row.
24015      * @param {Number} row The index of the row to select
24016      * @param {Boolean} keepExisting (optional) True to keep existing selections
24017      */
24018     selectRow : function(index, keepExisting, preventViewNotify)
24019     {
24020             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24021             return;
24022         }
24023         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24024             if(!keepExisting || this.singleSelect){
24025                 this.clearSelections();
24026             }
24027             
24028             var r = this.grid.store.getAt(index);
24029             //console.log('selectRow - record id :' + r.id);
24030             
24031             this.selections.add(r);
24032             this.last = this.lastActive = index;
24033             if(!preventViewNotify){
24034                 var proxy = new Roo.Element(
24035                                 this.grid.getRowDom(index)
24036                 );
24037                 proxy.addClass('bg-info info');
24038             }
24039             this.fireEvent("rowselect", this, index, r);
24040             this.fireEvent("selectionchange", this);
24041         }
24042     },
24043
24044     /**
24045      * Deselects a row.
24046      * @param {Number} row The index of the row to deselect
24047      */
24048     deselectRow : function(index, preventViewNotify)
24049     {
24050         if(this.locked) {
24051             return;
24052         }
24053         if(this.last == index){
24054             this.last = false;
24055         }
24056         if(this.lastActive == index){
24057             this.lastActive = false;
24058         }
24059         
24060         var r = this.grid.store.getAt(index);
24061         if (!r) {
24062             return;
24063         }
24064         
24065         this.selections.remove(r);
24066         //.console.log('deselectRow - record id :' + r.id);
24067         if(!preventViewNotify){
24068         
24069             var proxy = new Roo.Element(
24070                 this.grid.getRowDom(index)
24071             );
24072             proxy.removeClass('bg-info info');
24073         }
24074         this.fireEvent("rowdeselect", this, index);
24075         this.fireEvent("selectionchange", this);
24076     },
24077
24078     // private
24079     restoreLast : function(){
24080         if(this._last){
24081             this.last = this._last;
24082         }
24083     },
24084
24085     // private
24086     acceptsNav : function(row, col, cm){
24087         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24088     },
24089
24090     // private
24091     onEditorKey : function(field, e){
24092         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24093         if(k == e.TAB){
24094             e.stopEvent();
24095             ed.completeEdit();
24096             if(e.shiftKey){
24097                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24098             }else{
24099                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24100             }
24101         }else if(k == e.ENTER && !e.ctrlKey){
24102             e.stopEvent();
24103             ed.completeEdit();
24104             if(e.shiftKey){
24105                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24106             }else{
24107                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24108             }
24109         }else if(k == e.ESC){
24110             ed.cancelEdit();
24111         }
24112         if(newCell){
24113             g.startEditing(newCell[0], newCell[1]);
24114         }
24115     }
24116 });
24117 /*
24118  * Based on:
24119  * Ext JS Library 1.1.1
24120  * Copyright(c) 2006-2007, Ext JS, LLC.
24121  *
24122  * Originally Released Under LGPL - original licence link has changed is not relivant.
24123  *
24124  * Fork - LGPL
24125  * <script type="text/javascript">
24126  */
24127  
24128 /**
24129  * @class Roo.bootstrap.PagingToolbar
24130  * @extends Roo.bootstrap.NavSimplebar
24131  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24132  * @constructor
24133  * Create a new PagingToolbar
24134  * @param {Object} config The config object
24135  * @param {Roo.data.Store} store
24136  */
24137 Roo.bootstrap.PagingToolbar = function(config)
24138 {
24139     // old args format still supported... - xtype is prefered..
24140         // created from xtype...
24141     
24142     this.ds = config.dataSource;
24143     
24144     if (config.store && !this.ds) {
24145         this.store= Roo.factory(config.store, Roo.data);
24146         this.ds = this.store;
24147         this.ds.xmodule = this.xmodule || false;
24148     }
24149     
24150     this.toolbarItems = [];
24151     if (config.items) {
24152         this.toolbarItems = config.items;
24153     }
24154     
24155     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24156     
24157     this.cursor = 0;
24158     
24159     if (this.ds) { 
24160         this.bind(this.ds);
24161     }
24162     
24163     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24164     
24165 };
24166
24167 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24168     /**
24169      * @cfg {Roo.data.Store} dataSource
24170      * The underlying data store providing the paged data
24171      */
24172     /**
24173      * @cfg {String/HTMLElement/Element} container
24174      * container The id or element that will contain the toolbar
24175      */
24176     /**
24177      * @cfg {Boolean} displayInfo
24178      * True to display the displayMsg (defaults to false)
24179      */
24180     /**
24181      * @cfg {Number} pageSize
24182      * The number of records to display per page (defaults to 20)
24183      */
24184     pageSize: 20,
24185     /**
24186      * @cfg {String} displayMsg
24187      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24188      */
24189     displayMsg : 'Displaying {0} - {1} of {2}',
24190     /**
24191      * @cfg {String} emptyMsg
24192      * The message to display when no records are found (defaults to "No data to display")
24193      */
24194     emptyMsg : 'No data to display',
24195     /**
24196      * Customizable piece of the default paging text (defaults to "Page")
24197      * @type String
24198      */
24199     beforePageText : "Page",
24200     /**
24201      * Customizable piece of the default paging text (defaults to "of %0")
24202      * @type String
24203      */
24204     afterPageText : "of {0}",
24205     /**
24206      * Customizable piece of the default paging text (defaults to "First Page")
24207      * @type String
24208      */
24209     firstText : "First Page",
24210     /**
24211      * Customizable piece of the default paging text (defaults to "Previous Page")
24212      * @type String
24213      */
24214     prevText : "Previous Page",
24215     /**
24216      * Customizable piece of the default paging text (defaults to "Next Page")
24217      * @type String
24218      */
24219     nextText : "Next Page",
24220     /**
24221      * Customizable piece of the default paging text (defaults to "Last Page")
24222      * @type String
24223      */
24224     lastText : "Last Page",
24225     /**
24226      * Customizable piece of the default paging text (defaults to "Refresh")
24227      * @type String
24228      */
24229     refreshText : "Refresh",
24230
24231     buttons : false,
24232     // private
24233     onRender : function(ct, position) 
24234     {
24235         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24236         this.navgroup.parentId = this.id;
24237         this.navgroup.onRender(this.el, null);
24238         // add the buttons to the navgroup
24239         
24240         if(this.displayInfo){
24241             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24242             this.displayEl = this.el.select('.x-paging-info', true).first();
24243 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24244 //            this.displayEl = navel.el.select('span',true).first();
24245         }
24246         
24247         var _this = this;
24248         
24249         if(this.buttons){
24250             Roo.each(_this.buttons, function(e){ // this might need to use render????
24251                Roo.factory(e).onRender(_this.el, null);
24252             });
24253         }
24254             
24255         Roo.each(_this.toolbarItems, function(e) {
24256             _this.navgroup.addItem(e);
24257         });
24258         
24259         
24260         this.first = this.navgroup.addItem({
24261             tooltip: this.firstText,
24262             cls: "prev",
24263             icon : 'fa fa-backward',
24264             disabled: true,
24265             preventDefault: true,
24266             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24267         });
24268         
24269         this.prev =  this.navgroup.addItem({
24270             tooltip: this.prevText,
24271             cls: "prev",
24272             icon : 'fa fa-step-backward',
24273             disabled: true,
24274             preventDefault: true,
24275             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24276         });
24277     //this.addSeparator();
24278         
24279         
24280         var field = this.navgroup.addItem( {
24281             tagtype : 'span',
24282             cls : 'x-paging-position',
24283             
24284             html : this.beforePageText  +
24285                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24286                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24287          } ); //?? escaped?
24288         
24289         this.field = field.el.select('input', true).first();
24290         this.field.on("keydown", this.onPagingKeydown, this);
24291         this.field.on("focus", function(){this.dom.select();});
24292     
24293     
24294         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24295         //this.field.setHeight(18);
24296         //this.addSeparator();
24297         this.next = this.navgroup.addItem({
24298             tooltip: this.nextText,
24299             cls: "next",
24300             html : ' <i class="fa fa-step-forward">',
24301             disabled: true,
24302             preventDefault: true,
24303             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24304         });
24305         this.last = this.navgroup.addItem({
24306             tooltip: this.lastText,
24307             icon : 'fa fa-forward',
24308             cls: "next",
24309             disabled: true,
24310             preventDefault: true,
24311             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24312         });
24313     //this.addSeparator();
24314         this.loading = this.navgroup.addItem({
24315             tooltip: this.refreshText,
24316             icon: 'fa fa-refresh',
24317             preventDefault: true,
24318             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24319         });
24320         
24321     },
24322
24323     // private
24324     updateInfo : function(){
24325         if(this.displayEl){
24326             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24327             var msg = count == 0 ?
24328                 this.emptyMsg :
24329                 String.format(
24330                     this.displayMsg,
24331                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24332                 );
24333             this.displayEl.update(msg);
24334         }
24335     },
24336
24337     // private
24338     onLoad : function(ds, r, o)
24339     {
24340         this.cursor = o.params ? o.params.start : 0;
24341         var d = this.getPageData(),
24342             ap = d.activePage,
24343             ps = d.pages;
24344         
24345         
24346         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24347         this.field.dom.value = ap;
24348         this.first.setDisabled(ap == 1);
24349         this.prev.setDisabled(ap == 1);
24350         this.next.setDisabled(ap == ps);
24351         this.last.setDisabled(ap == ps);
24352         this.loading.enable();
24353         this.updateInfo();
24354     },
24355
24356     // private
24357     getPageData : function(){
24358         var total = this.ds.getTotalCount();
24359         return {
24360             total : total,
24361             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24362             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24363         };
24364     },
24365
24366     // private
24367     onLoadError : function(){
24368         this.loading.enable();
24369     },
24370
24371     // private
24372     onPagingKeydown : function(e){
24373         var k = e.getKey();
24374         var d = this.getPageData();
24375         if(k == e.RETURN){
24376             var v = this.field.dom.value, pageNum;
24377             if(!v || isNaN(pageNum = parseInt(v, 10))){
24378                 this.field.dom.value = d.activePage;
24379                 return;
24380             }
24381             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24382             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24383             e.stopEvent();
24384         }
24385         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))
24386         {
24387           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24388           this.field.dom.value = pageNum;
24389           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24390           e.stopEvent();
24391         }
24392         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24393         {
24394           var v = this.field.dom.value, pageNum; 
24395           var increment = (e.shiftKey) ? 10 : 1;
24396           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24397                 increment *= -1;
24398           }
24399           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24400             this.field.dom.value = d.activePage;
24401             return;
24402           }
24403           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24404           {
24405             this.field.dom.value = parseInt(v, 10) + increment;
24406             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24407             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24408           }
24409           e.stopEvent();
24410         }
24411     },
24412
24413     // private
24414     beforeLoad : function(){
24415         if(this.loading){
24416             this.loading.disable();
24417         }
24418     },
24419
24420     // private
24421     onClick : function(which){
24422         
24423         var ds = this.ds;
24424         if (!ds) {
24425             return;
24426         }
24427         
24428         switch(which){
24429             case "first":
24430                 ds.load({params:{start: 0, limit: this.pageSize}});
24431             break;
24432             case "prev":
24433                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24434             break;
24435             case "next":
24436                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24437             break;
24438             case "last":
24439                 var total = ds.getTotalCount();
24440                 var extra = total % this.pageSize;
24441                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24442                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24443             break;
24444             case "refresh":
24445                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24446             break;
24447         }
24448     },
24449
24450     /**
24451      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24452      * @param {Roo.data.Store} store The data store to unbind
24453      */
24454     unbind : function(ds){
24455         ds.un("beforeload", this.beforeLoad, this);
24456         ds.un("load", this.onLoad, this);
24457         ds.un("loadexception", this.onLoadError, this);
24458         ds.un("remove", this.updateInfo, this);
24459         ds.un("add", this.updateInfo, this);
24460         this.ds = undefined;
24461     },
24462
24463     /**
24464      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24465      * @param {Roo.data.Store} store The data store to bind
24466      */
24467     bind : function(ds){
24468         ds.on("beforeload", this.beforeLoad, this);
24469         ds.on("load", this.onLoad, this);
24470         ds.on("loadexception", this.onLoadError, this);
24471         ds.on("remove", this.updateInfo, this);
24472         ds.on("add", this.updateInfo, this);
24473         this.ds = ds;
24474     }
24475 });/*
24476  * - LGPL
24477  *
24478  * element
24479  * 
24480  */
24481
24482 /**
24483  * @class Roo.bootstrap.MessageBar
24484  * @extends Roo.bootstrap.Component
24485  * Bootstrap MessageBar class
24486  * @cfg {String} html contents of the MessageBar
24487  * @cfg {String} weight (info | success | warning | danger) default info
24488  * @cfg {String} beforeClass insert the bar before the given class
24489  * @cfg {Boolean} closable (true | false) default false
24490  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24491  * 
24492  * @constructor
24493  * Create a new Element
24494  * @param {Object} config The config object
24495  */
24496
24497 Roo.bootstrap.MessageBar = function(config){
24498     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24499 };
24500
24501 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24502     
24503     html: '',
24504     weight: 'info',
24505     closable: false,
24506     fixed: false,
24507     beforeClass: 'bootstrap-sticky-wrap',
24508     
24509     getAutoCreate : function(){
24510         
24511         var cfg = {
24512             tag: 'div',
24513             cls: 'alert alert-dismissable alert-' + this.weight,
24514             cn: [
24515                 {
24516                     tag: 'span',
24517                     cls: 'message',
24518                     html: this.html || ''
24519                 }
24520             ]
24521         };
24522         
24523         if(this.fixed){
24524             cfg.cls += ' alert-messages-fixed';
24525         }
24526         
24527         if(this.closable){
24528             cfg.cn.push({
24529                 tag: 'button',
24530                 cls: 'close',
24531                 html: 'x'
24532             });
24533         }
24534         
24535         return cfg;
24536     },
24537     
24538     onRender : function(ct, position)
24539     {
24540         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24541         
24542         if(!this.el){
24543             var cfg = Roo.apply({},  this.getAutoCreate());
24544             cfg.id = Roo.id();
24545             
24546             if (this.cls) {
24547                 cfg.cls += ' ' + this.cls;
24548             }
24549             if (this.style) {
24550                 cfg.style = this.style;
24551             }
24552             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24553             
24554             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24555         }
24556         
24557         this.el.select('>button.close').on('click', this.hide, this);
24558         
24559     },
24560     
24561     show : function()
24562     {
24563         if (!this.rendered) {
24564             this.render();
24565         }
24566         
24567         this.el.show();
24568         
24569         this.fireEvent('show', this);
24570         
24571     },
24572     
24573     hide : function()
24574     {
24575         if (!this.rendered) {
24576             this.render();
24577         }
24578         
24579         this.el.hide();
24580         
24581         this.fireEvent('hide', this);
24582     },
24583     
24584     update : function()
24585     {
24586 //        var e = this.el.dom.firstChild;
24587 //        
24588 //        if(this.closable){
24589 //            e = e.nextSibling;
24590 //        }
24591 //        
24592 //        e.data = this.html || '';
24593
24594         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24595     }
24596    
24597 });
24598
24599  
24600
24601      /*
24602  * - LGPL
24603  *
24604  * Graph
24605  * 
24606  */
24607
24608
24609 /**
24610  * @class Roo.bootstrap.Graph
24611  * @extends Roo.bootstrap.Component
24612  * Bootstrap Graph class
24613 > Prameters
24614  -sm {number} sm 4
24615  -md {number} md 5
24616  @cfg {String} graphtype  bar | vbar | pie
24617  @cfg {number} g_x coodinator | centre x (pie)
24618  @cfg {number} g_y coodinator | centre y (pie)
24619  @cfg {number} g_r radius (pie)
24620  @cfg {number} g_height height of the chart (respected by all elements in the set)
24621  @cfg {number} g_width width of the chart (respected by all elements in the set)
24622  @cfg {Object} title The title of the chart
24623     
24624  -{Array}  values
24625  -opts (object) options for the chart 
24626      o {
24627      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24628      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24629      o vgutter (number)
24630      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.
24631      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24632      o to
24633      o stretch (boolean)
24634      o }
24635  -opts (object) options for the pie
24636      o{
24637      o cut
24638      o startAngle (number)
24639      o endAngle (number)
24640      } 
24641  *
24642  * @constructor
24643  * Create a new Input
24644  * @param {Object} config The config object
24645  */
24646
24647 Roo.bootstrap.Graph = function(config){
24648     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24649     
24650     this.addEvents({
24651         // img events
24652         /**
24653          * @event click
24654          * The img click event for the img.
24655          * @param {Roo.EventObject} e
24656          */
24657         "click" : true
24658     });
24659 };
24660
24661 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24662     
24663     sm: 4,
24664     md: 5,
24665     graphtype: 'bar',
24666     g_height: 250,
24667     g_width: 400,
24668     g_x: 50,
24669     g_y: 50,
24670     g_r: 30,
24671     opts:{
24672         //g_colors: this.colors,
24673         g_type: 'soft',
24674         g_gutter: '20%'
24675
24676     },
24677     title : false,
24678
24679     getAutoCreate : function(){
24680         
24681         var cfg = {
24682             tag: 'div',
24683             html : null
24684         };
24685         
24686         
24687         return  cfg;
24688     },
24689
24690     onRender : function(ct,position){
24691         
24692         
24693         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24694         
24695         if (typeof(Raphael) == 'undefined') {
24696             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24697             return;
24698         }
24699         
24700         this.raphael = Raphael(this.el.dom);
24701         
24702                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24703                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24704                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24705                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24706                 /*
24707                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24708                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24709                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24710                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24711                 
24712                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24713                 r.barchart(330, 10, 300, 220, data1);
24714                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24715                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24716                 */
24717                 
24718                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24719                 // r.barchart(30, 30, 560, 250,  xdata, {
24720                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24721                 //     axis : "0 0 1 1",
24722                 //     axisxlabels :  xdata
24723                 //     //yvalues : cols,
24724                    
24725                 // });
24726 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24727 //        
24728 //        this.load(null,xdata,{
24729 //                axis : "0 0 1 1",
24730 //                axisxlabels :  xdata
24731 //                });
24732
24733     },
24734
24735     load : function(graphtype,xdata,opts)
24736     {
24737         this.raphael.clear();
24738         if(!graphtype) {
24739             graphtype = this.graphtype;
24740         }
24741         if(!opts){
24742             opts = this.opts;
24743         }
24744         var r = this.raphael,
24745             fin = function () {
24746                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24747             },
24748             fout = function () {
24749                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24750             },
24751             pfin = function() {
24752                 this.sector.stop();
24753                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24754
24755                 if (this.label) {
24756                     this.label[0].stop();
24757                     this.label[0].attr({ r: 7.5 });
24758                     this.label[1].attr({ "font-weight": 800 });
24759                 }
24760             },
24761             pfout = function() {
24762                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24763
24764                 if (this.label) {
24765                     this.label[0].animate({ r: 5 }, 500, "bounce");
24766                     this.label[1].attr({ "font-weight": 400 });
24767                 }
24768             };
24769
24770         switch(graphtype){
24771             case 'bar':
24772                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24773                 break;
24774             case 'hbar':
24775                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24776                 break;
24777             case 'pie':
24778 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24779 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24780 //            
24781                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24782                 
24783                 break;
24784
24785         }
24786         
24787         if(this.title){
24788             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24789         }
24790         
24791     },
24792     
24793     setTitle: function(o)
24794     {
24795         this.title = o;
24796     },
24797     
24798     initEvents: function() {
24799         
24800         if(!this.href){
24801             this.el.on('click', this.onClick, this);
24802         }
24803     },
24804     
24805     onClick : function(e)
24806     {
24807         Roo.log('img onclick');
24808         this.fireEvent('click', this, e);
24809     }
24810    
24811 });
24812
24813  
24814 /*
24815  * - LGPL
24816  *
24817  * numberBox
24818  * 
24819  */
24820 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24821
24822 /**
24823  * @class Roo.bootstrap.dash.NumberBox
24824  * @extends Roo.bootstrap.Component
24825  * Bootstrap NumberBox class
24826  * @cfg {String} headline Box headline
24827  * @cfg {String} content Box content
24828  * @cfg {String} icon Box icon
24829  * @cfg {String} footer Footer text
24830  * @cfg {String} fhref Footer href
24831  * 
24832  * @constructor
24833  * Create a new NumberBox
24834  * @param {Object} config The config object
24835  */
24836
24837
24838 Roo.bootstrap.dash.NumberBox = function(config){
24839     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24840     
24841 };
24842
24843 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24844     
24845     headline : '',
24846     content : '',
24847     icon : '',
24848     footer : '',
24849     fhref : '',
24850     ficon : '',
24851     
24852     getAutoCreate : function(){
24853         
24854         var cfg = {
24855             tag : 'div',
24856             cls : 'small-box ',
24857             cn : [
24858                 {
24859                     tag : 'div',
24860                     cls : 'inner',
24861                     cn :[
24862                         {
24863                             tag : 'h3',
24864                             cls : 'roo-headline',
24865                             html : this.headline
24866                         },
24867                         {
24868                             tag : 'p',
24869                             cls : 'roo-content',
24870                             html : this.content
24871                         }
24872                     ]
24873                 }
24874             ]
24875         };
24876         
24877         if(this.icon){
24878             cfg.cn.push({
24879                 tag : 'div',
24880                 cls : 'icon',
24881                 cn :[
24882                     {
24883                         tag : 'i',
24884                         cls : 'ion ' + this.icon
24885                     }
24886                 ]
24887             });
24888         }
24889         
24890         if(this.footer){
24891             var footer = {
24892                 tag : 'a',
24893                 cls : 'small-box-footer',
24894                 href : this.fhref || '#',
24895                 html : this.footer
24896             };
24897             
24898             cfg.cn.push(footer);
24899             
24900         }
24901         
24902         return  cfg;
24903     },
24904
24905     onRender : function(ct,position){
24906         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24907
24908
24909        
24910                 
24911     },
24912
24913     setHeadline: function (value)
24914     {
24915         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24916     },
24917     
24918     setFooter: function (value, href)
24919     {
24920         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24921         
24922         if(href){
24923             this.el.select('a.small-box-footer',true).first().attr('href', href);
24924         }
24925         
24926     },
24927
24928     setContent: function (value)
24929     {
24930         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24931     },
24932
24933     initEvents: function() 
24934     {   
24935         
24936     }
24937     
24938 });
24939
24940  
24941 /*
24942  * - LGPL
24943  *
24944  * TabBox
24945  * 
24946  */
24947 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24948
24949 /**
24950  * @class Roo.bootstrap.dash.TabBox
24951  * @extends Roo.bootstrap.Component
24952  * Bootstrap TabBox class
24953  * @cfg {String} title Title of the TabBox
24954  * @cfg {String} icon Icon of the TabBox
24955  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24956  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24957  * 
24958  * @constructor
24959  * Create a new TabBox
24960  * @param {Object} config The config object
24961  */
24962
24963
24964 Roo.bootstrap.dash.TabBox = function(config){
24965     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24966     this.addEvents({
24967         // raw events
24968         /**
24969          * @event addpane
24970          * When a pane is added
24971          * @param {Roo.bootstrap.dash.TabPane} pane
24972          */
24973         "addpane" : true,
24974         /**
24975          * @event activatepane
24976          * When a pane is activated
24977          * @param {Roo.bootstrap.dash.TabPane} pane
24978          */
24979         "activatepane" : true
24980         
24981          
24982     });
24983     
24984     this.panes = [];
24985 };
24986
24987 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24988
24989     title : '',
24990     icon : false,
24991     showtabs : true,
24992     tabScrollable : false,
24993     
24994     getChildContainer : function()
24995     {
24996         return this.el.select('.tab-content', true).first();
24997     },
24998     
24999     getAutoCreate : function(){
25000         
25001         var header = {
25002             tag: 'li',
25003             cls: 'pull-left header',
25004             html: this.title,
25005             cn : []
25006         };
25007         
25008         if(this.icon){
25009             header.cn.push({
25010                 tag: 'i',
25011                 cls: 'fa ' + this.icon
25012             });
25013         }
25014         
25015         var h = {
25016             tag: 'ul',
25017             cls: 'nav nav-tabs pull-right',
25018             cn: [
25019                 header
25020             ]
25021         };
25022         
25023         if(this.tabScrollable){
25024             h = {
25025                 tag: 'div',
25026                 cls: 'tab-header',
25027                 cn: [
25028                     {
25029                         tag: 'ul',
25030                         cls: 'nav nav-tabs pull-right',
25031                         cn: [
25032                             header
25033                         ]
25034                     }
25035                 ]
25036             };
25037         }
25038         
25039         var cfg = {
25040             tag: 'div',
25041             cls: 'nav-tabs-custom',
25042             cn: [
25043                 h,
25044                 {
25045                     tag: 'div',
25046                     cls: 'tab-content no-padding',
25047                     cn: []
25048                 }
25049             ]
25050         };
25051
25052         return  cfg;
25053     },
25054     initEvents : function()
25055     {
25056         //Roo.log('add add pane handler');
25057         this.on('addpane', this.onAddPane, this);
25058     },
25059      /**
25060      * Updates the box title
25061      * @param {String} html to set the title to.
25062      */
25063     setTitle : function(value)
25064     {
25065         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25066     },
25067     onAddPane : function(pane)
25068     {
25069         this.panes.push(pane);
25070         //Roo.log('addpane');
25071         //Roo.log(pane);
25072         // tabs are rendere left to right..
25073         if(!this.showtabs){
25074             return;
25075         }
25076         
25077         var ctr = this.el.select('.nav-tabs', true).first();
25078          
25079          
25080         var existing = ctr.select('.nav-tab',true);
25081         var qty = existing.getCount();;
25082         
25083         
25084         var tab = ctr.createChild({
25085             tag : 'li',
25086             cls : 'nav-tab' + (qty ? '' : ' active'),
25087             cn : [
25088                 {
25089                     tag : 'a',
25090                     href:'#',
25091                     html : pane.title
25092                 }
25093             ]
25094         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25095         pane.tab = tab;
25096         
25097         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25098         if (!qty) {
25099             pane.el.addClass('active');
25100         }
25101         
25102                 
25103     },
25104     onTabClick : function(ev,un,ob,pane)
25105     {
25106         //Roo.log('tab - prev default');
25107         ev.preventDefault();
25108         
25109         
25110         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25111         pane.tab.addClass('active');
25112         //Roo.log(pane.title);
25113         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25114         // technically we should have a deactivate event.. but maybe add later.
25115         // and it should not de-activate the selected tab...
25116         this.fireEvent('activatepane', pane);
25117         pane.el.addClass('active');
25118         pane.fireEvent('activate');
25119         
25120         
25121     },
25122     
25123     getActivePane : function()
25124     {
25125         var r = false;
25126         Roo.each(this.panes, function(p) {
25127             if(p.el.hasClass('active')){
25128                 r = p;
25129                 return false;
25130             }
25131             
25132             return;
25133         });
25134         
25135         return r;
25136     }
25137     
25138     
25139 });
25140
25141  
25142 /*
25143  * - LGPL
25144  *
25145  * Tab pane
25146  * 
25147  */
25148 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25149 /**
25150  * @class Roo.bootstrap.TabPane
25151  * @extends Roo.bootstrap.Component
25152  * Bootstrap TabPane class
25153  * @cfg {Boolean} active (false | true) Default false
25154  * @cfg {String} title title of panel
25155
25156  * 
25157  * @constructor
25158  * Create a new TabPane
25159  * @param {Object} config The config object
25160  */
25161
25162 Roo.bootstrap.dash.TabPane = function(config){
25163     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25164     
25165     this.addEvents({
25166         // raw events
25167         /**
25168          * @event activate
25169          * When a pane is activated
25170          * @param {Roo.bootstrap.dash.TabPane} pane
25171          */
25172         "activate" : true
25173          
25174     });
25175 };
25176
25177 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25178     
25179     active : false,
25180     title : '',
25181     
25182     // the tabBox that this is attached to.
25183     tab : false,
25184      
25185     getAutoCreate : function() 
25186     {
25187         var cfg = {
25188             tag: 'div',
25189             cls: 'tab-pane'
25190         };
25191         
25192         if(this.active){
25193             cfg.cls += ' active';
25194         }
25195         
25196         return cfg;
25197     },
25198     initEvents  : function()
25199     {
25200         //Roo.log('trigger add pane handler');
25201         this.parent().fireEvent('addpane', this)
25202     },
25203     
25204      /**
25205      * Updates the tab title 
25206      * @param {String} html to set the title to.
25207      */
25208     setTitle: function(str)
25209     {
25210         if (!this.tab) {
25211             return;
25212         }
25213         this.title = str;
25214         this.tab.select('a', true).first().dom.innerHTML = str;
25215         
25216     }
25217     
25218     
25219     
25220 });
25221
25222  
25223
25224
25225  /*
25226  * - LGPL
25227  *
25228  * menu
25229  * 
25230  */
25231 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25232
25233 /**
25234  * @class Roo.bootstrap.menu.Menu
25235  * @extends Roo.bootstrap.Component
25236  * Bootstrap Menu class - container for Menu
25237  * @cfg {String} html Text of the menu
25238  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25239  * @cfg {String} icon Font awesome icon
25240  * @cfg {String} pos Menu align to (top | bottom) default bottom
25241  * 
25242  * 
25243  * @constructor
25244  * Create a new Menu
25245  * @param {Object} config The config object
25246  */
25247
25248
25249 Roo.bootstrap.menu.Menu = function(config){
25250     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25251     
25252     this.addEvents({
25253         /**
25254          * @event beforeshow
25255          * Fires before this menu is displayed
25256          * @param {Roo.bootstrap.menu.Menu} this
25257          */
25258         beforeshow : true,
25259         /**
25260          * @event beforehide
25261          * Fires before this menu is hidden
25262          * @param {Roo.bootstrap.menu.Menu} this
25263          */
25264         beforehide : true,
25265         /**
25266          * @event show
25267          * Fires after this menu is displayed
25268          * @param {Roo.bootstrap.menu.Menu} this
25269          */
25270         show : true,
25271         /**
25272          * @event hide
25273          * Fires after this menu is hidden
25274          * @param {Roo.bootstrap.menu.Menu} this
25275          */
25276         hide : true,
25277         /**
25278          * @event click
25279          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25280          * @param {Roo.bootstrap.menu.Menu} this
25281          * @param {Roo.EventObject} e
25282          */
25283         click : true
25284     });
25285     
25286 };
25287
25288 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25289     
25290     submenu : false,
25291     html : '',
25292     weight : 'default',
25293     icon : false,
25294     pos : 'bottom',
25295     
25296     
25297     getChildContainer : function() {
25298         if(this.isSubMenu){
25299             return this.el;
25300         }
25301         
25302         return this.el.select('ul.dropdown-menu', true).first();  
25303     },
25304     
25305     getAutoCreate : function()
25306     {
25307         var text = [
25308             {
25309                 tag : 'span',
25310                 cls : 'roo-menu-text',
25311                 html : this.html
25312             }
25313         ];
25314         
25315         if(this.icon){
25316             text.unshift({
25317                 tag : 'i',
25318                 cls : 'fa ' + this.icon
25319             })
25320         }
25321         
25322         
25323         var cfg = {
25324             tag : 'div',
25325             cls : 'btn-group',
25326             cn : [
25327                 {
25328                     tag : 'button',
25329                     cls : 'dropdown-button btn btn-' + this.weight,
25330                     cn : text
25331                 },
25332                 {
25333                     tag : 'button',
25334                     cls : 'dropdown-toggle btn btn-' + this.weight,
25335                     cn : [
25336                         {
25337                             tag : 'span',
25338                             cls : 'caret'
25339                         }
25340                     ]
25341                 },
25342                 {
25343                     tag : 'ul',
25344                     cls : 'dropdown-menu'
25345                 }
25346             ]
25347             
25348         };
25349         
25350         if(this.pos == 'top'){
25351             cfg.cls += ' dropup';
25352         }
25353         
25354         if(this.isSubMenu){
25355             cfg = {
25356                 tag : 'ul',
25357                 cls : 'dropdown-menu'
25358             }
25359         }
25360         
25361         return cfg;
25362     },
25363     
25364     onRender : function(ct, position)
25365     {
25366         this.isSubMenu = ct.hasClass('dropdown-submenu');
25367         
25368         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25369     },
25370     
25371     initEvents : function() 
25372     {
25373         if(this.isSubMenu){
25374             return;
25375         }
25376         
25377         this.hidden = true;
25378         
25379         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25380         this.triggerEl.on('click', this.onTriggerPress, this);
25381         
25382         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25383         this.buttonEl.on('click', this.onClick, this);
25384         
25385     },
25386     
25387     list : function()
25388     {
25389         if(this.isSubMenu){
25390             return this.el;
25391         }
25392         
25393         return this.el.select('ul.dropdown-menu', true).first();
25394     },
25395     
25396     onClick : function(e)
25397     {
25398         this.fireEvent("click", this, e);
25399     },
25400     
25401     onTriggerPress  : function(e)
25402     {   
25403         if (this.isVisible()) {
25404             this.hide();
25405         } else {
25406             this.show();
25407         }
25408     },
25409     
25410     isVisible : function(){
25411         return !this.hidden;
25412     },
25413     
25414     show : function()
25415     {
25416         this.fireEvent("beforeshow", this);
25417         
25418         this.hidden = false;
25419         this.el.addClass('open');
25420         
25421         Roo.get(document).on("mouseup", this.onMouseUp, this);
25422         
25423         this.fireEvent("show", this);
25424         
25425         
25426     },
25427     
25428     hide : function()
25429     {
25430         this.fireEvent("beforehide", this);
25431         
25432         this.hidden = true;
25433         this.el.removeClass('open');
25434         
25435         Roo.get(document).un("mouseup", this.onMouseUp);
25436         
25437         this.fireEvent("hide", this);
25438     },
25439     
25440     onMouseUp : function()
25441     {
25442         this.hide();
25443     }
25444     
25445 });
25446
25447  
25448  /*
25449  * - LGPL
25450  *
25451  * menu item
25452  * 
25453  */
25454 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25455
25456 /**
25457  * @class Roo.bootstrap.menu.Item
25458  * @extends Roo.bootstrap.Component
25459  * Bootstrap MenuItem class
25460  * @cfg {Boolean} submenu (true | false) default false
25461  * @cfg {String} html text of the item
25462  * @cfg {String} href the link
25463  * @cfg {Boolean} disable (true | false) default false
25464  * @cfg {Boolean} preventDefault (true | false) default true
25465  * @cfg {String} icon Font awesome icon
25466  * @cfg {String} pos Submenu align to (left | right) default right 
25467  * 
25468  * 
25469  * @constructor
25470  * Create a new Item
25471  * @param {Object} config The config object
25472  */
25473
25474
25475 Roo.bootstrap.menu.Item = function(config){
25476     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25477     this.addEvents({
25478         /**
25479          * @event mouseover
25480          * Fires when the mouse is hovering over this menu
25481          * @param {Roo.bootstrap.menu.Item} this
25482          * @param {Roo.EventObject} e
25483          */
25484         mouseover : true,
25485         /**
25486          * @event mouseout
25487          * Fires when the mouse exits this menu
25488          * @param {Roo.bootstrap.menu.Item} this
25489          * @param {Roo.EventObject} e
25490          */
25491         mouseout : true,
25492         // raw events
25493         /**
25494          * @event click
25495          * The raw click event for the entire grid.
25496          * @param {Roo.EventObject} e
25497          */
25498         click : true
25499     });
25500 };
25501
25502 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25503     
25504     submenu : false,
25505     href : '',
25506     html : '',
25507     preventDefault: true,
25508     disable : false,
25509     icon : false,
25510     pos : 'right',
25511     
25512     getAutoCreate : function()
25513     {
25514         var text = [
25515             {
25516                 tag : 'span',
25517                 cls : 'roo-menu-item-text',
25518                 html : this.html
25519             }
25520         ];
25521         
25522         if(this.icon){
25523             text.unshift({
25524                 tag : 'i',
25525                 cls : 'fa ' + this.icon
25526             })
25527         }
25528         
25529         var cfg = {
25530             tag : 'li',
25531             cn : [
25532                 {
25533                     tag : 'a',
25534                     href : this.href || '#',
25535                     cn : text
25536                 }
25537             ]
25538         };
25539         
25540         if(this.disable){
25541             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25542         }
25543         
25544         if(this.submenu){
25545             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25546             
25547             if(this.pos == 'left'){
25548                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25549             }
25550         }
25551         
25552         return cfg;
25553     },
25554     
25555     initEvents : function() 
25556     {
25557         this.el.on('mouseover', this.onMouseOver, this);
25558         this.el.on('mouseout', this.onMouseOut, this);
25559         
25560         this.el.select('a', true).first().on('click', this.onClick, this);
25561         
25562     },
25563     
25564     onClick : function(e)
25565     {
25566         if(this.preventDefault){
25567             e.preventDefault();
25568         }
25569         
25570         this.fireEvent("click", this, e);
25571     },
25572     
25573     onMouseOver : function(e)
25574     {
25575         if(this.submenu && this.pos == 'left'){
25576             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25577         }
25578         
25579         this.fireEvent("mouseover", this, e);
25580     },
25581     
25582     onMouseOut : function(e)
25583     {
25584         this.fireEvent("mouseout", this, e);
25585     }
25586 });
25587
25588  
25589
25590  /*
25591  * - LGPL
25592  *
25593  * menu separator
25594  * 
25595  */
25596 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25597
25598 /**
25599  * @class Roo.bootstrap.menu.Separator
25600  * @extends Roo.bootstrap.Component
25601  * Bootstrap Separator class
25602  * 
25603  * @constructor
25604  * Create a new Separator
25605  * @param {Object} config The config object
25606  */
25607
25608
25609 Roo.bootstrap.menu.Separator = function(config){
25610     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25611 };
25612
25613 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25614     
25615     getAutoCreate : function(){
25616         var cfg = {
25617             tag : 'li',
25618             cls: 'divider'
25619         };
25620         
25621         return cfg;
25622     }
25623    
25624 });
25625
25626  
25627
25628  /*
25629  * - LGPL
25630  *
25631  * Tooltip
25632  * 
25633  */
25634
25635 /**
25636  * @class Roo.bootstrap.Tooltip
25637  * Bootstrap Tooltip class
25638  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25639  * to determine which dom element triggers the tooltip.
25640  * 
25641  * It needs to add support for additional attributes like tooltip-position
25642  * 
25643  * @constructor
25644  * Create a new Toolti
25645  * @param {Object} config The config object
25646  */
25647
25648 Roo.bootstrap.Tooltip = function(config){
25649     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25650     
25651     this.alignment = Roo.bootstrap.Tooltip.alignment;
25652     
25653     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25654         this.alignment = config.alignment;
25655     }
25656     
25657 };
25658
25659 Roo.apply(Roo.bootstrap.Tooltip, {
25660     /**
25661      * @function init initialize tooltip monitoring.
25662      * @static
25663      */
25664     currentEl : false,
25665     currentTip : false,
25666     currentRegion : false,
25667     
25668     //  init : delay?
25669     
25670     init : function()
25671     {
25672         Roo.get(document).on('mouseover', this.enter ,this);
25673         Roo.get(document).on('mouseout', this.leave, this);
25674          
25675         
25676         this.currentTip = new Roo.bootstrap.Tooltip();
25677     },
25678     
25679     enter : function(ev)
25680     {
25681         var dom = ev.getTarget();
25682         
25683         //Roo.log(['enter',dom]);
25684         var el = Roo.fly(dom);
25685         if (this.currentEl) {
25686             //Roo.log(dom);
25687             //Roo.log(this.currentEl);
25688             //Roo.log(this.currentEl.contains(dom));
25689             if (this.currentEl == el) {
25690                 return;
25691             }
25692             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25693                 return;
25694             }
25695
25696         }
25697         
25698         if (this.currentTip.el) {
25699             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25700         }    
25701         //Roo.log(ev);
25702         
25703         if(!el || el.dom == document){
25704             return;
25705         }
25706         
25707         var bindEl = el;
25708         
25709         // you can not look for children, as if el is the body.. then everythign is the child..
25710         if (!el.attr('tooltip')) { //
25711             if (!el.select("[tooltip]").elements.length) {
25712                 return;
25713             }
25714             // is the mouse over this child...?
25715             bindEl = el.select("[tooltip]").first();
25716             var xy = ev.getXY();
25717             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25718                 //Roo.log("not in region.");
25719                 return;
25720             }
25721             //Roo.log("child element over..");
25722             
25723         }
25724         this.currentEl = bindEl;
25725         this.currentTip.bind(bindEl);
25726         this.currentRegion = Roo.lib.Region.getRegion(dom);
25727         this.currentTip.enter();
25728         
25729     },
25730     leave : function(ev)
25731     {
25732         var dom = ev.getTarget();
25733         //Roo.log(['leave',dom]);
25734         if (!this.currentEl) {
25735             return;
25736         }
25737         
25738         
25739         if (dom != this.currentEl.dom) {
25740             return;
25741         }
25742         var xy = ev.getXY();
25743         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25744             return;
25745         }
25746         // only activate leave if mouse cursor is outside... bounding box..
25747         
25748         
25749         
25750         
25751         if (this.currentTip) {
25752             this.currentTip.leave();
25753         }
25754         //Roo.log('clear currentEl');
25755         this.currentEl = false;
25756         
25757         
25758     },
25759     alignment : {
25760         'left' : ['r-l', [-2,0], 'right'],
25761         'right' : ['l-r', [2,0], 'left'],
25762         'bottom' : ['t-b', [0,2], 'top'],
25763         'top' : [ 'b-t', [0,-2], 'bottom']
25764     }
25765     
25766 });
25767
25768
25769 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25770     
25771     
25772     bindEl : false,
25773     
25774     delay : null, // can be { show : 300 , hide: 500}
25775     
25776     timeout : null,
25777     
25778     hoverState : null, //???
25779     
25780     placement : 'bottom', 
25781     
25782     alignment : false,
25783     
25784     getAutoCreate : function(){
25785     
25786         var cfg = {
25787            cls : 'tooltip',
25788            role : 'tooltip',
25789            cn : [
25790                 {
25791                     cls : 'tooltip-arrow'
25792                 },
25793                 {
25794                     cls : 'tooltip-inner'
25795                 }
25796            ]
25797         };
25798         
25799         return cfg;
25800     },
25801     bind : function(el)
25802     {
25803         this.bindEl = el;
25804     },
25805       
25806     
25807     enter : function () {
25808        
25809         if (this.timeout != null) {
25810             clearTimeout(this.timeout);
25811         }
25812         
25813         this.hoverState = 'in';
25814          //Roo.log("enter - show");
25815         if (!this.delay || !this.delay.show) {
25816             this.show();
25817             return;
25818         }
25819         var _t = this;
25820         this.timeout = setTimeout(function () {
25821             if (_t.hoverState == 'in') {
25822                 _t.show();
25823             }
25824         }, this.delay.show);
25825     },
25826     leave : function()
25827     {
25828         clearTimeout(this.timeout);
25829     
25830         this.hoverState = 'out';
25831          if (!this.delay || !this.delay.hide) {
25832             this.hide();
25833             return;
25834         }
25835        
25836         var _t = this;
25837         this.timeout = setTimeout(function () {
25838             //Roo.log("leave - timeout");
25839             
25840             if (_t.hoverState == 'out') {
25841                 _t.hide();
25842                 Roo.bootstrap.Tooltip.currentEl = false;
25843             }
25844         }, delay);
25845     },
25846     
25847     show : function (msg)
25848     {
25849         if (!this.el) {
25850             this.render(document.body);
25851         }
25852         // set content.
25853         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25854         
25855         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25856         
25857         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25858         
25859         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25860         
25861         var placement = typeof this.placement == 'function' ?
25862             this.placement.call(this, this.el, on_el) :
25863             this.placement;
25864             
25865         var autoToken = /\s?auto?\s?/i;
25866         var autoPlace = autoToken.test(placement);
25867         if (autoPlace) {
25868             placement = placement.replace(autoToken, '') || 'top';
25869         }
25870         
25871         //this.el.detach()
25872         //this.el.setXY([0,0]);
25873         this.el.show();
25874         //this.el.dom.style.display='block';
25875         
25876         //this.el.appendTo(on_el);
25877         
25878         var p = this.getPosition();
25879         var box = this.el.getBox();
25880         
25881         if (autoPlace) {
25882             // fixme..
25883         }
25884         
25885         var align = this.alignment[placement];
25886         
25887         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25888         
25889         if(placement == 'top' || placement == 'bottom'){
25890             if(xy[0] < 0){
25891                 placement = 'right';
25892             }
25893             
25894             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25895                 placement = 'left';
25896             }
25897             
25898             var scroll = Roo.select('body', true).first().getScroll();
25899             
25900             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25901                 placement = 'top';
25902             }
25903             
25904         }
25905         
25906         this.el.alignTo(this.bindEl, align[0],align[1]);
25907         //var arrow = this.el.select('.arrow',true).first();
25908         //arrow.set(align[2], 
25909         
25910         this.el.addClass(placement);
25911         
25912         this.el.addClass('in fade');
25913         
25914         this.hoverState = null;
25915         
25916         if (this.el.hasClass('fade')) {
25917             // fade it?
25918         }
25919         
25920     },
25921     hide : function()
25922     {
25923          
25924         if (!this.el) {
25925             return;
25926         }
25927         //this.el.setXY([0,0]);
25928         this.el.removeClass('in');
25929         //this.el.hide();
25930         
25931     }
25932     
25933 });
25934  
25935
25936  /*
25937  * - LGPL
25938  *
25939  * Location Picker
25940  * 
25941  */
25942
25943 /**
25944  * @class Roo.bootstrap.LocationPicker
25945  * @extends Roo.bootstrap.Component
25946  * Bootstrap LocationPicker class
25947  * @cfg {Number} latitude Position when init default 0
25948  * @cfg {Number} longitude Position when init default 0
25949  * @cfg {Number} zoom default 15
25950  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25951  * @cfg {Boolean} mapTypeControl default false
25952  * @cfg {Boolean} disableDoubleClickZoom default false
25953  * @cfg {Boolean} scrollwheel default true
25954  * @cfg {Boolean} streetViewControl default false
25955  * @cfg {Number} radius default 0
25956  * @cfg {String} locationName
25957  * @cfg {Boolean} draggable default true
25958  * @cfg {Boolean} enableAutocomplete default false
25959  * @cfg {Boolean} enableReverseGeocode default true
25960  * @cfg {String} markerTitle
25961  * 
25962  * @constructor
25963  * Create a new LocationPicker
25964  * @param {Object} config The config object
25965  */
25966
25967
25968 Roo.bootstrap.LocationPicker = function(config){
25969     
25970     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25971     
25972     this.addEvents({
25973         /**
25974          * @event initial
25975          * Fires when the picker initialized.
25976          * @param {Roo.bootstrap.LocationPicker} this
25977          * @param {Google Location} location
25978          */
25979         initial : true,
25980         /**
25981          * @event positionchanged
25982          * Fires when the picker position changed.
25983          * @param {Roo.bootstrap.LocationPicker} this
25984          * @param {Google Location} location
25985          */
25986         positionchanged : true,
25987         /**
25988          * @event resize
25989          * Fires when the map resize.
25990          * @param {Roo.bootstrap.LocationPicker} this
25991          */
25992         resize : true,
25993         /**
25994          * @event show
25995          * Fires when the map show.
25996          * @param {Roo.bootstrap.LocationPicker} this
25997          */
25998         show : true,
25999         /**
26000          * @event hide
26001          * Fires when the map hide.
26002          * @param {Roo.bootstrap.LocationPicker} this
26003          */
26004         hide : true,
26005         /**
26006          * @event mapClick
26007          * Fires when click the map.
26008          * @param {Roo.bootstrap.LocationPicker} this
26009          * @param {Map event} e
26010          */
26011         mapClick : true,
26012         /**
26013          * @event mapRightClick
26014          * Fires when right click the map.
26015          * @param {Roo.bootstrap.LocationPicker} this
26016          * @param {Map event} e
26017          */
26018         mapRightClick : true,
26019         /**
26020          * @event markerClick
26021          * Fires when click the marker.
26022          * @param {Roo.bootstrap.LocationPicker} this
26023          * @param {Map event} e
26024          */
26025         markerClick : true,
26026         /**
26027          * @event markerRightClick
26028          * Fires when right click the marker.
26029          * @param {Roo.bootstrap.LocationPicker} this
26030          * @param {Map event} e
26031          */
26032         markerRightClick : true,
26033         /**
26034          * @event OverlayViewDraw
26035          * Fires when OverlayView Draw
26036          * @param {Roo.bootstrap.LocationPicker} this
26037          */
26038         OverlayViewDraw : true,
26039         /**
26040          * @event OverlayViewOnAdd
26041          * Fires when OverlayView Draw
26042          * @param {Roo.bootstrap.LocationPicker} this
26043          */
26044         OverlayViewOnAdd : true,
26045         /**
26046          * @event OverlayViewOnRemove
26047          * Fires when OverlayView Draw
26048          * @param {Roo.bootstrap.LocationPicker} this
26049          */
26050         OverlayViewOnRemove : true,
26051         /**
26052          * @event OverlayViewShow
26053          * Fires when OverlayView Draw
26054          * @param {Roo.bootstrap.LocationPicker} this
26055          * @param {Pixel} cpx
26056          */
26057         OverlayViewShow : true,
26058         /**
26059          * @event OverlayViewHide
26060          * Fires when OverlayView Draw
26061          * @param {Roo.bootstrap.LocationPicker} this
26062          */
26063         OverlayViewHide : true,
26064         /**
26065          * @event loadexception
26066          * Fires when load google lib failed.
26067          * @param {Roo.bootstrap.LocationPicker} this
26068          */
26069         loadexception : true
26070     });
26071         
26072 };
26073
26074 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26075     
26076     gMapContext: false,
26077     
26078     latitude: 0,
26079     longitude: 0,
26080     zoom: 15,
26081     mapTypeId: false,
26082     mapTypeControl: false,
26083     disableDoubleClickZoom: false,
26084     scrollwheel: true,
26085     streetViewControl: false,
26086     radius: 0,
26087     locationName: '',
26088     draggable: true,
26089     enableAutocomplete: false,
26090     enableReverseGeocode: true,
26091     markerTitle: '',
26092     
26093     getAutoCreate: function()
26094     {
26095
26096         var cfg = {
26097             tag: 'div',
26098             cls: 'roo-location-picker'
26099         };
26100         
26101         return cfg
26102     },
26103     
26104     initEvents: function(ct, position)
26105     {       
26106         if(!this.el.getWidth() || this.isApplied()){
26107             return;
26108         }
26109         
26110         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26111         
26112         this.initial();
26113     },
26114     
26115     initial: function()
26116     {
26117         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26118             this.fireEvent('loadexception', this);
26119             return;
26120         }
26121         
26122         if(!this.mapTypeId){
26123             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26124         }
26125         
26126         this.gMapContext = this.GMapContext();
26127         
26128         this.initOverlayView();
26129         
26130         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26131         
26132         var _this = this;
26133                 
26134         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26135             _this.setPosition(_this.gMapContext.marker.position);
26136         });
26137         
26138         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26139             _this.fireEvent('mapClick', this, event);
26140             
26141         });
26142
26143         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26144             _this.fireEvent('mapRightClick', this, event);
26145             
26146         });
26147         
26148         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26149             _this.fireEvent('markerClick', this, event);
26150             
26151         });
26152
26153         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26154             _this.fireEvent('markerRightClick', this, event);
26155             
26156         });
26157         
26158         this.setPosition(this.gMapContext.location);
26159         
26160         this.fireEvent('initial', this, this.gMapContext.location);
26161     },
26162     
26163     initOverlayView: function()
26164     {
26165         var _this = this;
26166         
26167         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26168             
26169             draw: function()
26170             {
26171                 _this.fireEvent('OverlayViewDraw', _this);
26172             },
26173             
26174             onAdd: function()
26175             {
26176                 _this.fireEvent('OverlayViewOnAdd', _this);
26177             },
26178             
26179             onRemove: function()
26180             {
26181                 _this.fireEvent('OverlayViewOnRemove', _this);
26182             },
26183             
26184             show: function(cpx)
26185             {
26186                 _this.fireEvent('OverlayViewShow', _this, cpx);
26187             },
26188             
26189             hide: function()
26190             {
26191                 _this.fireEvent('OverlayViewHide', _this);
26192             }
26193             
26194         });
26195     },
26196     
26197     fromLatLngToContainerPixel: function(event)
26198     {
26199         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26200     },
26201     
26202     isApplied: function() 
26203     {
26204         return this.getGmapContext() == false ? false : true;
26205     },
26206     
26207     getGmapContext: function() 
26208     {
26209         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26210     },
26211     
26212     GMapContext: function() 
26213     {
26214         var position = new google.maps.LatLng(this.latitude, this.longitude);
26215         
26216         var _map = new google.maps.Map(this.el.dom, {
26217             center: position,
26218             zoom: this.zoom,
26219             mapTypeId: this.mapTypeId,
26220             mapTypeControl: this.mapTypeControl,
26221             disableDoubleClickZoom: this.disableDoubleClickZoom,
26222             scrollwheel: this.scrollwheel,
26223             streetViewControl: this.streetViewControl,
26224             locationName: this.locationName,
26225             draggable: this.draggable,
26226             enableAutocomplete: this.enableAutocomplete,
26227             enableReverseGeocode: this.enableReverseGeocode
26228         });
26229         
26230         var _marker = new google.maps.Marker({
26231             position: position,
26232             map: _map,
26233             title: this.markerTitle,
26234             draggable: this.draggable
26235         });
26236         
26237         return {
26238             map: _map,
26239             marker: _marker,
26240             circle: null,
26241             location: position,
26242             radius: this.radius,
26243             locationName: this.locationName,
26244             addressComponents: {
26245                 formatted_address: null,
26246                 addressLine1: null,
26247                 addressLine2: null,
26248                 streetName: null,
26249                 streetNumber: null,
26250                 city: null,
26251                 district: null,
26252                 state: null,
26253                 stateOrProvince: null
26254             },
26255             settings: this,
26256             domContainer: this.el.dom,
26257             geodecoder: new google.maps.Geocoder()
26258         };
26259     },
26260     
26261     drawCircle: function(center, radius, options) 
26262     {
26263         if (this.gMapContext.circle != null) {
26264             this.gMapContext.circle.setMap(null);
26265         }
26266         if (radius > 0) {
26267             radius *= 1;
26268             options = Roo.apply({}, options, {
26269                 strokeColor: "#0000FF",
26270                 strokeOpacity: .35,
26271                 strokeWeight: 2,
26272                 fillColor: "#0000FF",
26273                 fillOpacity: .2
26274             });
26275             
26276             options.map = this.gMapContext.map;
26277             options.radius = radius;
26278             options.center = center;
26279             this.gMapContext.circle = new google.maps.Circle(options);
26280             return this.gMapContext.circle;
26281         }
26282         
26283         return null;
26284     },
26285     
26286     setPosition: function(location) 
26287     {
26288         this.gMapContext.location = location;
26289         this.gMapContext.marker.setPosition(location);
26290         this.gMapContext.map.panTo(location);
26291         this.drawCircle(location, this.gMapContext.radius, {});
26292         
26293         var _this = this;
26294         
26295         if (this.gMapContext.settings.enableReverseGeocode) {
26296             this.gMapContext.geodecoder.geocode({
26297                 latLng: this.gMapContext.location
26298             }, function(results, status) {
26299                 
26300                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26301                     _this.gMapContext.locationName = results[0].formatted_address;
26302                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26303                     
26304                     _this.fireEvent('positionchanged', this, location);
26305                 }
26306             });
26307             
26308             return;
26309         }
26310         
26311         this.fireEvent('positionchanged', this, location);
26312     },
26313     
26314     resize: function()
26315     {
26316         google.maps.event.trigger(this.gMapContext.map, "resize");
26317         
26318         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26319         
26320         this.fireEvent('resize', this);
26321     },
26322     
26323     setPositionByLatLng: function(latitude, longitude)
26324     {
26325         this.setPosition(new google.maps.LatLng(latitude, longitude));
26326     },
26327     
26328     getCurrentPosition: function() 
26329     {
26330         return {
26331             latitude: this.gMapContext.location.lat(),
26332             longitude: this.gMapContext.location.lng()
26333         };
26334     },
26335     
26336     getAddressName: function() 
26337     {
26338         return this.gMapContext.locationName;
26339     },
26340     
26341     getAddressComponents: function() 
26342     {
26343         return this.gMapContext.addressComponents;
26344     },
26345     
26346     address_component_from_google_geocode: function(address_components) 
26347     {
26348         var result = {};
26349         
26350         for (var i = 0; i < address_components.length; i++) {
26351             var component = address_components[i];
26352             if (component.types.indexOf("postal_code") >= 0) {
26353                 result.postalCode = component.short_name;
26354             } else if (component.types.indexOf("street_number") >= 0) {
26355                 result.streetNumber = component.short_name;
26356             } else if (component.types.indexOf("route") >= 0) {
26357                 result.streetName = component.short_name;
26358             } else if (component.types.indexOf("neighborhood") >= 0) {
26359                 result.city = component.short_name;
26360             } else if (component.types.indexOf("locality") >= 0) {
26361                 result.city = component.short_name;
26362             } else if (component.types.indexOf("sublocality") >= 0) {
26363                 result.district = component.short_name;
26364             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26365                 result.stateOrProvince = component.short_name;
26366             } else if (component.types.indexOf("country") >= 0) {
26367                 result.country = component.short_name;
26368             }
26369         }
26370         
26371         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26372         result.addressLine2 = "";
26373         return result;
26374     },
26375     
26376     setZoomLevel: function(zoom)
26377     {
26378         this.gMapContext.map.setZoom(zoom);
26379     },
26380     
26381     show: function()
26382     {
26383         if(!this.el){
26384             return;
26385         }
26386         
26387         this.el.show();
26388         
26389         this.resize();
26390         
26391         this.fireEvent('show', this);
26392     },
26393     
26394     hide: function()
26395     {
26396         if(!this.el){
26397             return;
26398         }
26399         
26400         this.el.hide();
26401         
26402         this.fireEvent('hide', this);
26403     }
26404     
26405 });
26406
26407 Roo.apply(Roo.bootstrap.LocationPicker, {
26408     
26409     OverlayView : function(map, options)
26410     {
26411         options = options || {};
26412         
26413         this.setMap(map);
26414     }
26415     
26416     
26417 });/*
26418  * - LGPL
26419  *
26420  * Alert
26421  * 
26422  */
26423
26424 /**
26425  * @class Roo.bootstrap.Alert
26426  * @extends Roo.bootstrap.Component
26427  * Bootstrap Alert class
26428  * @cfg {String} title The title of alert
26429  * @cfg {String} html The content of alert
26430  * @cfg {String} weight (  success | info | warning | danger )
26431  * @cfg {String} faicon font-awesomeicon
26432  * 
26433  * @constructor
26434  * Create a new alert
26435  * @param {Object} config The config object
26436  */
26437
26438
26439 Roo.bootstrap.Alert = function(config){
26440     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26441     
26442 };
26443
26444 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26445     
26446     title: '',
26447     html: '',
26448     weight: false,
26449     faicon: false,
26450     
26451     getAutoCreate : function()
26452     {
26453         
26454         var cfg = {
26455             tag : 'div',
26456             cls : 'alert',
26457             cn : [
26458                 {
26459                     tag : 'i',
26460                     cls : 'roo-alert-icon'
26461                     
26462                 },
26463                 {
26464                     tag : 'b',
26465                     cls : 'roo-alert-title',
26466                     html : this.title
26467                 },
26468                 {
26469                     tag : 'span',
26470                     cls : 'roo-alert-text',
26471                     html : this.html
26472                 }
26473             ]
26474         };
26475         
26476         if(this.faicon){
26477             cfg.cn[0].cls += ' fa ' + this.faicon;
26478         }
26479         
26480         if(this.weight){
26481             cfg.cls += ' alert-' + this.weight;
26482         }
26483         
26484         return cfg;
26485     },
26486     
26487     initEvents: function() 
26488     {
26489         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26490     },
26491     
26492     setTitle : function(str)
26493     {
26494         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26495     },
26496     
26497     setText : function(str)
26498     {
26499         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26500     },
26501     
26502     setWeight : function(weight)
26503     {
26504         if(this.weight){
26505             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26506         }
26507         
26508         this.weight = weight;
26509         
26510         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26511     },
26512     
26513     setIcon : function(icon)
26514     {
26515         if(this.faicon){
26516             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26517         }
26518         
26519         this.faicon = icon;
26520         
26521         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26522     },
26523     
26524     hide: function() 
26525     {
26526         this.el.hide();   
26527     },
26528     
26529     show: function() 
26530     {  
26531         this.el.show();   
26532     }
26533     
26534 });
26535
26536  
26537 /*
26538 * Licence: LGPL
26539 */
26540
26541 /**
26542  * @class Roo.bootstrap.UploadCropbox
26543  * @extends Roo.bootstrap.Component
26544  * Bootstrap UploadCropbox class
26545  * @cfg {String} emptyText show when image has been loaded
26546  * @cfg {String} rotateNotify show when image too small to rotate
26547  * @cfg {Number} errorTimeout default 3000
26548  * @cfg {Number} minWidth default 300
26549  * @cfg {Number} minHeight default 300
26550  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26551  * @cfg {Boolean} isDocument (true|false) default false
26552  * @cfg {String} url action url
26553  * @cfg {String} paramName default 'imageUpload'
26554  * @cfg {String} method default POST
26555  * @cfg {Boolean} loadMask (true|false) default true
26556  * @cfg {Boolean} loadingText default 'Loading...'
26557  * 
26558  * @constructor
26559  * Create a new UploadCropbox
26560  * @param {Object} config The config object
26561  */
26562
26563 Roo.bootstrap.UploadCropbox = function(config){
26564     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26565     
26566     this.addEvents({
26567         /**
26568          * @event beforeselectfile
26569          * Fire before select file
26570          * @param {Roo.bootstrap.UploadCropbox} this
26571          */
26572         "beforeselectfile" : true,
26573         /**
26574          * @event initial
26575          * Fire after initEvent
26576          * @param {Roo.bootstrap.UploadCropbox} this
26577          */
26578         "initial" : true,
26579         /**
26580          * @event crop
26581          * Fire after initEvent
26582          * @param {Roo.bootstrap.UploadCropbox} this
26583          * @param {String} data
26584          */
26585         "crop" : true,
26586         /**
26587          * @event prepare
26588          * Fire when preparing the file data
26589          * @param {Roo.bootstrap.UploadCropbox} this
26590          * @param {Object} file
26591          */
26592         "prepare" : true,
26593         /**
26594          * @event exception
26595          * Fire when get exception
26596          * @param {Roo.bootstrap.UploadCropbox} this
26597          * @param {XMLHttpRequest} xhr
26598          */
26599         "exception" : true,
26600         /**
26601          * @event beforeloadcanvas
26602          * Fire before load the canvas
26603          * @param {Roo.bootstrap.UploadCropbox} this
26604          * @param {String} src
26605          */
26606         "beforeloadcanvas" : true,
26607         /**
26608          * @event trash
26609          * Fire when trash image
26610          * @param {Roo.bootstrap.UploadCropbox} this
26611          */
26612         "trash" : true,
26613         /**
26614          * @event download
26615          * Fire when download the image
26616          * @param {Roo.bootstrap.UploadCropbox} this
26617          */
26618         "download" : true,
26619         /**
26620          * @event footerbuttonclick
26621          * Fire when footerbuttonclick
26622          * @param {Roo.bootstrap.UploadCropbox} this
26623          * @param {String} type
26624          */
26625         "footerbuttonclick" : true,
26626         /**
26627          * @event resize
26628          * Fire when resize
26629          * @param {Roo.bootstrap.UploadCropbox} this
26630          */
26631         "resize" : true,
26632         /**
26633          * @event rotate
26634          * Fire when rotate the image
26635          * @param {Roo.bootstrap.UploadCropbox} this
26636          * @param {String} pos
26637          */
26638         "rotate" : true,
26639         /**
26640          * @event inspect
26641          * Fire when inspect the file
26642          * @param {Roo.bootstrap.UploadCropbox} this
26643          * @param {Object} file
26644          */
26645         "inspect" : true,
26646         /**
26647          * @event upload
26648          * Fire when xhr upload the file
26649          * @param {Roo.bootstrap.UploadCropbox} this
26650          * @param {Object} data
26651          */
26652         "upload" : true,
26653         /**
26654          * @event arrange
26655          * Fire when arrange the file data
26656          * @param {Roo.bootstrap.UploadCropbox} this
26657          * @param {Object} formData
26658          */
26659         "arrange" : true
26660     });
26661     
26662     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26663 };
26664
26665 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26666     
26667     emptyText : 'Click to upload image',
26668     rotateNotify : 'Image is too small to rotate',
26669     errorTimeout : 3000,
26670     scale : 0,
26671     baseScale : 1,
26672     rotate : 0,
26673     dragable : false,
26674     pinching : false,
26675     mouseX : 0,
26676     mouseY : 0,
26677     cropData : false,
26678     minWidth : 300,
26679     minHeight : 300,
26680     file : false,
26681     exif : {},
26682     baseRotate : 1,
26683     cropType : 'image/jpeg',
26684     buttons : false,
26685     canvasLoaded : false,
26686     isDocument : false,
26687     method : 'POST',
26688     paramName : 'imageUpload',
26689     loadMask : true,
26690     loadingText : 'Loading...',
26691     maskEl : false,
26692     
26693     getAutoCreate : function()
26694     {
26695         var cfg = {
26696             tag : 'div',
26697             cls : 'roo-upload-cropbox',
26698             cn : [
26699                 {
26700                     tag : 'input',
26701                     cls : 'roo-upload-cropbox-selector',
26702                     type : 'file'
26703                 },
26704                 {
26705                     tag : 'div',
26706                     cls : 'roo-upload-cropbox-body',
26707                     style : 'cursor:pointer',
26708                     cn : [
26709                         {
26710                             tag : 'div',
26711                             cls : 'roo-upload-cropbox-preview'
26712                         },
26713                         {
26714                             tag : 'div',
26715                             cls : 'roo-upload-cropbox-thumb'
26716                         },
26717                         {
26718                             tag : 'div',
26719                             cls : 'roo-upload-cropbox-empty-notify',
26720                             html : this.emptyText
26721                         },
26722                         {
26723                             tag : 'div',
26724                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26725                             html : this.rotateNotify
26726                         }
26727                     ]
26728                 },
26729                 {
26730                     tag : 'div',
26731                     cls : 'roo-upload-cropbox-footer',
26732                     cn : {
26733                         tag : 'div',
26734                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26735                         cn : []
26736                     }
26737                 }
26738             ]
26739         };
26740         
26741         return cfg;
26742     },
26743     
26744     onRender : function(ct, position)
26745     {
26746         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26747         
26748         if (this.buttons.length) {
26749             
26750             Roo.each(this.buttons, function(bb) {
26751                 
26752                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26753                 
26754                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26755                 
26756             }, this);
26757         }
26758         
26759         if(this.loadMask){
26760             this.maskEl = this.el;
26761         }
26762     },
26763     
26764     initEvents : function()
26765     {
26766         this.urlAPI = (window.createObjectURL && window) || 
26767                                 (window.URL && URL.revokeObjectURL && URL) || 
26768                                 (window.webkitURL && webkitURL);
26769                         
26770         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26771         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26772         
26773         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26774         this.selectorEl.hide();
26775         
26776         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26777         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26778         
26779         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26780         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26781         this.thumbEl.hide();
26782         
26783         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26784         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26785         
26786         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26787         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26788         this.errorEl.hide();
26789         
26790         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26791         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26792         this.footerEl.hide();
26793         
26794         this.setThumbBoxSize();
26795         
26796         this.bind();
26797         
26798         this.resize();
26799         
26800         this.fireEvent('initial', this);
26801     },
26802
26803     bind : function()
26804     {
26805         var _this = this;
26806         
26807         window.addEventListener("resize", function() { _this.resize(); } );
26808         
26809         this.bodyEl.on('click', this.beforeSelectFile, this);
26810         
26811         if(Roo.isTouch){
26812             this.bodyEl.on('touchstart', this.onTouchStart, this);
26813             this.bodyEl.on('touchmove', this.onTouchMove, this);
26814             this.bodyEl.on('touchend', this.onTouchEnd, this);
26815         }
26816         
26817         if(!Roo.isTouch){
26818             this.bodyEl.on('mousedown', this.onMouseDown, this);
26819             this.bodyEl.on('mousemove', this.onMouseMove, this);
26820             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26821             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26822             Roo.get(document).on('mouseup', this.onMouseUp, this);
26823         }
26824         
26825         this.selectorEl.on('change', this.onFileSelected, this);
26826     },
26827     
26828     reset : function()
26829     {    
26830         this.scale = 0;
26831         this.baseScale = 1;
26832         this.rotate = 0;
26833         this.baseRotate = 1;
26834         this.dragable = false;
26835         this.pinching = false;
26836         this.mouseX = 0;
26837         this.mouseY = 0;
26838         this.cropData = false;
26839         this.notifyEl.dom.innerHTML = this.emptyText;
26840         
26841         this.selectorEl.dom.value = '';
26842         
26843     },
26844     
26845     resize : function()
26846     {
26847         if(this.fireEvent('resize', this) != false){
26848             this.setThumbBoxPosition();
26849             this.setCanvasPosition();
26850         }
26851     },
26852     
26853     onFooterButtonClick : function(e, el, o, type)
26854     {
26855         switch (type) {
26856             case 'rotate-left' :
26857                 this.onRotateLeft(e);
26858                 break;
26859             case 'rotate-right' :
26860                 this.onRotateRight(e);
26861                 break;
26862             case 'picture' :
26863                 this.beforeSelectFile(e);
26864                 break;
26865             case 'trash' :
26866                 this.trash(e);
26867                 break;
26868             case 'crop' :
26869                 this.crop(e);
26870                 break;
26871             case 'download' :
26872                 this.download(e);
26873                 break;
26874             default :
26875                 break;
26876         }
26877         
26878         this.fireEvent('footerbuttonclick', this, type);
26879     },
26880     
26881     beforeSelectFile : function(e)
26882     {
26883         e.preventDefault();
26884         
26885         if(this.fireEvent('beforeselectfile', this) != false){
26886             this.selectorEl.dom.click();
26887         }
26888     },
26889     
26890     onFileSelected : function(e)
26891     {
26892         e.preventDefault();
26893         
26894         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26895             return;
26896         }
26897         
26898         var file = this.selectorEl.dom.files[0];
26899         
26900         if(this.fireEvent('inspect', this, file) != false){
26901             this.prepare(file);
26902         }
26903         
26904     },
26905     
26906     trash : function(e)
26907     {
26908         this.fireEvent('trash', this);
26909     },
26910     
26911     download : function(e)
26912     {
26913         this.fireEvent('download', this);
26914     },
26915     
26916     loadCanvas : function(src)
26917     {   
26918         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26919             
26920             this.reset();
26921             
26922             this.imageEl = document.createElement('img');
26923             
26924             var _this = this;
26925             
26926             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26927             
26928             this.imageEl.src = src;
26929         }
26930     },
26931     
26932     onLoadCanvas : function()
26933     {   
26934         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26935         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26936         
26937         this.bodyEl.un('click', this.beforeSelectFile, this);
26938         
26939         this.notifyEl.hide();
26940         this.thumbEl.show();
26941         this.footerEl.show();
26942         
26943         this.baseRotateLevel();
26944         
26945         if(this.isDocument){
26946             this.setThumbBoxSize();
26947         }
26948         
26949         this.setThumbBoxPosition();
26950         
26951         this.baseScaleLevel();
26952         
26953         this.draw();
26954         
26955         this.resize();
26956         
26957         this.canvasLoaded = true;
26958         
26959         if(this.loadMask){
26960             this.maskEl.unmask();
26961         }
26962         
26963     },
26964     
26965     setCanvasPosition : function()
26966     {   
26967         if(!this.canvasEl){
26968             return;
26969         }
26970         
26971         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26972         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26973         
26974         this.previewEl.setLeft(pw);
26975         this.previewEl.setTop(ph);
26976         
26977     },
26978     
26979     onMouseDown : function(e)
26980     {   
26981         e.stopEvent();
26982         
26983         this.dragable = true;
26984         this.pinching = false;
26985         
26986         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26987             this.dragable = false;
26988             return;
26989         }
26990         
26991         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26992         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26993         
26994     },
26995     
26996     onMouseMove : function(e)
26997     {   
26998         e.stopEvent();
26999         
27000         if(!this.canvasLoaded){
27001             return;
27002         }
27003         
27004         if (!this.dragable){
27005             return;
27006         }
27007         
27008         var minX = Math.ceil(this.thumbEl.getLeft(true));
27009         var minY = Math.ceil(this.thumbEl.getTop(true));
27010         
27011         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27012         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27013         
27014         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27015         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27016         
27017         x = x - this.mouseX;
27018         y = y - this.mouseY;
27019         
27020         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27021         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27022         
27023         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27024         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27025         
27026         this.previewEl.setLeft(bgX);
27027         this.previewEl.setTop(bgY);
27028         
27029         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27030         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27031     },
27032     
27033     onMouseUp : function(e)
27034     {   
27035         e.stopEvent();
27036         
27037         this.dragable = false;
27038     },
27039     
27040     onMouseWheel : function(e)
27041     {   
27042         e.stopEvent();
27043         
27044         this.startScale = this.scale;
27045         
27046         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27047         
27048         if(!this.zoomable()){
27049             this.scale = this.startScale;
27050             return;
27051         }
27052         
27053         this.draw();
27054         
27055         return;
27056     },
27057     
27058     zoomable : function()
27059     {
27060         var minScale = this.thumbEl.getWidth() / this.minWidth;
27061         
27062         if(this.minWidth < this.minHeight){
27063             minScale = this.thumbEl.getHeight() / this.minHeight;
27064         }
27065         
27066         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27067         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27068         
27069         if(
27070                 this.isDocument &&
27071                 (this.rotate == 0 || this.rotate == 180) && 
27072                 (
27073                     width > this.imageEl.OriginWidth || 
27074                     height > this.imageEl.OriginHeight ||
27075                     (width < this.minWidth && height < this.minHeight)
27076                 )
27077         ){
27078             return false;
27079         }
27080         
27081         if(
27082                 this.isDocument &&
27083                 (this.rotate == 90 || this.rotate == 270) && 
27084                 (
27085                     width > this.imageEl.OriginWidth || 
27086                     height > this.imageEl.OriginHeight ||
27087                     (width < this.minHeight && height < this.minWidth)
27088                 )
27089         ){
27090             return false;
27091         }
27092         
27093         if(
27094                 !this.isDocument &&
27095                 (this.rotate == 0 || this.rotate == 180) && 
27096                 (
27097                     width < this.minWidth || 
27098                     width > this.imageEl.OriginWidth || 
27099                     height < this.minHeight || 
27100                     height > this.imageEl.OriginHeight
27101                 )
27102         ){
27103             return false;
27104         }
27105         
27106         if(
27107                 !this.isDocument &&
27108                 (this.rotate == 90 || this.rotate == 270) && 
27109                 (
27110                     width < this.minHeight || 
27111                     width > this.imageEl.OriginWidth || 
27112                     height < this.minWidth || 
27113                     height > this.imageEl.OriginHeight
27114                 )
27115         ){
27116             return false;
27117         }
27118         
27119         return true;
27120         
27121     },
27122     
27123     onRotateLeft : function(e)
27124     {   
27125         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27126             
27127             var minScale = this.thumbEl.getWidth() / this.minWidth;
27128             
27129             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27130             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27131             
27132             this.startScale = this.scale;
27133             
27134             while (this.getScaleLevel() < minScale){
27135             
27136                 this.scale = this.scale + 1;
27137                 
27138                 if(!this.zoomable()){
27139                     break;
27140                 }
27141                 
27142                 if(
27143                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27144                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27145                 ){
27146                     continue;
27147                 }
27148                 
27149                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27150
27151                 this.draw();
27152                 
27153                 return;
27154             }
27155             
27156             this.scale = this.startScale;
27157             
27158             this.onRotateFail();
27159             
27160             return false;
27161         }
27162         
27163         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27164
27165         if(this.isDocument){
27166             this.setThumbBoxSize();
27167             this.setThumbBoxPosition();
27168             this.setCanvasPosition();
27169         }
27170         
27171         this.draw();
27172         
27173         this.fireEvent('rotate', this, 'left');
27174         
27175     },
27176     
27177     onRotateRight : function(e)
27178     {
27179         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27180             
27181             var minScale = this.thumbEl.getWidth() / this.minWidth;
27182         
27183             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27184             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27185             
27186             this.startScale = this.scale;
27187             
27188             while (this.getScaleLevel() < minScale){
27189             
27190                 this.scale = this.scale + 1;
27191                 
27192                 if(!this.zoomable()){
27193                     break;
27194                 }
27195                 
27196                 if(
27197                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27198                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27199                 ){
27200                     continue;
27201                 }
27202                 
27203                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27204
27205                 this.draw();
27206                 
27207                 return;
27208             }
27209             
27210             this.scale = this.startScale;
27211             
27212             this.onRotateFail();
27213             
27214             return false;
27215         }
27216         
27217         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27218
27219         if(this.isDocument){
27220             this.setThumbBoxSize();
27221             this.setThumbBoxPosition();
27222             this.setCanvasPosition();
27223         }
27224         
27225         this.draw();
27226         
27227         this.fireEvent('rotate', this, 'right');
27228     },
27229     
27230     onRotateFail : function()
27231     {
27232         this.errorEl.show(true);
27233         
27234         var _this = this;
27235         
27236         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27237     },
27238     
27239     draw : function()
27240     {
27241         this.previewEl.dom.innerHTML = '';
27242         
27243         var canvasEl = document.createElement("canvas");
27244         
27245         var contextEl = canvasEl.getContext("2d");
27246         
27247         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27248         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27249         var center = this.imageEl.OriginWidth / 2;
27250         
27251         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27252             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27253             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27254             center = this.imageEl.OriginHeight / 2;
27255         }
27256         
27257         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27258         
27259         contextEl.translate(center, center);
27260         contextEl.rotate(this.rotate * Math.PI / 180);
27261
27262         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27263         
27264         this.canvasEl = document.createElement("canvas");
27265         
27266         this.contextEl = this.canvasEl.getContext("2d");
27267         
27268         switch (this.rotate) {
27269             case 0 :
27270                 
27271                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27272                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27273                 
27274                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27275                 
27276                 break;
27277             case 90 : 
27278                 
27279                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27280                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27281                 
27282                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27283                     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);
27284                     break;
27285                 }
27286                 
27287                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27288                 
27289                 break;
27290             case 180 :
27291                 
27292                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27293                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27294                 
27295                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27296                     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);
27297                     break;
27298                 }
27299                 
27300                 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);
27301                 
27302                 break;
27303             case 270 :
27304                 
27305                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27306                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27307         
27308                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27309                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27310                     break;
27311                 }
27312                 
27313                 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);
27314                 
27315                 break;
27316             default : 
27317                 break;
27318         }
27319         
27320         this.previewEl.appendChild(this.canvasEl);
27321         
27322         this.setCanvasPosition();
27323     },
27324     
27325     crop : function()
27326     {
27327         if(!this.canvasLoaded){
27328             return;
27329         }
27330         
27331         var imageCanvas = document.createElement("canvas");
27332         
27333         var imageContext = imageCanvas.getContext("2d");
27334         
27335         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27336         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27337         
27338         var center = imageCanvas.width / 2;
27339         
27340         imageContext.translate(center, center);
27341         
27342         imageContext.rotate(this.rotate * Math.PI / 180);
27343         
27344         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27345         
27346         var canvas = document.createElement("canvas");
27347         
27348         var context = canvas.getContext("2d");
27349                 
27350         canvas.width = this.minWidth;
27351         canvas.height = this.minHeight;
27352
27353         switch (this.rotate) {
27354             case 0 :
27355                 
27356                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27357                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27358                 
27359                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27360                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27361                 
27362                 var targetWidth = this.minWidth - 2 * x;
27363                 var targetHeight = this.minHeight - 2 * y;
27364                 
27365                 var scale = 1;
27366                 
27367                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27368                     scale = targetWidth / width;
27369                 }
27370                 
27371                 if(x > 0 && y == 0){
27372                     scale = targetHeight / height;
27373                 }
27374                 
27375                 if(x > 0 && y > 0){
27376                     scale = targetWidth / width;
27377                     
27378                     if(width < height){
27379                         scale = targetHeight / height;
27380                     }
27381                 }
27382                 
27383                 context.scale(scale, scale);
27384                 
27385                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27386                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27387
27388                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27389                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27390
27391                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27392                 
27393                 break;
27394             case 90 : 
27395                 
27396                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27397                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27398                 
27399                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27400                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27401                 
27402                 var targetWidth = this.minWidth - 2 * x;
27403                 var targetHeight = this.minHeight - 2 * y;
27404                 
27405                 var scale = 1;
27406                 
27407                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27408                     scale = targetWidth / width;
27409                 }
27410                 
27411                 if(x > 0 && y == 0){
27412                     scale = targetHeight / height;
27413                 }
27414                 
27415                 if(x > 0 && y > 0){
27416                     scale = targetWidth / width;
27417                     
27418                     if(width < height){
27419                         scale = targetHeight / height;
27420                     }
27421                 }
27422                 
27423                 context.scale(scale, scale);
27424                 
27425                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27426                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27427
27428                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27429                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27430                 
27431                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27432                 
27433                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27434                 
27435                 break;
27436             case 180 :
27437                 
27438                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27439                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27440                 
27441                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27442                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27443                 
27444                 var targetWidth = this.minWidth - 2 * x;
27445                 var targetHeight = this.minHeight - 2 * y;
27446                 
27447                 var scale = 1;
27448                 
27449                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27450                     scale = targetWidth / width;
27451                 }
27452                 
27453                 if(x > 0 && y == 0){
27454                     scale = targetHeight / height;
27455                 }
27456                 
27457                 if(x > 0 && y > 0){
27458                     scale = targetWidth / width;
27459                     
27460                     if(width < height){
27461                         scale = targetHeight / height;
27462                     }
27463                 }
27464                 
27465                 context.scale(scale, scale);
27466                 
27467                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27468                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27469
27470                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27471                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27472
27473                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27474                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27475                 
27476                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27477                 
27478                 break;
27479             case 270 :
27480                 
27481                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27482                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27483                 
27484                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27485                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27486                 
27487                 var targetWidth = this.minWidth - 2 * x;
27488                 var targetHeight = this.minHeight - 2 * y;
27489                 
27490                 var scale = 1;
27491                 
27492                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27493                     scale = targetWidth / width;
27494                 }
27495                 
27496                 if(x > 0 && y == 0){
27497                     scale = targetHeight / height;
27498                 }
27499                 
27500                 if(x > 0 && y > 0){
27501                     scale = targetWidth / width;
27502                     
27503                     if(width < height){
27504                         scale = targetHeight / height;
27505                     }
27506                 }
27507                 
27508                 context.scale(scale, scale);
27509                 
27510                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27511                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27512
27513                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27514                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27515                 
27516                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27517                 
27518                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27519                 
27520                 break;
27521             default : 
27522                 break;
27523         }
27524         
27525         this.cropData = canvas.toDataURL(this.cropType);
27526         
27527         if(this.fireEvent('crop', this, this.cropData) !== false){
27528             this.process(this.file, this.cropData);
27529         }
27530         
27531         return;
27532         
27533     },
27534     
27535     setThumbBoxSize : function()
27536     {
27537         var width, height;
27538         
27539         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27540             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27541             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27542             
27543             this.minWidth = width;
27544             this.minHeight = height;
27545             
27546             if(this.rotate == 90 || this.rotate == 270){
27547                 this.minWidth = height;
27548                 this.minHeight = width;
27549             }
27550         }
27551         
27552         height = 300;
27553         width = Math.ceil(this.minWidth * height / this.minHeight);
27554         
27555         if(this.minWidth > this.minHeight){
27556             width = 300;
27557             height = Math.ceil(this.minHeight * width / this.minWidth);
27558         }
27559         
27560         this.thumbEl.setStyle({
27561             width : width + 'px',
27562             height : height + 'px'
27563         });
27564
27565         return;
27566             
27567     },
27568     
27569     setThumbBoxPosition : function()
27570     {
27571         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27572         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27573         
27574         this.thumbEl.setLeft(x);
27575         this.thumbEl.setTop(y);
27576         
27577     },
27578     
27579     baseRotateLevel : function()
27580     {
27581         this.baseRotate = 1;
27582         
27583         if(
27584                 typeof(this.exif) != 'undefined' &&
27585                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27586                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27587         ){
27588             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27589         }
27590         
27591         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27592         
27593     },
27594     
27595     baseScaleLevel : function()
27596     {
27597         var width, height;
27598         
27599         if(this.isDocument){
27600             
27601             if(this.baseRotate == 6 || this.baseRotate == 8){
27602             
27603                 height = this.thumbEl.getHeight();
27604                 this.baseScale = height / this.imageEl.OriginWidth;
27605
27606                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27607                     width = this.thumbEl.getWidth();
27608                     this.baseScale = width / this.imageEl.OriginHeight;
27609                 }
27610
27611                 return;
27612             }
27613
27614             height = this.thumbEl.getHeight();
27615             this.baseScale = height / this.imageEl.OriginHeight;
27616
27617             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27618                 width = this.thumbEl.getWidth();
27619                 this.baseScale = width / this.imageEl.OriginWidth;
27620             }
27621
27622             return;
27623         }
27624         
27625         if(this.baseRotate == 6 || this.baseRotate == 8){
27626             
27627             width = this.thumbEl.getHeight();
27628             this.baseScale = width / this.imageEl.OriginHeight;
27629             
27630             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27631                 height = this.thumbEl.getWidth();
27632                 this.baseScale = height / this.imageEl.OriginHeight;
27633             }
27634             
27635             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27636                 height = this.thumbEl.getWidth();
27637                 this.baseScale = height / this.imageEl.OriginHeight;
27638                 
27639                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27640                     width = this.thumbEl.getHeight();
27641                     this.baseScale = width / this.imageEl.OriginWidth;
27642                 }
27643             }
27644             
27645             return;
27646         }
27647         
27648         width = this.thumbEl.getWidth();
27649         this.baseScale = width / this.imageEl.OriginWidth;
27650         
27651         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27652             height = this.thumbEl.getHeight();
27653             this.baseScale = height / this.imageEl.OriginHeight;
27654         }
27655         
27656         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27657             
27658             height = this.thumbEl.getHeight();
27659             this.baseScale = height / this.imageEl.OriginHeight;
27660             
27661             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27662                 width = this.thumbEl.getWidth();
27663                 this.baseScale = width / this.imageEl.OriginWidth;
27664             }
27665             
27666         }
27667         
27668         return;
27669     },
27670     
27671     getScaleLevel : function()
27672     {
27673         return this.baseScale * Math.pow(1.1, this.scale);
27674     },
27675     
27676     onTouchStart : function(e)
27677     {
27678         if(!this.canvasLoaded){
27679             this.beforeSelectFile(e);
27680             return;
27681         }
27682         
27683         var touches = e.browserEvent.touches;
27684         
27685         if(!touches){
27686             return;
27687         }
27688         
27689         if(touches.length == 1){
27690             this.onMouseDown(e);
27691             return;
27692         }
27693         
27694         if(touches.length != 2){
27695             return;
27696         }
27697         
27698         var coords = [];
27699         
27700         for(var i = 0, finger; finger = touches[i]; i++){
27701             coords.push(finger.pageX, finger.pageY);
27702         }
27703         
27704         var x = Math.pow(coords[0] - coords[2], 2);
27705         var y = Math.pow(coords[1] - coords[3], 2);
27706         
27707         this.startDistance = Math.sqrt(x + y);
27708         
27709         this.startScale = this.scale;
27710         
27711         this.pinching = true;
27712         this.dragable = false;
27713         
27714     },
27715     
27716     onTouchMove : function(e)
27717     {
27718         if(!this.pinching && !this.dragable){
27719             return;
27720         }
27721         
27722         var touches = e.browserEvent.touches;
27723         
27724         if(!touches){
27725             return;
27726         }
27727         
27728         if(this.dragable){
27729             this.onMouseMove(e);
27730             return;
27731         }
27732         
27733         var coords = [];
27734         
27735         for(var i = 0, finger; finger = touches[i]; i++){
27736             coords.push(finger.pageX, finger.pageY);
27737         }
27738         
27739         var x = Math.pow(coords[0] - coords[2], 2);
27740         var y = Math.pow(coords[1] - coords[3], 2);
27741         
27742         this.endDistance = Math.sqrt(x + y);
27743         
27744         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27745         
27746         if(!this.zoomable()){
27747             this.scale = this.startScale;
27748             return;
27749         }
27750         
27751         this.draw();
27752         
27753     },
27754     
27755     onTouchEnd : function(e)
27756     {
27757         this.pinching = false;
27758         this.dragable = false;
27759         
27760     },
27761     
27762     process : function(file, crop)
27763     {
27764         if(this.loadMask){
27765             this.maskEl.mask(this.loadingText);
27766         }
27767         
27768         this.xhr = new XMLHttpRequest();
27769         
27770         file.xhr = this.xhr;
27771
27772         this.xhr.open(this.method, this.url, true);
27773         
27774         var headers = {
27775             "Accept": "application/json",
27776             "Cache-Control": "no-cache",
27777             "X-Requested-With": "XMLHttpRequest"
27778         };
27779         
27780         for (var headerName in headers) {
27781             var headerValue = headers[headerName];
27782             if (headerValue) {
27783                 this.xhr.setRequestHeader(headerName, headerValue);
27784             }
27785         }
27786         
27787         var _this = this;
27788         
27789         this.xhr.onload = function()
27790         {
27791             _this.xhrOnLoad(_this.xhr);
27792         }
27793         
27794         this.xhr.onerror = function()
27795         {
27796             _this.xhrOnError(_this.xhr);
27797         }
27798         
27799         var formData = new FormData();
27800
27801         formData.append('returnHTML', 'NO');
27802         
27803         if(crop){
27804             formData.append('crop', crop);
27805         }
27806         
27807         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27808             formData.append(this.paramName, file, file.name);
27809         }
27810         
27811         if(typeof(file.filename) != 'undefined'){
27812             formData.append('filename', file.filename);
27813         }
27814         
27815         if(typeof(file.mimetype) != 'undefined'){
27816             formData.append('mimetype', file.mimetype);
27817         }
27818         
27819         if(this.fireEvent('arrange', this, formData) != false){
27820             this.xhr.send(formData);
27821         };
27822     },
27823     
27824     xhrOnLoad : function(xhr)
27825     {
27826         if(this.loadMask){
27827             this.maskEl.unmask();
27828         }
27829         
27830         if (xhr.readyState !== 4) {
27831             this.fireEvent('exception', this, xhr);
27832             return;
27833         }
27834
27835         var response = Roo.decode(xhr.responseText);
27836         
27837         if(!response.success){
27838             this.fireEvent('exception', this, xhr);
27839             return;
27840         }
27841         
27842         var response = Roo.decode(xhr.responseText);
27843         
27844         this.fireEvent('upload', this, response);
27845         
27846     },
27847     
27848     xhrOnError : function()
27849     {
27850         if(this.loadMask){
27851             this.maskEl.unmask();
27852         }
27853         
27854         Roo.log('xhr on error');
27855         
27856         var response = Roo.decode(xhr.responseText);
27857           
27858         Roo.log(response);
27859         
27860     },
27861     
27862     prepare : function(file)
27863     {   
27864         if(this.loadMask){
27865             this.maskEl.mask(this.loadingText);
27866         }
27867         
27868         this.file = false;
27869         this.exif = {};
27870         
27871         if(typeof(file) === 'string'){
27872             this.loadCanvas(file);
27873             return;
27874         }
27875         
27876         if(!file || !this.urlAPI){
27877             return;
27878         }
27879         
27880         this.file = file;
27881         this.cropType = file.type;
27882         
27883         var _this = this;
27884         
27885         if(this.fireEvent('prepare', this, this.file) != false){
27886             
27887             var reader = new FileReader();
27888             
27889             reader.onload = function (e) {
27890                 if (e.target.error) {
27891                     Roo.log(e.target.error);
27892                     return;
27893                 }
27894                 
27895                 var buffer = e.target.result,
27896                     dataView = new DataView(buffer),
27897                     offset = 2,
27898                     maxOffset = dataView.byteLength - 4,
27899                     markerBytes,
27900                     markerLength;
27901                 
27902                 if (dataView.getUint16(0) === 0xffd8) {
27903                     while (offset < maxOffset) {
27904                         markerBytes = dataView.getUint16(offset);
27905                         
27906                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27907                             markerLength = dataView.getUint16(offset + 2) + 2;
27908                             if (offset + markerLength > dataView.byteLength) {
27909                                 Roo.log('Invalid meta data: Invalid segment size.');
27910                                 break;
27911                             }
27912                             
27913                             if(markerBytes == 0xffe1){
27914                                 _this.parseExifData(
27915                                     dataView,
27916                                     offset,
27917                                     markerLength
27918                                 );
27919                             }
27920                             
27921                             offset += markerLength;
27922                             
27923                             continue;
27924                         }
27925                         
27926                         break;
27927                     }
27928                     
27929                 }
27930                 
27931                 var url = _this.urlAPI.createObjectURL(_this.file);
27932                 
27933                 _this.loadCanvas(url);
27934                 
27935                 return;
27936             }
27937             
27938             reader.readAsArrayBuffer(this.file);
27939             
27940         }
27941         
27942     },
27943     
27944     parseExifData : function(dataView, offset, length)
27945     {
27946         var tiffOffset = offset + 10,
27947             littleEndian,
27948             dirOffset;
27949     
27950         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27951             // No Exif data, might be XMP data instead
27952             return;
27953         }
27954         
27955         // Check for the ASCII code for "Exif" (0x45786966):
27956         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27957             // No Exif data, might be XMP data instead
27958             return;
27959         }
27960         if (tiffOffset + 8 > dataView.byteLength) {
27961             Roo.log('Invalid Exif data: Invalid segment size.');
27962             return;
27963         }
27964         // Check for the two null bytes:
27965         if (dataView.getUint16(offset + 8) !== 0x0000) {
27966             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27967             return;
27968         }
27969         // Check the byte alignment:
27970         switch (dataView.getUint16(tiffOffset)) {
27971         case 0x4949:
27972             littleEndian = true;
27973             break;
27974         case 0x4D4D:
27975             littleEndian = false;
27976             break;
27977         default:
27978             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27979             return;
27980         }
27981         // Check for the TIFF tag marker (0x002A):
27982         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27983             Roo.log('Invalid Exif data: Missing TIFF marker.');
27984             return;
27985         }
27986         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27987         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27988         
27989         this.parseExifTags(
27990             dataView,
27991             tiffOffset,
27992             tiffOffset + dirOffset,
27993             littleEndian
27994         );
27995     },
27996     
27997     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27998     {
27999         var tagsNumber,
28000             dirEndOffset,
28001             i;
28002         if (dirOffset + 6 > dataView.byteLength) {
28003             Roo.log('Invalid Exif data: Invalid directory offset.');
28004             return;
28005         }
28006         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28007         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28008         if (dirEndOffset + 4 > dataView.byteLength) {
28009             Roo.log('Invalid Exif data: Invalid directory size.');
28010             return;
28011         }
28012         for (i = 0; i < tagsNumber; i += 1) {
28013             this.parseExifTag(
28014                 dataView,
28015                 tiffOffset,
28016                 dirOffset + 2 + 12 * i, // tag offset
28017                 littleEndian
28018             );
28019         }
28020         // Return the offset to the next directory:
28021         return dataView.getUint32(dirEndOffset, littleEndian);
28022     },
28023     
28024     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28025     {
28026         var tag = dataView.getUint16(offset, littleEndian);
28027         
28028         this.exif[tag] = this.getExifValue(
28029             dataView,
28030             tiffOffset,
28031             offset,
28032             dataView.getUint16(offset + 2, littleEndian), // tag type
28033             dataView.getUint32(offset + 4, littleEndian), // tag length
28034             littleEndian
28035         );
28036     },
28037     
28038     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28039     {
28040         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28041             tagSize,
28042             dataOffset,
28043             values,
28044             i,
28045             str,
28046             c;
28047     
28048         if (!tagType) {
28049             Roo.log('Invalid Exif data: Invalid tag type.');
28050             return;
28051         }
28052         
28053         tagSize = tagType.size * length;
28054         // Determine if the value is contained in the dataOffset bytes,
28055         // or if the value at the dataOffset is a pointer to the actual data:
28056         dataOffset = tagSize > 4 ?
28057                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28058         if (dataOffset + tagSize > dataView.byteLength) {
28059             Roo.log('Invalid Exif data: Invalid data offset.');
28060             return;
28061         }
28062         if (length === 1) {
28063             return tagType.getValue(dataView, dataOffset, littleEndian);
28064         }
28065         values = [];
28066         for (i = 0; i < length; i += 1) {
28067             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28068         }
28069         
28070         if (tagType.ascii) {
28071             str = '';
28072             // Concatenate the chars:
28073             for (i = 0; i < values.length; i += 1) {
28074                 c = values[i];
28075                 // Ignore the terminating NULL byte(s):
28076                 if (c === '\u0000') {
28077                     break;
28078                 }
28079                 str += c;
28080             }
28081             return str;
28082         }
28083         return values;
28084     }
28085     
28086 });
28087
28088 Roo.apply(Roo.bootstrap.UploadCropbox, {
28089     tags : {
28090         'Orientation': 0x0112
28091     },
28092     
28093     Orientation: {
28094             1: 0, //'top-left',
28095 //            2: 'top-right',
28096             3: 180, //'bottom-right',
28097 //            4: 'bottom-left',
28098 //            5: 'left-top',
28099             6: 90, //'right-top',
28100 //            7: 'right-bottom',
28101             8: 270 //'left-bottom'
28102     },
28103     
28104     exifTagTypes : {
28105         // byte, 8-bit unsigned int:
28106         1: {
28107             getValue: function (dataView, dataOffset) {
28108                 return dataView.getUint8(dataOffset);
28109             },
28110             size: 1
28111         },
28112         // ascii, 8-bit byte:
28113         2: {
28114             getValue: function (dataView, dataOffset) {
28115                 return String.fromCharCode(dataView.getUint8(dataOffset));
28116             },
28117             size: 1,
28118             ascii: true
28119         },
28120         // short, 16 bit int:
28121         3: {
28122             getValue: function (dataView, dataOffset, littleEndian) {
28123                 return dataView.getUint16(dataOffset, littleEndian);
28124             },
28125             size: 2
28126         },
28127         // long, 32 bit int:
28128         4: {
28129             getValue: function (dataView, dataOffset, littleEndian) {
28130                 return dataView.getUint32(dataOffset, littleEndian);
28131             },
28132             size: 4
28133         },
28134         // rational = two long values, first is numerator, second is denominator:
28135         5: {
28136             getValue: function (dataView, dataOffset, littleEndian) {
28137                 return dataView.getUint32(dataOffset, littleEndian) /
28138                     dataView.getUint32(dataOffset + 4, littleEndian);
28139             },
28140             size: 8
28141         },
28142         // slong, 32 bit signed int:
28143         9: {
28144             getValue: function (dataView, dataOffset, littleEndian) {
28145                 return dataView.getInt32(dataOffset, littleEndian);
28146             },
28147             size: 4
28148         },
28149         // srational, two slongs, first is numerator, second is denominator:
28150         10: {
28151             getValue: function (dataView, dataOffset, littleEndian) {
28152                 return dataView.getInt32(dataOffset, littleEndian) /
28153                     dataView.getInt32(dataOffset + 4, littleEndian);
28154             },
28155             size: 8
28156         }
28157     },
28158     
28159     footer : {
28160         STANDARD : [
28161             {
28162                 tag : 'div',
28163                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28164                 action : 'rotate-left',
28165                 cn : [
28166                     {
28167                         tag : 'button',
28168                         cls : 'btn btn-default',
28169                         html : '<i class="fa fa-undo"></i>'
28170                     }
28171                 ]
28172             },
28173             {
28174                 tag : 'div',
28175                 cls : 'btn-group roo-upload-cropbox-picture',
28176                 action : 'picture',
28177                 cn : [
28178                     {
28179                         tag : 'button',
28180                         cls : 'btn btn-default',
28181                         html : '<i class="fa fa-picture-o"></i>'
28182                     }
28183                 ]
28184             },
28185             {
28186                 tag : 'div',
28187                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28188                 action : 'rotate-right',
28189                 cn : [
28190                     {
28191                         tag : 'button',
28192                         cls : 'btn btn-default',
28193                         html : '<i class="fa fa-repeat"></i>'
28194                     }
28195                 ]
28196             }
28197         ],
28198         DOCUMENT : [
28199             {
28200                 tag : 'div',
28201                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28202                 action : 'rotate-left',
28203                 cn : [
28204                     {
28205                         tag : 'button',
28206                         cls : 'btn btn-default',
28207                         html : '<i class="fa fa-undo"></i>'
28208                     }
28209                 ]
28210             },
28211             {
28212                 tag : 'div',
28213                 cls : 'btn-group roo-upload-cropbox-download',
28214                 action : 'download',
28215                 cn : [
28216                     {
28217                         tag : 'button',
28218                         cls : 'btn btn-default',
28219                         html : '<i class="fa fa-download"></i>'
28220                     }
28221                 ]
28222             },
28223             {
28224                 tag : 'div',
28225                 cls : 'btn-group roo-upload-cropbox-crop',
28226                 action : 'crop',
28227                 cn : [
28228                     {
28229                         tag : 'button',
28230                         cls : 'btn btn-default',
28231                         html : '<i class="fa fa-crop"></i>'
28232                     }
28233                 ]
28234             },
28235             {
28236                 tag : 'div',
28237                 cls : 'btn-group roo-upload-cropbox-trash',
28238                 action : 'trash',
28239                 cn : [
28240                     {
28241                         tag : 'button',
28242                         cls : 'btn btn-default',
28243                         html : '<i class="fa fa-trash"></i>'
28244                     }
28245                 ]
28246             },
28247             {
28248                 tag : 'div',
28249                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28250                 action : 'rotate-right',
28251                 cn : [
28252                     {
28253                         tag : 'button',
28254                         cls : 'btn btn-default',
28255                         html : '<i class="fa fa-repeat"></i>'
28256                     }
28257                 ]
28258             }
28259         ],
28260         ROTATOR : [
28261             {
28262                 tag : 'div',
28263                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28264                 action : 'rotate-left',
28265                 cn : [
28266                     {
28267                         tag : 'button',
28268                         cls : 'btn btn-default',
28269                         html : '<i class="fa fa-undo"></i>'
28270                     }
28271                 ]
28272             },
28273             {
28274                 tag : 'div',
28275                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28276                 action : 'rotate-right',
28277                 cn : [
28278                     {
28279                         tag : 'button',
28280                         cls : 'btn btn-default',
28281                         html : '<i class="fa fa-repeat"></i>'
28282                     }
28283                 ]
28284             }
28285         ]
28286     }
28287 });
28288
28289 /*
28290 * Licence: LGPL
28291 */
28292
28293 /**
28294  * @class Roo.bootstrap.DocumentManager
28295  * @extends Roo.bootstrap.Component
28296  * Bootstrap DocumentManager class
28297  * @cfg {String} paramName default 'imageUpload'
28298  * @cfg {String} toolTipName default 'filename'
28299  * @cfg {String} method default POST
28300  * @cfg {String} url action url
28301  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28302  * @cfg {Boolean} multiple multiple upload default true
28303  * @cfg {Number} thumbSize default 300
28304  * @cfg {String} fieldLabel
28305  * @cfg {Number} labelWidth default 4
28306  * @cfg {String} labelAlign (left|top) default left
28307  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28308 * @cfg {Number} labellg set the width of label (1-12)
28309  * @cfg {Number} labelmd set the width of label (1-12)
28310  * @cfg {Number} labelsm set the width of label (1-12)
28311  * @cfg {Number} labelxs set the width of label (1-12)
28312  * 
28313  * @constructor
28314  * Create a new DocumentManager
28315  * @param {Object} config The config object
28316  */
28317
28318 Roo.bootstrap.DocumentManager = function(config){
28319     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28320     
28321     this.files = [];
28322     this.delegates = [];
28323     
28324     this.addEvents({
28325         /**
28326          * @event initial
28327          * Fire when initial the DocumentManager
28328          * @param {Roo.bootstrap.DocumentManager} this
28329          */
28330         "initial" : true,
28331         /**
28332          * @event inspect
28333          * inspect selected file
28334          * @param {Roo.bootstrap.DocumentManager} this
28335          * @param {File} file
28336          */
28337         "inspect" : true,
28338         /**
28339          * @event exception
28340          * Fire when xhr load exception
28341          * @param {Roo.bootstrap.DocumentManager} this
28342          * @param {XMLHttpRequest} xhr
28343          */
28344         "exception" : true,
28345         /**
28346          * @event afterupload
28347          * Fire when xhr load exception
28348          * @param {Roo.bootstrap.DocumentManager} this
28349          * @param {XMLHttpRequest} xhr
28350          */
28351         "afterupload" : true,
28352         /**
28353          * @event prepare
28354          * prepare the form data
28355          * @param {Roo.bootstrap.DocumentManager} this
28356          * @param {Object} formData
28357          */
28358         "prepare" : true,
28359         /**
28360          * @event remove
28361          * Fire when remove the file
28362          * @param {Roo.bootstrap.DocumentManager} this
28363          * @param {Object} file
28364          */
28365         "remove" : true,
28366         /**
28367          * @event refresh
28368          * Fire after refresh the file
28369          * @param {Roo.bootstrap.DocumentManager} this
28370          */
28371         "refresh" : true,
28372         /**
28373          * @event click
28374          * Fire after click the image
28375          * @param {Roo.bootstrap.DocumentManager} this
28376          * @param {Object} file
28377          */
28378         "click" : true,
28379         /**
28380          * @event edit
28381          * Fire when upload a image and editable set to true
28382          * @param {Roo.bootstrap.DocumentManager} this
28383          * @param {Object} file
28384          */
28385         "edit" : true,
28386         /**
28387          * @event beforeselectfile
28388          * Fire before select file
28389          * @param {Roo.bootstrap.DocumentManager} this
28390          */
28391         "beforeselectfile" : true,
28392         /**
28393          * @event process
28394          * Fire before process file
28395          * @param {Roo.bootstrap.DocumentManager} this
28396          * @param {Object} file
28397          */
28398         "process" : true
28399         
28400     });
28401 };
28402
28403 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28404     
28405     boxes : 0,
28406     inputName : '',
28407     thumbSize : 300,
28408     multiple : true,
28409     files : false,
28410     method : 'POST',
28411     url : '',
28412     paramName : 'imageUpload',
28413     toolTipName : 'filename',
28414     fieldLabel : '',
28415     labelWidth : 4,
28416     labelAlign : 'left',
28417     editable : true,
28418     delegates : false,
28419     xhr : false, 
28420     
28421     labellg : 0,
28422     labelmd : 0,
28423     labelsm : 0,
28424     labelxs : 0,
28425     
28426     getAutoCreate : function()
28427     {   
28428         var managerWidget = {
28429             tag : 'div',
28430             cls : 'roo-document-manager',
28431             cn : [
28432                 {
28433                     tag : 'input',
28434                     cls : 'roo-document-manager-selector',
28435                     type : 'file'
28436                 },
28437                 {
28438                     tag : 'div',
28439                     cls : 'roo-document-manager-uploader',
28440                     cn : [
28441                         {
28442                             tag : 'div',
28443                             cls : 'roo-document-manager-upload-btn',
28444                             html : '<i class="fa fa-plus"></i>'
28445                         }
28446                     ]
28447                     
28448                 }
28449             ]
28450         };
28451         
28452         var content = [
28453             {
28454                 tag : 'div',
28455                 cls : 'column col-md-12',
28456                 cn : managerWidget
28457             }
28458         ];
28459         
28460         if(this.fieldLabel.length){
28461             
28462             content = [
28463                 {
28464                     tag : 'div',
28465                     cls : 'column col-md-12',
28466                     html : this.fieldLabel
28467                 },
28468                 {
28469                     tag : 'div',
28470                     cls : 'column col-md-12',
28471                     cn : managerWidget
28472                 }
28473             ];
28474
28475             if(this.labelAlign == 'left'){
28476                 content = [
28477                     {
28478                         tag : 'div',
28479                         cls : 'column',
28480                         html : this.fieldLabel
28481                     },
28482                     {
28483                         tag : 'div',
28484                         cls : 'column',
28485                         cn : managerWidget
28486                     }
28487                 ];
28488                 
28489                 if(this.labelWidth > 12){
28490                     content[0].style = "width: " + this.labelWidth + 'px';
28491                 }
28492
28493                 if(this.labelWidth < 13 && this.labelmd == 0){
28494                     this.labelmd = this.labelWidth;
28495                 }
28496
28497                 if(this.labellg > 0){
28498                     content[0].cls += ' col-lg-' + this.labellg;
28499                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28500                 }
28501
28502                 if(this.labelmd > 0){
28503                     content[0].cls += ' col-md-' + this.labelmd;
28504                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28505                 }
28506
28507                 if(this.labelsm > 0){
28508                     content[0].cls += ' col-sm-' + this.labelsm;
28509                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28510                 }
28511
28512                 if(this.labelxs > 0){
28513                     content[0].cls += ' col-xs-' + this.labelxs;
28514                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28515                 }
28516                 
28517             }
28518         }
28519         
28520         var cfg = {
28521             tag : 'div',
28522             cls : 'row clearfix',
28523             cn : content
28524         };
28525         
28526         return cfg;
28527         
28528     },
28529     
28530     initEvents : function()
28531     {
28532         this.managerEl = this.el.select('.roo-document-manager', true).first();
28533         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28534         
28535         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28536         this.selectorEl.hide();
28537         
28538         if(this.multiple){
28539             this.selectorEl.attr('multiple', 'multiple');
28540         }
28541         
28542         this.selectorEl.on('change', this.onFileSelected, this);
28543         
28544         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28545         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28546         
28547         this.uploader.on('click', this.onUploaderClick, this);
28548         
28549         this.renderProgressDialog();
28550         
28551         var _this = this;
28552         
28553         window.addEventListener("resize", function() { _this.refresh(); } );
28554         
28555         this.fireEvent('initial', this);
28556     },
28557     
28558     renderProgressDialog : function()
28559     {
28560         var _this = this;
28561         
28562         this.progressDialog = new Roo.bootstrap.Modal({
28563             cls : 'roo-document-manager-progress-dialog',
28564             allow_close : false,
28565             title : '',
28566             buttons : [
28567                 {
28568                     name  :'cancel',
28569                     weight : 'danger',
28570                     html : 'Cancel'
28571                 }
28572             ], 
28573             listeners : { 
28574                 btnclick : function() {
28575                     _this.uploadCancel();
28576                     this.hide();
28577                 }
28578             }
28579         });
28580          
28581         this.progressDialog.render(Roo.get(document.body));
28582          
28583         this.progress = new Roo.bootstrap.Progress({
28584             cls : 'roo-document-manager-progress',
28585             active : true,
28586             striped : true
28587         });
28588         
28589         this.progress.render(this.progressDialog.getChildContainer());
28590         
28591         this.progressBar = new Roo.bootstrap.ProgressBar({
28592             cls : 'roo-document-manager-progress-bar',
28593             aria_valuenow : 0,
28594             aria_valuemin : 0,
28595             aria_valuemax : 12,
28596             panel : 'success'
28597         });
28598         
28599         this.progressBar.render(this.progress.getChildContainer());
28600     },
28601     
28602     onUploaderClick : function(e)
28603     {
28604         e.preventDefault();
28605      
28606         if(this.fireEvent('beforeselectfile', this) != false){
28607             this.selectorEl.dom.click();
28608         }
28609         
28610     },
28611     
28612     onFileSelected : function(e)
28613     {
28614         e.preventDefault();
28615         
28616         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28617             return;
28618         }
28619         
28620         Roo.each(this.selectorEl.dom.files, function(file){
28621             if(this.fireEvent('inspect', this, file) != false){
28622                 this.files.push(file);
28623             }
28624         }, this);
28625         
28626         this.queue();
28627         
28628     },
28629     
28630     queue : function()
28631     {
28632         this.selectorEl.dom.value = '';
28633         
28634         if(!this.files.length){
28635             return;
28636         }
28637         
28638         if(this.boxes > 0 && this.files.length > this.boxes){
28639             this.files = this.files.slice(0, this.boxes);
28640         }
28641         
28642         this.uploader.show();
28643         
28644         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28645             this.uploader.hide();
28646         }
28647         
28648         var _this = this;
28649         
28650         var files = [];
28651         
28652         var docs = [];
28653         
28654         Roo.each(this.files, function(file){
28655             
28656             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28657                 var f = this.renderPreview(file);
28658                 files.push(f);
28659                 return;
28660             }
28661             
28662             if(file.type.indexOf('image') != -1){
28663                 this.delegates.push(
28664                     (function(){
28665                         _this.process(file);
28666                     }).createDelegate(this)
28667                 );
28668         
28669                 return;
28670             }
28671             
28672             docs.push(
28673                 (function(){
28674                     _this.process(file);
28675                 }).createDelegate(this)
28676             );
28677             
28678         }, this);
28679         
28680         this.files = files;
28681         
28682         this.delegates = this.delegates.concat(docs);
28683         
28684         if(!this.delegates.length){
28685             this.refresh();
28686             return;
28687         }
28688         
28689         this.progressBar.aria_valuemax = this.delegates.length;
28690         
28691         this.arrange();
28692         
28693         return;
28694     },
28695     
28696     arrange : function()
28697     {
28698         if(!this.delegates.length){
28699             this.progressDialog.hide();
28700             this.refresh();
28701             return;
28702         }
28703         
28704         var delegate = this.delegates.shift();
28705         
28706         this.progressDialog.show();
28707         
28708         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28709         
28710         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28711         
28712         delegate();
28713     },
28714     
28715     refresh : function()
28716     {
28717         this.uploader.show();
28718         
28719         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28720             this.uploader.hide();
28721         }
28722         
28723         Roo.isTouch ? this.closable(false) : this.closable(true);
28724         
28725         this.fireEvent('refresh', this);
28726     },
28727     
28728     onRemove : function(e, el, o)
28729     {
28730         e.preventDefault();
28731         
28732         this.fireEvent('remove', this, o);
28733         
28734     },
28735     
28736     remove : function(o)
28737     {
28738         var files = [];
28739         
28740         Roo.each(this.files, function(file){
28741             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28742                 files.push(file);
28743                 return;
28744             }
28745
28746             o.target.remove();
28747
28748         }, this);
28749         
28750         this.files = files;
28751         
28752         this.refresh();
28753     },
28754     
28755     clear : function()
28756     {
28757         Roo.each(this.files, function(file){
28758             if(!file.target){
28759                 return;
28760             }
28761             
28762             file.target.remove();
28763
28764         }, this);
28765         
28766         this.files = [];
28767         
28768         this.refresh();
28769     },
28770     
28771     onClick : function(e, el, o)
28772     {
28773         e.preventDefault();
28774         
28775         this.fireEvent('click', this, o);
28776         
28777     },
28778     
28779     closable : function(closable)
28780     {
28781         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28782             
28783             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28784             
28785             if(closable){
28786                 el.show();
28787                 return;
28788             }
28789             
28790             el.hide();
28791             
28792         }, this);
28793     },
28794     
28795     xhrOnLoad : function(xhr)
28796     {
28797         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28798             el.remove();
28799         }, this);
28800         
28801         if (xhr.readyState !== 4) {
28802             this.arrange();
28803             this.fireEvent('exception', this, xhr);
28804             return;
28805         }
28806
28807         var response = Roo.decode(xhr.responseText);
28808         
28809         if(!response.success){
28810             this.arrange();
28811             this.fireEvent('exception', this, xhr);
28812             return;
28813         }
28814         
28815         var file = this.renderPreview(response.data);
28816         
28817         this.files.push(file);
28818         
28819         this.arrange();
28820         
28821         this.fireEvent('afterupload', this, xhr);
28822         
28823     },
28824     
28825     xhrOnError : function(xhr)
28826     {
28827         Roo.log('xhr on error');
28828         
28829         var response = Roo.decode(xhr.responseText);
28830           
28831         Roo.log(response);
28832         
28833         this.arrange();
28834     },
28835     
28836     process : function(file)
28837     {
28838         if(this.fireEvent('process', this, file) !== false){
28839             if(this.editable && file.type.indexOf('image') != -1){
28840                 this.fireEvent('edit', this, file);
28841                 return;
28842             }
28843
28844             this.uploadStart(file, false);
28845
28846             return;
28847         }
28848         
28849     },
28850     
28851     uploadStart : function(file, crop)
28852     {
28853         this.xhr = new XMLHttpRequest();
28854         
28855         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28856             this.arrange();
28857             return;
28858         }
28859         
28860         file.xhr = this.xhr;
28861             
28862         this.managerEl.createChild({
28863             tag : 'div',
28864             cls : 'roo-document-manager-loading',
28865             cn : [
28866                 {
28867                     tag : 'div',
28868                     tooltip : file.name,
28869                     cls : 'roo-document-manager-thumb',
28870                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28871                 }
28872             ]
28873
28874         });
28875
28876         this.xhr.open(this.method, this.url, true);
28877         
28878         var headers = {
28879             "Accept": "application/json",
28880             "Cache-Control": "no-cache",
28881             "X-Requested-With": "XMLHttpRequest"
28882         };
28883         
28884         for (var headerName in headers) {
28885             var headerValue = headers[headerName];
28886             if (headerValue) {
28887                 this.xhr.setRequestHeader(headerName, headerValue);
28888             }
28889         }
28890         
28891         var _this = this;
28892         
28893         this.xhr.onload = function()
28894         {
28895             _this.xhrOnLoad(_this.xhr);
28896         }
28897         
28898         this.xhr.onerror = function()
28899         {
28900             _this.xhrOnError(_this.xhr);
28901         }
28902         
28903         var formData = new FormData();
28904
28905         formData.append('returnHTML', 'NO');
28906         
28907         if(crop){
28908             formData.append('crop', crop);
28909         }
28910         
28911         formData.append(this.paramName, file, file.name);
28912         
28913         var options = {
28914             file : file, 
28915             manually : false
28916         };
28917         
28918         if(this.fireEvent('prepare', this, formData, options) != false){
28919             
28920             if(options.manually){
28921                 return;
28922             }
28923             
28924             this.xhr.send(formData);
28925             return;
28926         };
28927         
28928         this.uploadCancel();
28929     },
28930     
28931     uploadCancel : function()
28932     {
28933         if (this.xhr) {
28934             this.xhr.abort();
28935         }
28936         
28937         this.delegates = [];
28938         
28939         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28940             el.remove();
28941         }, this);
28942         
28943         this.arrange();
28944     },
28945     
28946     renderPreview : function(file)
28947     {
28948         if(typeof(file.target) != 'undefined' && file.target){
28949             return file;
28950         }
28951         
28952         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28953         
28954         var previewEl = this.managerEl.createChild({
28955             tag : 'div',
28956             cls : 'roo-document-manager-preview',
28957             cn : [
28958                 {
28959                     tag : 'div',
28960                     tooltip : file[this.toolTipName],
28961                     cls : 'roo-document-manager-thumb',
28962                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28963                 },
28964                 {
28965                     tag : 'button',
28966                     cls : 'close',
28967                     html : '<i class="fa fa-times-circle"></i>'
28968                 }
28969             ]
28970         });
28971
28972         var close = previewEl.select('button.close', true).first();
28973
28974         close.on('click', this.onRemove, this, file);
28975
28976         file.target = previewEl;
28977
28978         var image = previewEl.select('img', true).first();
28979         
28980         var _this = this;
28981         
28982         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28983         
28984         image.on('click', this.onClick, this, file);
28985         
28986         return file;
28987         
28988     },
28989     
28990     onPreviewLoad : function(file, image)
28991     {
28992         if(typeof(file.target) == 'undefined' || !file.target){
28993             return;
28994         }
28995         
28996         var width = image.dom.naturalWidth || image.dom.width;
28997         var height = image.dom.naturalHeight || image.dom.height;
28998         
28999         if(width > height){
29000             file.target.addClass('wide');
29001             return;
29002         }
29003         
29004         file.target.addClass('tall');
29005         return;
29006         
29007     },
29008     
29009     uploadFromSource : function(file, crop)
29010     {
29011         this.xhr = new XMLHttpRequest();
29012         
29013         this.managerEl.createChild({
29014             tag : 'div',
29015             cls : 'roo-document-manager-loading',
29016             cn : [
29017                 {
29018                     tag : 'div',
29019                     tooltip : file.name,
29020                     cls : 'roo-document-manager-thumb',
29021                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29022                 }
29023             ]
29024
29025         });
29026
29027         this.xhr.open(this.method, this.url, true);
29028         
29029         var headers = {
29030             "Accept": "application/json",
29031             "Cache-Control": "no-cache",
29032             "X-Requested-With": "XMLHttpRequest"
29033         };
29034         
29035         for (var headerName in headers) {
29036             var headerValue = headers[headerName];
29037             if (headerValue) {
29038                 this.xhr.setRequestHeader(headerName, headerValue);
29039             }
29040         }
29041         
29042         var _this = this;
29043         
29044         this.xhr.onload = function()
29045         {
29046             _this.xhrOnLoad(_this.xhr);
29047         }
29048         
29049         this.xhr.onerror = function()
29050         {
29051             _this.xhrOnError(_this.xhr);
29052         }
29053         
29054         var formData = new FormData();
29055
29056         formData.append('returnHTML', 'NO');
29057         
29058         formData.append('crop', crop);
29059         
29060         if(typeof(file.filename) != 'undefined'){
29061             formData.append('filename', file.filename);
29062         }
29063         
29064         if(typeof(file.mimetype) != 'undefined'){
29065             formData.append('mimetype', file.mimetype);
29066         }
29067         
29068         Roo.log(formData);
29069         
29070         if(this.fireEvent('prepare', this, formData) != false){
29071             this.xhr.send(formData);
29072         };
29073     }
29074 });
29075
29076 /*
29077 * Licence: LGPL
29078 */
29079
29080 /**
29081  * @class Roo.bootstrap.DocumentViewer
29082  * @extends Roo.bootstrap.Component
29083  * Bootstrap DocumentViewer class
29084  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29085  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29086  * 
29087  * @constructor
29088  * Create a new DocumentViewer
29089  * @param {Object} config The config object
29090  */
29091
29092 Roo.bootstrap.DocumentViewer = function(config){
29093     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29094     
29095     this.addEvents({
29096         /**
29097          * @event initial
29098          * Fire after initEvent
29099          * @param {Roo.bootstrap.DocumentViewer} this
29100          */
29101         "initial" : true,
29102         /**
29103          * @event click
29104          * Fire after click
29105          * @param {Roo.bootstrap.DocumentViewer} this
29106          */
29107         "click" : true,
29108         /**
29109          * @event download
29110          * Fire after download button
29111          * @param {Roo.bootstrap.DocumentViewer} this
29112          */
29113         "download" : true,
29114         /**
29115          * @event trash
29116          * Fire after trash button
29117          * @param {Roo.bootstrap.DocumentViewer} this
29118          */
29119         "trash" : true
29120         
29121     });
29122 };
29123
29124 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29125     
29126     showDownload : true,
29127     
29128     showTrash : true,
29129     
29130     getAutoCreate : function()
29131     {
29132         var cfg = {
29133             tag : 'div',
29134             cls : 'roo-document-viewer',
29135             cn : [
29136                 {
29137                     tag : 'div',
29138                     cls : 'roo-document-viewer-body',
29139                     cn : [
29140                         {
29141                             tag : 'div',
29142                             cls : 'roo-document-viewer-thumb',
29143                             cn : [
29144                                 {
29145                                     tag : 'img',
29146                                     cls : 'roo-document-viewer-image'
29147                                 }
29148                             ]
29149                         }
29150                     ]
29151                 },
29152                 {
29153                     tag : 'div',
29154                     cls : 'roo-document-viewer-footer',
29155                     cn : {
29156                         tag : 'div',
29157                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29158                         cn : [
29159                             {
29160                                 tag : 'div',
29161                                 cls : 'btn-group roo-document-viewer-download',
29162                                 cn : [
29163                                     {
29164                                         tag : 'button',
29165                                         cls : 'btn btn-default',
29166                                         html : '<i class="fa fa-download"></i>'
29167                                     }
29168                                 ]
29169                             },
29170                             {
29171                                 tag : 'div',
29172                                 cls : 'btn-group roo-document-viewer-trash',
29173                                 cn : [
29174                                     {
29175                                         tag : 'button',
29176                                         cls : 'btn btn-default',
29177                                         html : '<i class="fa fa-trash"></i>'
29178                                     }
29179                                 ]
29180                             }
29181                         ]
29182                     }
29183                 }
29184             ]
29185         };
29186         
29187         return cfg;
29188     },
29189     
29190     initEvents : function()
29191     {
29192         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29193         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29194         
29195         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29196         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29197         
29198         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29199         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29200         
29201         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29202         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29203         
29204         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29205         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29206         
29207         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29208         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29209         
29210         this.bodyEl.on('click', this.onClick, this);
29211         this.downloadBtn.on('click', this.onDownload, this);
29212         this.trashBtn.on('click', this.onTrash, this);
29213         
29214         this.downloadBtn.hide();
29215         this.trashBtn.hide();
29216         
29217         if(this.showDownload){
29218             this.downloadBtn.show();
29219         }
29220         
29221         if(this.showTrash){
29222             this.trashBtn.show();
29223         }
29224         
29225         if(!this.showDownload && !this.showTrash) {
29226             this.footerEl.hide();
29227         }
29228         
29229     },
29230     
29231     initial : function()
29232     {
29233         this.fireEvent('initial', this);
29234         
29235     },
29236     
29237     onClick : function(e)
29238     {
29239         e.preventDefault();
29240         
29241         this.fireEvent('click', this);
29242     },
29243     
29244     onDownload : function(e)
29245     {
29246         e.preventDefault();
29247         
29248         this.fireEvent('download', this);
29249     },
29250     
29251     onTrash : function(e)
29252     {
29253         e.preventDefault();
29254         
29255         this.fireEvent('trash', this);
29256     }
29257     
29258 });
29259 /*
29260  * - LGPL
29261  *
29262  * nav progress bar
29263  * 
29264  */
29265
29266 /**
29267  * @class Roo.bootstrap.NavProgressBar
29268  * @extends Roo.bootstrap.Component
29269  * Bootstrap NavProgressBar class
29270  * 
29271  * @constructor
29272  * Create a new nav progress bar
29273  * @param {Object} config The config object
29274  */
29275
29276 Roo.bootstrap.NavProgressBar = function(config){
29277     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29278
29279     this.bullets = this.bullets || [];
29280    
29281 //    Roo.bootstrap.NavProgressBar.register(this);
29282      this.addEvents({
29283         /**
29284              * @event changed
29285              * Fires when the active item changes
29286              * @param {Roo.bootstrap.NavProgressBar} this
29287              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29288              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29289          */
29290         'changed': true
29291      });
29292     
29293 };
29294
29295 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29296     
29297     bullets : [],
29298     barItems : [],
29299     
29300     getAutoCreate : function()
29301     {
29302         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29303         
29304         cfg = {
29305             tag : 'div',
29306             cls : 'roo-navigation-bar-group',
29307             cn : [
29308                 {
29309                     tag : 'div',
29310                     cls : 'roo-navigation-top-bar'
29311                 },
29312                 {
29313                     tag : 'div',
29314                     cls : 'roo-navigation-bullets-bar',
29315                     cn : [
29316                         {
29317                             tag : 'ul',
29318                             cls : 'roo-navigation-bar'
29319                         }
29320                     ]
29321                 },
29322                 
29323                 {
29324                     tag : 'div',
29325                     cls : 'roo-navigation-bottom-bar'
29326                 }
29327             ]
29328             
29329         };
29330         
29331         return cfg;
29332         
29333     },
29334     
29335     initEvents: function() 
29336     {
29337         
29338     },
29339     
29340     onRender : function(ct, position) 
29341     {
29342         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29343         
29344         if(this.bullets.length){
29345             Roo.each(this.bullets, function(b){
29346                this.addItem(b);
29347             }, this);
29348         }
29349         
29350         this.format();
29351         
29352     },
29353     
29354     addItem : function(cfg)
29355     {
29356         var item = new Roo.bootstrap.NavProgressItem(cfg);
29357         
29358         item.parentId = this.id;
29359         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29360         
29361         if(cfg.html){
29362             var top = new Roo.bootstrap.Element({
29363                 tag : 'div',
29364                 cls : 'roo-navigation-bar-text'
29365             });
29366             
29367             var bottom = new Roo.bootstrap.Element({
29368                 tag : 'div',
29369                 cls : 'roo-navigation-bar-text'
29370             });
29371             
29372             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29373             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29374             
29375             var topText = new Roo.bootstrap.Element({
29376                 tag : 'span',
29377                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29378             });
29379             
29380             var bottomText = new Roo.bootstrap.Element({
29381                 tag : 'span',
29382                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29383             });
29384             
29385             topText.onRender(top.el, null);
29386             bottomText.onRender(bottom.el, null);
29387             
29388             item.topEl = top;
29389             item.bottomEl = bottom;
29390         }
29391         
29392         this.barItems.push(item);
29393         
29394         return item;
29395     },
29396     
29397     getActive : function()
29398     {
29399         var active = false;
29400         
29401         Roo.each(this.barItems, function(v){
29402             
29403             if (!v.isActive()) {
29404                 return;
29405             }
29406             
29407             active = v;
29408             return false;
29409             
29410         });
29411         
29412         return active;
29413     },
29414     
29415     setActiveItem : function(item)
29416     {
29417         var prev = false;
29418         
29419         Roo.each(this.barItems, function(v){
29420             if (v.rid == item.rid) {
29421                 return ;
29422             }
29423             
29424             if (v.isActive()) {
29425                 v.setActive(false);
29426                 prev = v;
29427             }
29428         });
29429
29430         item.setActive(true);
29431         
29432         this.fireEvent('changed', this, item, prev);
29433     },
29434     
29435     getBarItem: function(rid)
29436     {
29437         var ret = false;
29438         
29439         Roo.each(this.barItems, function(e) {
29440             if (e.rid != rid) {
29441                 return;
29442             }
29443             
29444             ret =  e;
29445             return false;
29446         });
29447         
29448         return ret;
29449     },
29450     
29451     indexOfItem : function(item)
29452     {
29453         var index = false;
29454         
29455         Roo.each(this.barItems, function(v, i){
29456             
29457             if (v.rid != item.rid) {
29458                 return;
29459             }
29460             
29461             index = i;
29462             return false
29463         });
29464         
29465         return index;
29466     },
29467     
29468     setActiveNext : function()
29469     {
29470         var i = this.indexOfItem(this.getActive());
29471         
29472         if (i > this.barItems.length) {
29473             return;
29474         }
29475         
29476         this.setActiveItem(this.barItems[i+1]);
29477     },
29478     
29479     setActivePrev : function()
29480     {
29481         var i = this.indexOfItem(this.getActive());
29482         
29483         if (i  < 1) {
29484             return;
29485         }
29486         
29487         this.setActiveItem(this.barItems[i-1]);
29488     },
29489     
29490     format : function()
29491     {
29492         if(!this.barItems.length){
29493             return;
29494         }
29495      
29496         var width = 100 / this.barItems.length;
29497         
29498         Roo.each(this.barItems, function(i){
29499             i.el.setStyle('width', width + '%');
29500             i.topEl.el.setStyle('width', width + '%');
29501             i.bottomEl.el.setStyle('width', width + '%');
29502         }, this);
29503         
29504     }
29505     
29506 });
29507 /*
29508  * - LGPL
29509  *
29510  * Nav Progress Item
29511  * 
29512  */
29513
29514 /**
29515  * @class Roo.bootstrap.NavProgressItem
29516  * @extends Roo.bootstrap.Component
29517  * Bootstrap NavProgressItem class
29518  * @cfg {String} rid the reference id
29519  * @cfg {Boolean} active (true|false) Is item active default false
29520  * @cfg {Boolean} disabled (true|false) Is item active default false
29521  * @cfg {String} html
29522  * @cfg {String} position (top|bottom) text position default bottom
29523  * @cfg {String} icon show icon instead of number
29524  * 
29525  * @constructor
29526  * Create a new NavProgressItem
29527  * @param {Object} config The config object
29528  */
29529 Roo.bootstrap.NavProgressItem = function(config){
29530     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29531     this.addEvents({
29532         // raw events
29533         /**
29534          * @event click
29535          * The raw click event for the entire grid.
29536          * @param {Roo.bootstrap.NavProgressItem} this
29537          * @param {Roo.EventObject} e
29538          */
29539         "click" : true
29540     });
29541    
29542 };
29543
29544 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29545     
29546     rid : '',
29547     active : false,
29548     disabled : false,
29549     html : '',
29550     position : 'bottom',
29551     icon : false,
29552     
29553     getAutoCreate : function()
29554     {
29555         var iconCls = 'roo-navigation-bar-item-icon';
29556         
29557         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29558         
29559         var cfg = {
29560             tag: 'li',
29561             cls: 'roo-navigation-bar-item',
29562             cn : [
29563                 {
29564                     tag : 'i',
29565                     cls : iconCls
29566                 }
29567             ]
29568         };
29569         
29570         if(this.active){
29571             cfg.cls += ' active';
29572         }
29573         if(this.disabled){
29574             cfg.cls += ' disabled';
29575         }
29576         
29577         return cfg;
29578     },
29579     
29580     disable : function()
29581     {
29582         this.setDisabled(true);
29583     },
29584     
29585     enable : function()
29586     {
29587         this.setDisabled(false);
29588     },
29589     
29590     initEvents: function() 
29591     {
29592         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29593         
29594         this.iconEl.on('click', this.onClick, this);
29595     },
29596     
29597     onClick : function(e)
29598     {
29599         e.preventDefault();
29600         
29601         if(this.disabled){
29602             return;
29603         }
29604         
29605         if(this.fireEvent('click', this, e) === false){
29606             return;
29607         };
29608         
29609         this.parent().setActiveItem(this);
29610     },
29611     
29612     isActive: function () 
29613     {
29614         return this.active;
29615     },
29616     
29617     setActive : function(state)
29618     {
29619         if(this.active == state){
29620             return;
29621         }
29622         
29623         this.active = state;
29624         
29625         if (state) {
29626             this.el.addClass('active');
29627             return;
29628         }
29629         
29630         this.el.removeClass('active');
29631         
29632         return;
29633     },
29634     
29635     setDisabled : function(state)
29636     {
29637         if(this.disabled == state){
29638             return;
29639         }
29640         
29641         this.disabled = state;
29642         
29643         if (state) {
29644             this.el.addClass('disabled');
29645             return;
29646         }
29647         
29648         this.el.removeClass('disabled');
29649     },
29650     
29651     tooltipEl : function()
29652     {
29653         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29654     }
29655 });
29656  
29657
29658  /*
29659  * - LGPL
29660  *
29661  * FieldLabel
29662  * 
29663  */
29664
29665 /**
29666  * @class Roo.bootstrap.FieldLabel
29667  * @extends Roo.bootstrap.Component
29668  * Bootstrap FieldLabel class
29669  * @cfg {String} html contents of the element
29670  * @cfg {String} tag tag of the element default label
29671  * @cfg {String} cls class of the element
29672  * @cfg {String} target label target 
29673  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29674  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29675  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29676  * @cfg {String} iconTooltip default "This field is required"
29677  * 
29678  * @constructor
29679  * Create a new FieldLabel
29680  * @param {Object} config The config object
29681  */
29682
29683 Roo.bootstrap.FieldLabel = function(config){
29684     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29685     
29686     this.addEvents({
29687             /**
29688              * @event invalid
29689              * Fires after the field has been marked as invalid.
29690              * @param {Roo.form.FieldLabel} this
29691              * @param {String} msg The validation message
29692              */
29693             invalid : true,
29694             /**
29695              * @event valid
29696              * Fires after the field has been validated with no errors.
29697              * @param {Roo.form.FieldLabel} this
29698              */
29699             valid : true
29700         });
29701 };
29702
29703 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29704     
29705     tag: 'label',
29706     cls: '',
29707     html: '',
29708     target: '',
29709     allowBlank : true,
29710     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29711     validClass : 'text-success fa fa-lg fa-check',
29712     iconTooltip : 'This field is required',
29713     
29714     getAutoCreate : function(){
29715         
29716         var cfg = {
29717             tag : this.tag,
29718             cls : 'roo-bootstrap-field-label ' + this.cls,
29719             for : this.target,
29720             cn : [
29721                 {
29722                     tag : 'i',
29723                     cls : '',
29724                     tooltip : this.iconTooltip
29725                 },
29726                 {
29727                     tag : 'span',
29728                     html : this.html
29729                 }
29730             ] 
29731         };
29732         
29733         return cfg;
29734     },
29735     
29736     initEvents: function() 
29737     {
29738         Roo.bootstrap.Element.superclass.initEvents.call(this);
29739         
29740         this.iconEl = this.el.select('i', true).first();
29741         
29742         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29743         
29744         Roo.bootstrap.FieldLabel.register(this);
29745     },
29746     
29747     /**
29748      * Mark this field as valid
29749      */
29750     markValid : function()
29751     {
29752         this.iconEl.show();
29753         
29754         this.iconEl.removeClass(this.invalidClass);
29755         
29756         this.iconEl.addClass(this.validClass);
29757         
29758         this.fireEvent('valid', this);
29759     },
29760     
29761     /**
29762      * Mark this field as invalid
29763      * @param {String} msg The validation message
29764      */
29765     markInvalid : function(msg)
29766     {
29767         this.iconEl.show();
29768         
29769         this.iconEl.removeClass(this.validClass);
29770         
29771         this.iconEl.addClass(this.invalidClass);
29772         
29773         this.fireEvent('invalid', this, msg);
29774     }
29775     
29776    
29777 });
29778
29779 Roo.apply(Roo.bootstrap.FieldLabel, {
29780     
29781     groups: {},
29782     
29783      /**
29784     * register a FieldLabel Group
29785     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29786     */
29787     register : function(label)
29788     {
29789         if(this.groups.hasOwnProperty(label.target)){
29790             return;
29791         }
29792      
29793         this.groups[label.target] = label;
29794         
29795     },
29796     /**
29797     * fetch a FieldLabel Group based on the target
29798     * @param {string} target
29799     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29800     */
29801     get: function(target) {
29802         if (typeof(this.groups[target]) == 'undefined') {
29803             return false;
29804         }
29805         
29806         return this.groups[target] ;
29807     }
29808 });
29809
29810  
29811
29812  /*
29813  * - LGPL
29814  *
29815  * page DateSplitField.
29816  * 
29817  */
29818
29819
29820 /**
29821  * @class Roo.bootstrap.DateSplitField
29822  * @extends Roo.bootstrap.Component
29823  * Bootstrap DateSplitField class
29824  * @cfg {string} fieldLabel - the label associated
29825  * @cfg {Number} labelWidth set the width of label (0-12)
29826  * @cfg {String} labelAlign (top|left)
29827  * @cfg {Boolean} dayAllowBlank (true|false) default false
29828  * @cfg {Boolean} monthAllowBlank (true|false) default false
29829  * @cfg {Boolean} yearAllowBlank (true|false) default false
29830  * @cfg {string} dayPlaceholder 
29831  * @cfg {string} monthPlaceholder
29832  * @cfg {string} yearPlaceholder
29833  * @cfg {string} dayFormat default 'd'
29834  * @cfg {string} monthFormat default 'm'
29835  * @cfg {string} yearFormat default 'Y'
29836  * @cfg {Number} labellg set the width of label (1-12)
29837  * @cfg {Number} labelmd set the width of label (1-12)
29838  * @cfg {Number} labelsm set the width of label (1-12)
29839  * @cfg {Number} labelxs set the width of label (1-12)
29840
29841  *     
29842  * @constructor
29843  * Create a new DateSplitField
29844  * @param {Object} config The config object
29845  */
29846
29847 Roo.bootstrap.DateSplitField = function(config){
29848     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29849     
29850     this.addEvents({
29851         // raw events
29852          /**
29853          * @event years
29854          * getting the data of years
29855          * @param {Roo.bootstrap.DateSplitField} this
29856          * @param {Object} years
29857          */
29858         "years" : true,
29859         /**
29860          * @event days
29861          * getting the data of days
29862          * @param {Roo.bootstrap.DateSplitField} this
29863          * @param {Object} days
29864          */
29865         "days" : true,
29866         /**
29867          * @event invalid
29868          * Fires after the field has been marked as invalid.
29869          * @param {Roo.form.Field} this
29870          * @param {String} msg The validation message
29871          */
29872         invalid : true,
29873        /**
29874          * @event valid
29875          * Fires after the field has been validated with no errors.
29876          * @param {Roo.form.Field} this
29877          */
29878         valid : true
29879     });
29880 };
29881
29882 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29883     
29884     fieldLabel : '',
29885     labelAlign : 'top',
29886     labelWidth : 3,
29887     dayAllowBlank : false,
29888     monthAllowBlank : false,
29889     yearAllowBlank : false,
29890     dayPlaceholder : '',
29891     monthPlaceholder : '',
29892     yearPlaceholder : '',
29893     dayFormat : 'd',
29894     monthFormat : 'm',
29895     yearFormat : 'Y',
29896     isFormField : true,
29897     labellg : 0,
29898     labelmd : 0,
29899     labelsm : 0,
29900     labelxs : 0,
29901     
29902     getAutoCreate : function()
29903     {
29904         var cfg = {
29905             tag : 'div',
29906             cls : 'row roo-date-split-field-group',
29907             cn : [
29908                 {
29909                     tag : 'input',
29910                     type : 'hidden',
29911                     cls : 'form-hidden-field roo-date-split-field-group-value',
29912                     name : this.name
29913                 }
29914             ]
29915         };
29916         
29917         var labelCls = 'col-md-12';
29918         var contentCls = 'col-md-4';
29919         
29920         if(this.fieldLabel){
29921             
29922             var label = {
29923                 tag : 'div',
29924                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29925                 cn : [
29926                     {
29927                         tag : 'label',
29928                         html : this.fieldLabel
29929                     }
29930                 ]
29931             };
29932             
29933             if(this.labelAlign == 'left'){
29934             
29935                 if(this.labelWidth > 12){
29936                     label.style = "width: " + this.labelWidth + 'px';
29937                 }
29938
29939                 if(this.labelWidth < 13 && this.labelmd == 0){
29940                     this.labelmd = this.labelWidth;
29941                 }
29942
29943                 if(this.labellg > 0){
29944                     labelCls = ' col-lg-' + this.labellg;
29945                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29946                 }
29947
29948                 if(this.labelmd > 0){
29949                     labelCls = ' col-md-' + this.labelmd;
29950                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29951                 }
29952
29953                 if(this.labelsm > 0){
29954                     labelCls = ' col-sm-' + this.labelsm;
29955                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29956                 }
29957
29958                 if(this.labelxs > 0){
29959                     labelCls = ' col-xs-' + this.labelxs;
29960                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29961                 }
29962             }
29963             
29964             label.cls += ' ' + labelCls;
29965             
29966             cfg.cn.push(label);
29967         }
29968         
29969         Roo.each(['day', 'month', 'year'], function(t){
29970             cfg.cn.push({
29971                 tag : 'div',
29972                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29973             });
29974         }, this);
29975         
29976         return cfg;
29977     },
29978     
29979     inputEl: function ()
29980     {
29981         return this.el.select('.roo-date-split-field-group-value', true).first();
29982     },
29983     
29984     onRender : function(ct, position) 
29985     {
29986         var _this = this;
29987         
29988         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29989         
29990         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29991         
29992         this.dayField = new Roo.bootstrap.ComboBox({
29993             allowBlank : this.dayAllowBlank,
29994             alwaysQuery : true,
29995             displayField : 'value',
29996             editable : false,
29997             fieldLabel : '',
29998             forceSelection : true,
29999             mode : 'local',
30000             placeholder : this.dayPlaceholder,
30001             selectOnFocus : true,
30002             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30003             triggerAction : 'all',
30004             typeAhead : true,
30005             valueField : 'value',
30006             store : new Roo.data.SimpleStore({
30007                 data : (function() {    
30008                     var days = [];
30009                     _this.fireEvent('days', _this, days);
30010                     return days;
30011                 })(),
30012                 fields : [ 'value' ]
30013             }),
30014             listeners : {
30015                 select : function (_self, record, index)
30016                 {
30017                     _this.setValue(_this.getValue());
30018                 }
30019             }
30020         });
30021
30022         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30023         
30024         this.monthField = new Roo.bootstrap.MonthField({
30025             after : '<i class=\"fa fa-calendar\"></i>',
30026             allowBlank : this.monthAllowBlank,
30027             placeholder : this.monthPlaceholder,
30028             readOnly : true,
30029             listeners : {
30030                 render : function (_self)
30031                 {
30032                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30033                         e.preventDefault();
30034                         _self.focus();
30035                     });
30036                 },
30037                 select : function (_self, oldvalue, newvalue)
30038                 {
30039                     _this.setValue(_this.getValue());
30040                 }
30041             }
30042         });
30043         
30044         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30045         
30046         this.yearField = new Roo.bootstrap.ComboBox({
30047             allowBlank : this.yearAllowBlank,
30048             alwaysQuery : true,
30049             displayField : 'value',
30050             editable : false,
30051             fieldLabel : '',
30052             forceSelection : true,
30053             mode : 'local',
30054             placeholder : this.yearPlaceholder,
30055             selectOnFocus : true,
30056             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30057             triggerAction : 'all',
30058             typeAhead : true,
30059             valueField : 'value',
30060             store : new Roo.data.SimpleStore({
30061                 data : (function() {
30062                     var years = [];
30063                     _this.fireEvent('years', _this, years);
30064                     return years;
30065                 })(),
30066                 fields : [ 'value' ]
30067             }),
30068             listeners : {
30069                 select : function (_self, record, index)
30070                 {
30071                     _this.setValue(_this.getValue());
30072                 }
30073             }
30074         });
30075
30076         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30077     },
30078     
30079     setValue : function(v, format)
30080     {
30081         this.inputEl.dom.value = v;
30082         
30083         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30084         
30085         var d = Date.parseDate(v, f);
30086         
30087         if(!d){
30088             this.validate();
30089             return;
30090         }
30091         
30092         this.setDay(d.format(this.dayFormat));
30093         this.setMonth(d.format(this.monthFormat));
30094         this.setYear(d.format(this.yearFormat));
30095         
30096         this.validate();
30097         
30098         return;
30099     },
30100     
30101     setDay : function(v)
30102     {
30103         this.dayField.setValue(v);
30104         this.inputEl.dom.value = this.getValue();
30105         this.validate();
30106         return;
30107     },
30108     
30109     setMonth : function(v)
30110     {
30111         this.monthField.setValue(v, true);
30112         this.inputEl.dom.value = this.getValue();
30113         this.validate();
30114         return;
30115     },
30116     
30117     setYear : function(v)
30118     {
30119         this.yearField.setValue(v);
30120         this.inputEl.dom.value = this.getValue();
30121         this.validate();
30122         return;
30123     },
30124     
30125     getDay : function()
30126     {
30127         return this.dayField.getValue();
30128     },
30129     
30130     getMonth : function()
30131     {
30132         return this.monthField.getValue();
30133     },
30134     
30135     getYear : function()
30136     {
30137         return this.yearField.getValue();
30138     },
30139     
30140     getValue : function()
30141     {
30142         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30143         
30144         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30145         
30146         return date;
30147     },
30148     
30149     reset : function()
30150     {
30151         this.setDay('');
30152         this.setMonth('');
30153         this.setYear('');
30154         this.inputEl.dom.value = '';
30155         this.validate();
30156         return;
30157     },
30158     
30159     validate : function()
30160     {
30161         var d = this.dayField.validate();
30162         var m = this.monthField.validate();
30163         var y = this.yearField.validate();
30164         
30165         var valid = true;
30166         
30167         if(
30168                 (!this.dayAllowBlank && !d) ||
30169                 (!this.monthAllowBlank && !m) ||
30170                 (!this.yearAllowBlank && !y)
30171         ){
30172             valid = false;
30173         }
30174         
30175         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30176             return valid;
30177         }
30178         
30179         if(valid){
30180             this.markValid();
30181             return valid;
30182         }
30183         
30184         this.markInvalid();
30185         
30186         return valid;
30187     },
30188     
30189     markValid : function()
30190     {
30191         
30192         var label = this.el.select('label', true).first();
30193         var icon = this.el.select('i.fa-star', true).first();
30194
30195         if(label && icon){
30196             icon.remove();
30197         }
30198         
30199         this.fireEvent('valid', this);
30200     },
30201     
30202      /**
30203      * Mark this field as invalid
30204      * @param {String} msg The validation message
30205      */
30206     markInvalid : function(msg)
30207     {
30208         
30209         var label = this.el.select('label', true).first();
30210         var icon = this.el.select('i.fa-star', true).first();
30211
30212         if(label && !icon){
30213             this.el.select('.roo-date-split-field-label', true).createChild({
30214                 tag : 'i',
30215                 cls : 'text-danger fa fa-lg fa-star',
30216                 tooltip : 'This field is required',
30217                 style : 'margin-right:5px;'
30218             }, label, true);
30219         }
30220         
30221         this.fireEvent('invalid', this, msg);
30222     },
30223     
30224     clearInvalid : function()
30225     {
30226         var label = this.el.select('label', true).first();
30227         var icon = this.el.select('i.fa-star', true).first();
30228
30229         if(label && icon){
30230             icon.remove();
30231         }
30232         
30233         this.fireEvent('valid', this);
30234     },
30235     
30236     getName: function()
30237     {
30238         return this.name;
30239     }
30240     
30241 });
30242
30243  /**
30244  *
30245  * This is based on 
30246  * http://masonry.desandro.com
30247  *
30248  * The idea is to render all the bricks based on vertical width...
30249  *
30250  * The original code extends 'outlayer' - we might need to use that....
30251  * 
30252  */
30253
30254
30255 /**
30256  * @class Roo.bootstrap.LayoutMasonry
30257  * @extends Roo.bootstrap.Component
30258  * Bootstrap Layout Masonry class
30259  * 
30260  * @constructor
30261  * Create a new Element
30262  * @param {Object} config The config object
30263  */
30264
30265 Roo.bootstrap.LayoutMasonry = function(config){
30266     
30267     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30268     
30269     this.bricks = [];
30270     
30271     Roo.bootstrap.LayoutMasonry.register(this);
30272     
30273     this.addEvents({
30274         // raw events
30275         /**
30276          * @event layout
30277          * Fire after layout the items
30278          * @param {Roo.bootstrap.LayoutMasonry} this
30279          * @param {Roo.EventObject} e
30280          */
30281         "layout" : true
30282     });
30283     
30284 };
30285
30286 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30287     
30288     /**
30289      * @cfg {Boolean} isLayoutInstant = no animation?
30290      */   
30291     isLayoutInstant : false, // needed?
30292    
30293     /**
30294      * @cfg {Number} boxWidth  width of the columns
30295      */   
30296     boxWidth : 450,
30297     
30298       /**
30299      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30300      */   
30301     boxHeight : 0,
30302     
30303     /**
30304      * @cfg {Number} padWidth padding below box..
30305      */   
30306     padWidth : 10, 
30307     
30308     /**
30309      * @cfg {Number} gutter gutter width..
30310      */   
30311     gutter : 10,
30312     
30313      /**
30314      * @cfg {Number} maxCols maximum number of columns
30315      */   
30316     
30317     maxCols: 0,
30318     
30319     /**
30320      * @cfg {Boolean} isAutoInitial defalut true
30321      */   
30322     isAutoInitial : true, 
30323     
30324     containerWidth: 0,
30325     
30326     /**
30327      * @cfg {Boolean} isHorizontal defalut false
30328      */   
30329     isHorizontal : false, 
30330
30331     currentSize : null,
30332     
30333     tag: 'div',
30334     
30335     cls: '',
30336     
30337     bricks: null, //CompositeElement
30338     
30339     cols : 1,
30340     
30341     _isLayoutInited : false,
30342     
30343 //    isAlternative : false, // only use for vertical layout...
30344     
30345     /**
30346      * @cfg {Number} alternativePadWidth padding below box..
30347      */   
30348     alternativePadWidth : 50,
30349     
30350     selectedBrick : [],
30351     
30352     getAutoCreate : function(){
30353         
30354         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30355         
30356         var cfg = {
30357             tag: this.tag,
30358             cls: 'blog-masonary-wrapper ' + this.cls,
30359             cn : {
30360                 cls : 'mas-boxes masonary'
30361             }
30362         };
30363         
30364         return cfg;
30365     },
30366     
30367     getChildContainer: function( )
30368     {
30369         if (this.boxesEl) {
30370             return this.boxesEl;
30371         }
30372         
30373         this.boxesEl = this.el.select('.mas-boxes').first();
30374         
30375         return this.boxesEl;
30376     },
30377     
30378     
30379     initEvents : function()
30380     {
30381         var _this = this;
30382         
30383         if(this.isAutoInitial){
30384             Roo.log('hook children rendered');
30385             this.on('childrenrendered', function() {
30386                 Roo.log('children rendered');
30387                 _this.initial();
30388             } ,this);
30389         }
30390     },
30391     
30392     initial : function()
30393     {
30394         this.selectedBrick = [];
30395         
30396         this.currentSize = this.el.getBox(true);
30397         
30398         Roo.EventManager.onWindowResize(this.resize, this); 
30399
30400         if(!this.isAutoInitial){
30401             this.layout();
30402             return;
30403         }
30404         
30405         this.layout();
30406         
30407         return;
30408         //this.layout.defer(500,this);
30409         
30410     },
30411     
30412     resize : function()
30413     {
30414         var cs = this.el.getBox(true);
30415         
30416         if (
30417                 this.currentSize.width == cs.width && 
30418                 this.currentSize.x == cs.x && 
30419                 this.currentSize.height == cs.height && 
30420                 this.currentSize.y == cs.y 
30421         ) {
30422             Roo.log("no change in with or X or Y");
30423             return;
30424         }
30425         
30426         this.currentSize = cs;
30427         
30428         this.layout();
30429         
30430     },
30431     
30432     layout : function()
30433     {   
30434         this._resetLayout();
30435         
30436         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30437         
30438         this.layoutItems( isInstant );
30439       
30440         this._isLayoutInited = true;
30441         
30442         this.fireEvent('layout', this);
30443         
30444     },
30445     
30446     _resetLayout : function()
30447     {
30448         if(this.isHorizontal){
30449             this.horizontalMeasureColumns();
30450             return;
30451         }
30452         
30453         this.verticalMeasureColumns();
30454         
30455     },
30456     
30457     verticalMeasureColumns : function()
30458     {
30459         this.getContainerWidth();
30460         
30461 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30462 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30463 //            return;
30464 //        }
30465         
30466         var boxWidth = this.boxWidth + this.padWidth;
30467         
30468         if(this.containerWidth < this.boxWidth){
30469             boxWidth = this.containerWidth
30470         }
30471         
30472         var containerWidth = this.containerWidth;
30473         
30474         var cols = Math.floor(containerWidth / boxWidth);
30475         
30476         this.cols = Math.max( cols, 1 );
30477         
30478         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30479         
30480         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30481         
30482         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30483         
30484         this.colWidth = boxWidth + avail - this.padWidth;
30485         
30486         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30487         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30488     },
30489     
30490     horizontalMeasureColumns : function()
30491     {
30492         this.getContainerWidth();
30493         
30494         var boxWidth = this.boxWidth;
30495         
30496         if(this.containerWidth < boxWidth){
30497             boxWidth = this.containerWidth;
30498         }
30499         
30500         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30501         
30502         this.el.setHeight(boxWidth);
30503         
30504     },
30505     
30506     getContainerWidth : function()
30507     {
30508         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30509     },
30510     
30511     layoutItems : function( isInstant )
30512     {
30513         Roo.log(this.bricks);
30514         
30515         var items = Roo.apply([], this.bricks);
30516         
30517         if(this.isHorizontal){
30518             this._horizontalLayoutItems( items , isInstant );
30519             return;
30520         }
30521         
30522 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30523 //            this._verticalAlternativeLayoutItems( items , isInstant );
30524 //            return;
30525 //        }
30526         
30527         this._verticalLayoutItems( items , isInstant );
30528         
30529     },
30530     
30531     _verticalLayoutItems : function ( items , isInstant)
30532     {
30533         if ( !items || !items.length ) {
30534             return;
30535         }
30536         
30537         var standard = [
30538             ['xs', 'xs', 'xs', 'tall'],
30539             ['xs', 'xs', 'tall'],
30540             ['xs', 'xs', 'sm'],
30541             ['xs', 'xs', 'xs'],
30542             ['xs', 'tall'],
30543             ['xs', 'sm'],
30544             ['xs', 'xs'],
30545             ['xs'],
30546             
30547             ['sm', 'xs', 'xs'],
30548             ['sm', 'xs'],
30549             ['sm'],
30550             
30551             ['tall', 'xs', 'xs', 'xs'],
30552             ['tall', 'xs', 'xs'],
30553             ['tall', 'xs'],
30554             ['tall']
30555             
30556         ];
30557         
30558         var queue = [];
30559         
30560         var boxes = [];
30561         
30562         var box = [];
30563         
30564         Roo.each(items, function(item, k){
30565             
30566             switch (item.size) {
30567                 // these layouts take up a full box,
30568                 case 'md' :
30569                 case 'md-left' :
30570                 case 'md-right' :
30571                 case 'wide' :
30572                     
30573                     if(box.length){
30574                         boxes.push(box);
30575                         box = [];
30576                     }
30577                     
30578                     boxes.push([item]);
30579                     
30580                     break;
30581                     
30582                 case 'xs' :
30583                 case 'sm' :
30584                 case 'tall' :
30585                     
30586                     box.push(item);
30587                     
30588                     break;
30589                 default :
30590                     break;
30591                     
30592             }
30593             
30594         }, this);
30595         
30596         if(box.length){
30597             boxes.push(box);
30598             box = [];
30599         }
30600         
30601         var filterPattern = function(box, length)
30602         {
30603             if(!box.length){
30604                 return;
30605             }
30606             
30607             var match = false;
30608             
30609             var pattern = box.slice(0, length);
30610             
30611             var format = [];
30612             
30613             Roo.each(pattern, function(i){
30614                 format.push(i.size);
30615             }, this);
30616             
30617             Roo.each(standard, function(s){
30618                 
30619                 if(String(s) != String(format)){
30620                     return;
30621                 }
30622                 
30623                 match = true;
30624                 return false;
30625                 
30626             }, this);
30627             
30628             if(!match && length == 1){
30629                 return;
30630             }
30631             
30632             if(!match){
30633                 filterPattern(box, length - 1);
30634                 return;
30635             }
30636                 
30637             queue.push(pattern);
30638
30639             box = box.slice(length, box.length);
30640
30641             filterPattern(box, 4);
30642
30643             return;
30644             
30645         }
30646         
30647         Roo.each(boxes, function(box, k){
30648             
30649             if(!box.length){
30650                 return;
30651             }
30652             
30653             if(box.length == 1){
30654                 queue.push(box);
30655                 return;
30656             }
30657             
30658             filterPattern(box, 4);
30659             
30660         }, this);
30661         
30662         this._processVerticalLayoutQueue( queue, isInstant );
30663         
30664     },
30665     
30666 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30667 //    {
30668 //        if ( !items || !items.length ) {
30669 //            return;
30670 //        }
30671 //
30672 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30673 //        
30674 //    },
30675     
30676     _horizontalLayoutItems : function ( items , isInstant)
30677     {
30678         if ( !items || !items.length || items.length < 3) {
30679             return;
30680         }
30681         
30682         items.reverse();
30683         
30684         var eItems = items.slice(0, 3);
30685         
30686         items = items.slice(3, items.length);
30687         
30688         var standard = [
30689             ['xs', 'xs', 'xs', 'wide'],
30690             ['xs', 'xs', 'wide'],
30691             ['xs', 'xs', 'sm'],
30692             ['xs', 'xs', 'xs'],
30693             ['xs', 'wide'],
30694             ['xs', 'sm'],
30695             ['xs', 'xs'],
30696             ['xs'],
30697             
30698             ['sm', 'xs', 'xs'],
30699             ['sm', 'xs'],
30700             ['sm'],
30701             
30702             ['wide', 'xs', 'xs', 'xs'],
30703             ['wide', 'xs', 'xs'],
30704             ['wide', 'xs'],
30705             ['wide'],
30706             
30707             ['wide-thin']
30708         ];
30709         
30710         var queue = [];
30711         
30712         var boxes = [];
30713         
30714         var box = [];
30715         
30716         Roo.each(items, function(item, k){
30717             
30718             switch (item.size) {
30719                 case 'md' :
30720                 case 'md-left' :
30721                 case 'md-right' :
30722                 case 'tall' :
30723                     
30724                     if(box.length){
30725                         boxes.push(box);
30726                         box = [];
30727                     }
30728                     
30729                     boxes.push([item]);
30730                     
30731                     break;
30732                     
30733                 case 'xs' :
30734                 case 'sm' :
30735                 case 'wide' :
30736                 case 'wide-thin' :
30737                     
30738                     box.push(item);
30739                     
30740                     break;
30741                 default :
30742                     break;
30743                     
30744             }
30745             
30746         }, this);
30747         
30748         if(box.length){
30749             boxes.push(box);
30750             box = [];
30751         }
30752         
30753         var filterPattern = function(box, length)
30754         {
30755             if(!box.length){
30756                 return;
30757             }
30758             
30759             var match = false;
30760             
30761             var pattern = box.slice(0, length);
30762             
30763             var format = [];
30764             
30765             Roo.each(pattern, function(i){
30766                 format.push(i.size);
30767             }, this);
30768             
30769             Roo.each(standard, function(s){
30770                 
30771                 if(String(s) != String(format)){
30772                     return;
30773                 }
30774                 
30775                 match = true;
30776                 return false;
30777                 
30778             }, this);
30779             
30780             if(!match && length == 1){
30781                 return;
30782             }
30783             
30784             if(!match){
30785                 filterPattern(box, length - 1);
30786                 return;
30787             }
30788                 
30789             queue.push(pattern);
30790
30791             box = box.slice(length, box.length);
30792
30793             filterPattern(box, 4);
30794
30795             return;
30796             
30797         }
30798         
30799         Roo.each(boxes, function(box, k){
30800             
30801             if(!box.length){
30802                 return;
30803             }
30804             
30805             if(box.length == 1){
30806                 queue.push(box);
30807                 return;
30808             }
30809             
30810             filterPattern(box, 4);
30811             
30812         }, this);
30813         
30814         
30815         var prune = [];
30816         
30817         var pos = this.el.getBox(true);
30818         
30819         var minX = pos.x;
30820         
30821         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30822         
30823         var hit_end = false;
30824         
30825         Roo.each(queue, function(box){
30826             
30827             if(hit_end){
30828                 
30829                 Roo.each(box, function(b){
30830                 
30831                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30832                     b.el.hide();
30833
30834                 }, this);
30835
30836                 return;
30837             }
30838             
30839             var mx = 0;
30840             
30841             Roo.each(box, function(b){
30842                 
30843                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30844                 b.el.show();
30845
30846                 mx = Math.max(mx, b.x);
30847                 
30848             }, this);
30849             
30850             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30851             
30852             if(maxX < minX){
30853                 
30854                 Roo.each(box, function(b){
30855                 
30856                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30857                     b.el.hide();
30858                     
30859                 }, this);
30860                 
30861                 hit_end = true;
30862                 
30863                 return;
30864             }
30865             
30866             prune.push(box);
30867             
30868         }, this);
30869         
30870         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30871     },
30872     
30873     /** Sets position of item in DOM
30874     * @param {Element} item
30875     * @param {Number} x - horizontal position
30876     * @param {Number} y - vertical position
30877     * @param {Boolean} isInstant - disables transitions
30878     */
30879     _processVerticalLayoutQueue : function( queue, isInstant )
30880     {
30881         var pos = this.el.getBox(true);
30882         var x = pos.x;
30883         var y = pos.y;
30884         var maxY = [];
30885         
30886         for (var i = 0; i < this.cols; i++){
30887             maxY[i] = pos.y;
30888         }
30889         
30890         Roo.each(queue, function(box, k){
30891             
30892             var col = k % this.cols;
30893             
30894             Roo.each(box, function(b,kk){
30895                 
30896                 b.el.position('absolute');
30897                 
30898                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30899                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30900                 
30901                 if(b.size == 'md-left' || b.size == 'md-right'){
30902                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30903                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30904                 }
30905                 
30906                 b.el.setWidth(width);
30907                 b.el.setHeight(height);
30908                 // iframe?
30909                 b.el.select('iframe',true).setSize(width,height);
30910                 
30911             }, this);
30912             
30913             for (var i = 0; i < this.cols; i++){
30914                 
30915                 if(maxY[i] < maxY[col]){
30916                     col = i;
30917                     continue;
30918                 }
30919                 
30920                 col = Math.min(col, i);
30921                 
30922             }
30923             
30924             x = pos.x + col * (this.colWidth + this.padWidth);
30925             
30926             y = maxY[col];
30927             
30928             var positions = [];
30929             
30930             switch (box.length){
30931                 case 1 :
30932                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30933                     break;
30934                 case 2 :
30935                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30936                     break;
30937                 case 3 :
30938                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30939                     break;
30940                 case 4 :
30941                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30942                     break;
30943                 default :
30944                     break;
30945             }
30946             
30947             Roo.each(box, function(b,kk){
30948                 
30949                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30950                 
30951                 var sz = b.el.getSize();
30952                 
30953                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30954                 
30955             }, this);
30956             
30957         }, this);
30958         
30959         var mY = 0;
30960         
30961         for (var i = 0; i < this.cols; i++){
30962             mY = Math.max(mY, maxY[i]);
30963         }
30964         
30965         this.el.setHeight(mY - pos.y);
30966         
30967     },
30968     
30969 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30970 //    {
30971 //        var pos = this.el.getBox(true);
30972 //        var x = pos.x;
30973 //        var y = pos.y;
30974 //        var maxX = pos.right;
30975 //        
30976 //        var maxHeight = 0;
30977 //        
30978 //        Roo.each(items, function(item, k){
30979 //            
30980 //            var c = k % 2;
30981 //            
30982 //            item.el.position('absolute');
30983 //                
30984 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30985 //
30986 //            item.el.setWidth(width);
30987 //
30988 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30989 //
30990 //            item.el.setHeight(height);
30991 //            
30992 //            if(c == 0){
30993 //                item.el.setXY([x, y], isInstant ? false : true);
30994 //            } else {
30995 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30996 //            }
30997 //            
30998 //            y = y + height + this.alternativePadWidth;
30999 //            
31000 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31001 //            
31002 //        }, this);
31003 //        
31004 //        this.el.setHeight(maxHeight);
31005 //        
31006 //    },
31007     
31008     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31009     {
31010         var pos = this.el.getBox(true);
31011         
31012         var minX = pos.x;
31013         var minY = pos.y;
31014         
31015         var maxX = pos.right;
31016         
31017         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31018         
31019         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31020         
31021         Roo.each(queue, function(box, k){
31022             
31023             Roo.each(box, function(b, kk){
31024                 
31025                 b.el.position('absolute');
31026                 
31027                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31028                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31029                 
31030                 if(b.size == 'md-left' || b.size == 'md-right'){
31031                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31032                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31033                 }
31034                 
31035                 b.el.setWidth(width);
31036                 b.el.setHeight(height);
31037                 
31038             }, this);
31039             
31040             if(!box.length){
31041                 return;
31042             }
31043             
31044             var positions = [];
31045             
31046             switch (box.length){
31047                 case 1 :
31048                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31049                     break;
31050                 case 2 :
31051                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31052                     break;
31053                 case 3 :
31054                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31055                     break;
31056                 case 4 :
31057                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31058                     break;
31059                 default :
31060                     break;
31061             }
31062             
31063             Roo.each(box, function(b,kk){
31064                 
31065                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31066                 
31067                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31068                 
31069             }, this);
31070             
31071         }, this);
31072         
31073     },
31074     
31075     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31076     {
31077         Roo.each(eItems, function(b,k){
31078             
31079             b.size = (k == 0) ? 'sm' : 'xs';
31080             b.x = (k == 0) ? 2 : 1;
31081             b.y = (k == 0) ? 2 : 1;
31082             
31083             b.el.position('absolute');
31084             
31085             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31086                 
31087             b.el.setWidth(width);
31088             
31089             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31090             
31091             b.el.setHeight(height);
31092             
31093         }, this);
31094
31095         var positions = [];
31096         
31097         positions.push({
31098             x : maxX - this.unitWidth * 2 - this.gutter,
31099             y : minY
31100         });
31101         
31102         positions.push({
31103             x : maxX - this.unitWidth,
31104             y : minY + (this.unitWidth + this.gutter) * 2
31105         });
31106         
31107         positions.push({
31108             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31109             y : minY
31110         });
31111         
31112         Roo.each(eItems, function(b,k){
31113             
31114             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31115
31116         }, this);
31117         
31118     },
31119     
31120     getVerticalOneBoxColPositions : function(x, y, box)
31121     {
31122         var pos = [];
31123         
31124         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31125         
31126         if(box[0].size == 'md-left'){
31127             rand = 0;
31128         }
31129         
31130         if(box[0].size == 'md-right'){
31131             rand = 1;
31132         }
31133         
31134         pos.push({
31135             x : x + (this.unitWidth + this.gutter) * rand,
31136             y : y
31137         });
31138         
31139         return pos;
31140     },
31141     
31142     getVerticalTwoBoxColPositions : function(x, y, box)
31143     {
31144         var pos = [];
31145         
31146         if(box[0].size == 'xs'){
31147             
31148             pos.push({
31149                 x : x,
31150                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31151             });
31152
31153             pos.push({
31154                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31155                 y : y
31156             });
31157             
31158             return pos;
31159             
31160         }
31161         
31162         pos.push({
31163             x : x,
31164             y : y
31165         });
31166
31167         pos.push({
31168             x : x + (this.unitWidth + this.gutter) * 2,
31169             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31170         });
31171         
31172         return pos;
31173         
31174     },
31175     
31176     getVerticalThreeBoxColPositions : function(x, y, box)
31177     {
31178         var pos = [];
31179         
31180         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31181             
31182             pos.push({
31183                 x : x,
31184                 y : y
31185             });
31186
31187             pos.push({
31188                 x : x + (this.unitWidth + this.gutter) * 1,
31189                 y : y
31190             });
31191             
31192             pos.push({
31193                 x : x + (this.unitWidth + this.gutter) * 2,
31194                 y : y
31195             });
31196             
31197             return pos;
31198             
31199         }
31200         
31201         if(box[0].size == 'xs' && box[1].size == 'xs'){
31202             
31203             pos.push({
31204                 x : x,
31205                 y : y
31206             });
31207
31208             pos.push({
31209                 x : x,
31210                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31211             });
31212             
31213             pos.push({
31214                 x : x + (this.unitWidth + this.gutter) * 1,
31215                 y : y
31216             });
31217             
31218             return pos;
31219             
31220         }
31221         
31222         pos.push({
31223             x : x,
31224             y : y
31225         });
31226
31227         pos.push({
31228             x : x + (this.unitWidth + this.gutter) * 2,
31229             y : y
31230         });
31231
31232         pos.push({
31233             x : x + (this.unitWidth + this.gutter) * 2,
31234             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31235         });
31236             
31237         return pos;
31238         
31239     },
31240     
31241     getVerticalFourBoxColPositions : function(x, y, box)
31242     {
31243         var pos = [];
31244         
31245         if(box[0].size == 'xs'){
31246             
31247             pos.push({
31248                 x : x,
31249                 y : y
31250             });
31251
31252             pos.push({
31253                 x : x,
31254                 y : y + (this.unitHeight + this.gutter) * 1
31255             });
31256             
31257             pos.push({
31258                 x : x,
31259                 y : y + (this.unitHeight + this.gutter) * 2
31260             });
31261             
31262             pos.push({
31263                 x : x + (this.unitWidth + this.gutter) * 1,
31264                 y : y
31265             });
31266             
31267             return pos;
31268             
31269         }
31270         
31271         pos.push({
31272             x : x,
31273             y : y
31274         });
31275
31276         pos.push({
31277             x : x + (this.unitWidth + this.gutter) * 2,
31278             y : y
31279         });
31280
31281         pos.push({
31282             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31283             y : y + (this.unitHeight + this.gutter) * 1
31284         });
31285
31286         pos.push({
31287             x : x + (this.unitWidth + this.gutter) * 2,
31288             y : y + (this.unitWidth + this.gutter) * 2
31289         });
31290
31291         return pos;
31292         
31293     },
31294     
31295     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31296     {
31297         var pos = [];
31298         
31299         if(box[0].size == 'md-left'){
31300             pos.push({
31301                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31302                 y : minY
31303             });
31304             
31305             return pos;
31306         }
31307         
31308         if(box[0].size == 'md-right'){
31309             pos.push({
31310                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31311                 y : minY + (this.unitWidth + this.gutter) * 1
31312             });
31313             
31314             return pos;
31315         }
31316         
31317         var rand = Math.floor(Math.random() * (4 - box[0].y));
31318         
31319         pos.push({
31320             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31321             y : minY + (this.unitWidth + this.gutter) * rand
31322         });
31323         
31324         return pos;
31325         
31326     },
31327     
31328     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31329     {
31330         var pos = [];
31331         
31332         if(box[0].size == 'xs'){
31333             
31334             pos.push({
31335                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31336                 y : minY
31337             });
31338
31339             pos.push({
31340                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31341                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31342             });
31343             
31344             return pos;
31345             
31346         }
31347         
31348         pos.push({
31349             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31350             y : minY
31351         });
31352
31353         pos.push({
31354             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31355             y : minY + (this.unitWidth + this.gutter) * 2
31356         });
31357         
31358         return pos;
31359         
31360     },
31361     
31362     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31363     {
31364         var pos = [];
31365         
31366         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31367             
31368             pos.push({
31369                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31370                 y : minY
31371             });
31372
31373             pos.push({
31374                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31375                 y : minY + (this.unitWidth + this.gutter) * 1
31376             });
31377             
31378             pos.push({
31379                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31380                 y : minY + (this.unitWidth + this.gutter) * 2
31381             });
31382             
31383             return pos;
31384             
31385         }
31386         
31387         if(box[0].size == 'xs' && box[1].size == 'xs'){
31388             
31389             pos.push({
31390                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31391                 y : minY
31392             });
31393
31394             pos.push({
31395                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31396                 y : minY
31397             });
31398             
31399             pos.push({
31400                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31401                 y : minY + (this.unitWidth + this.gutter) * 1
31402             });
31403             
31404             return pos;
31405             
31406         }
31407         
31408         pos.push({
31409             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31410             y : minY
31411         });
31412
31413         pos.push({
31414             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31415             y : minY + (this.unitWidth + this.gutter) * 2
31416         });
31417
31418         pos.push({
31419             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31420             y : minY + (this.unitWidth + this.gutter) * 2
31421         });
31422             
31423         return pos;
31424         
31425     },
31426     
31427     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31428     {
31429         var pos = [];
31430         
31431         if(box[0].size == 'xs'){
31432             
31433             pos.push({
31434                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31435                 y : minY
31436             });
31437
31438             pos.push({
31439                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31440                 y : minY
31441             });
31442             
31443             pos.push({
31444                 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),
31445                 y : minY
31446             });
31447             
31448             pos.push({
31449                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31450                 y : minY + (this.unitWidth + this.gutter) * 1
31451             });
31452             
31453             return pos;
31454             
31455         }
31456         
31457         pos.push({
31458             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31459             y : minY
31460         });
31461         
31462         pos.push({
31463             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31464             y : minY + (this.unitWidth + this.gutter) * 2
31465         });
31466         
31467         pos.push({
31468             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31469             y : minY + (this.unitWidth + this.gutter) * 2
31470         });
31471         
31472         pos.push({
31473             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),
31474             y : minY + (this.unitWidth + this.gutter) * 2
31475         });
31476
31477         return pos;
31478         
31479     },
31480     
31481     /**
31482     * remove a Masonry Brick
31483     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31484     */
31485     removeBrick : function(brick_id)
31486     {
31487         if (!brick_id) {
31488             return;
31489         }
31490         
31491         for (var i = 0; i<this.bricks.length; i++) {
31492             if (this.bricks[i].id == brick_id) {
31493                 this.bricks.splice(i,1);
31494                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31495                 this.initial();
31496             }
31497         }
31498     },
31499     
31500     /**
31501     * adds a Masonry Brick
31502     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31503     */
31504     addBrick : function(cfg)
31505     {
31506         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31507         //this.register(cn);
31508         cn.parentId = this.id;
31509         cn.onRender(this.el, null);
31510         return cn;
31511     },
31512     
31513     /**
31514     * register a Masonry Brick
31515     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31516     */
31517     
31518     register : function(brick)
31519     {
31520         this.bricks.push(brick);
31521         brick.masonryId = this.id;
31522     },
31523     
31524     /**
31525     * clear all the Masonry Brick
31526     */
31527     clearAll : function()
31528     {
31529         this.bricks = [];
31530         //this.getChildContainer().dom.innerHTML = "";
31531         this.el.dom.innerHTML = '';
31532     },
31533     
31534     getSelected : function()
31535     {
31536         if (!this.selectedBrick) {
31537             return false;
31538         }
31539         
31540         return this.selectedBrick;
31541     }
31542 });
31543
31544 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31545     
31546     groups: {},
31547      /**
31548     * register a Masonry Layout
31549     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31550     */
31551     
31552     register : function(layout)
31553     {
31554         this.groups[layout.id] = layout;
31555     },
31556     /**
31557     * fetch a  Masonry Layout based on the masonry layout ID
31558     * @param {string} the masonry layout to add
31559     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31560     */
31561     
31562     get: function(layout_id) {
31563         if (typeof(this.groups[layout_id]) == 'undefined') {
31564             return false;
31565         }
31566         return this.groups[layout_id] ;
31567     }
31568     
31569     
31570     
31571 });
31572
31573  
31574
31575  /**
31576  *
31577  * This is based on 
31578  * http://masonry.desandro.com
31579  *
31580  * The idea is to render all the bricks based on vertical width...
31581  *
31582  * The original code extends 'outlayer' - we might need to use that....
31583  * 
31584  */
31585
31586
31587 /**
31588  * @class Roo.bootstrap.LayoutMasonryAuto
31589  * @extends Roo.bootstrap.Component
31590  * Bootstrap Layout Masonry class
31591  * 
31592  * @constructor
31593  * Create a new Element
31594  * @param {Object} config The config object
31595  */
31596
31597 Roo.bootstrap.LayoutMasonryAuto = function(config){
31598     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31599 };
31600
31601 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31602     
31603       /**
31604      * @cfg {Boolean} isFitWidth  - resize the width..
31605      */   
31606     isFitWidth : false,  // options..
31607     /**
31608      * @cfg {Boolean} isOriginLeft = left align?
31609      */   
31610     isOriginLeft : true,
31611     /**
31612      * @cfg {Boolean} isOriginTop = top align?
31613      */   
31614     isOriginTop : false,
31615     /**
31616      * @cfg {Boolean} isLayoutInstant = no animation?
31617      */   
31618     isLayoutInstant : false, // needed?
31619     /**
31620      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31621      */   
31622     isResizingContainer : true,
31623     /**
31624      * @cfg {Number} columnWidth  width of the columns 
31625      */   
31626     
31627     columnWidth : 0,
31628     
31629     /**
31630      * @cfg {Number} maxCols maximum number of columns
31631      */   
31632     
31633     maxCols: 0,
31634     /**
31635      * @cfg {Number} padHeight padding below box..
31636      */   
31637     
31638     padHeight : 10, 
31639     
31640     /**
31641      * @cfg {Boolean} isAutoInitial defalut true
31642      */   
31643     
31644     isAutoInitial : true, 
31645     
31646     // private?
31647     gutter : 0,
31648     
31649     containerWidth: 0,
31650     initialColumnWidth : 0,
31651     currentSize : null,
31652     
31653     colYs : null, // array.
31654     maxY : 0,
31655     padWidth: 10,
31656     
31657     
31658     tag: 'div',
31659     cls: '',
31660     bricks: null, //CompositeElement
31661     cols : 0, // array?
31662     // element : null, // wrapped now this.el
31663     _isLayoutInited : null, 
31664     
31665     
31666     getAutoCreate : function(){
31667         
31668         var cfg = {
31669             tag: this.tag,
31670             cls: 'blog-masonary-wrapper ' + this.cls,
31671             cn : {
31672                 cls : 'mas-boxes masonary'
31673             }
31674         };
31675         
31676         return cfg;
31677     },
31678     
31679     getChildContainer: function( )
31680     {
31681         if (this.boxesEl) {
31682             return this.boxesEl;
31683         }
31684         
31685         this.boxesEl = this.el.select('.mas-boxes').first();
31686         
31687         return this.boxesEl;
31688     },
31689     
31690     
31691     initEvents : function()
31692     {
31693         var _this = this;
31694         
31695         if(this.isAutoInitial){
31696             Roo.log('hook children rendered');
31697             this.on('childrenrendered', function() {
31698                 Roo.log('children rendered');
31699                 _this.initial();
31700             } ,this);
31701         }
31702         
31703     },
31704     
31705     initial : function()
31706     {
31707         this.reloadItems();
31708
31709         this.currentSize = this.el.getBox(true);
31710
31711         /// was window resize... - let's see if this works..
31712         Roo.EventManager.onWindowResize(this.resize, this); 
31713
31714         if(!this.isAutoInitial){
31715             this.layout();
31716             return;
31717         }
31718         
31719         this.layout.defer(500,this);
31720     },
31721     
31722     reloadItems: function()
31723     {
31724         this.bricks = this.el.select('.masonry-brick', true);
31725         
31726         this.bricks.each(function(b) {
31727             //Roo.log(b.getSize());
31728             if (!b.attr('originalwidth')) {
31729                 b.attr('originalwidth',  b.getSize().width);
31730             }
31731             
31732         });
31733         
31734         Roo.log(this.bricks.elements.length);
31735     },
31736     
31737     resize : function()
31738     {
31739         Roo.log('resize');
31740         var cs = this.el.getBox(true);
31741         
31742         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31743             Roo.log("no change in with or X");
31744             return;
31745         }
31746         this.currentSize = cs;
31747         this.layout();
31748     },
31749     
31750     layout : function()
31751     {
31752          Roo.log('layout');
31753         this._resetLayout();
31754         //this._manageStamps();
31755       
31756         // don't animate first layout
31757         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31758         this.layoutItems( isInstant );
31759       
31760         // flag for initalized
31761         this._isLayoutInited = true;
31762     },
31763     
31764     layoutItems : function( isInstant )
31765     {
31766         //var items = this._getItemsForLayout( this.items );
31767         // original code supports filtering layout items.. we just ignore it..
31768         
31769         this._layoutItems( this.bricks , isInstant );
31770       
31771         this._postLayout();
31772     },
31773     _layoutItems : function ( items , isInstant)
31774     {
31775        //this.fireEvent( 'layout', this, items );
31776     
31777
31778         if ( !items || !items.elements.length ) {
31779           // no items, emit event with empty array
31780             return;
31781         }
31782
31783         var queue = [];
31784         items.each(function(item) {
31785             Roo.log("layout item");
31786             Roo.log(item);
31787             // get x/y object from method
31788             var position = this._getItemLayoutPosition( item );
31789             // enqueue
31790             position.item = item;
31791             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31792             queue.push( position );
31793         }, this);
31794       
31795         this._processLayoutQueue( queue );
31796     },
31797     /** Sets position of item in DOM
31798     * @param {Element} item
31799     * @param {Number} x - horizontal position
31800     * @param {Number} y - vertical position
31801     * @param {Boolean} isInstant - disables transitions
31802     */
31803     _processLayoutQueue : function( queue )
31804     {
31805         for ( var i=0, len = queue.length; i < len; i++ ) {
31806             var obj = queue[i];
31807             obj.item.position('absolute');
31808             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31809         }
31810     },
31811       
31812     
31813     /**
31814     * Any logic you want to do after each layout,
31815     * i.e. size the container
31816     */
31817     _postLayout : function()
31818     {
31819         this.resizeContainer();
31820     },
31821     
31822     resizeContainer : function()
31823     {
31824         if ( !this.isResizingContainer ) {
31825             return;
31826         }
31827         var size = this._getContainerSize();
31828         if ( size ) {
31829             this.el.setSize(size.width,size.height);
31830             this.boxesEl.setSize(size.width,size.height);
31831         }
31832     },
31833     
31834     
31835     
31836     _resetLayout : function()
31837     {
31838         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31839         this.colWidth = this.el.getWidth();
31840         //this.gutter = this.el.getWidth(); 
31841         
31842         this.measureColumns();
31843
31844         // reset column Y
31845         var i = this.cols;
31846         this.colYs = [];
31847         while (i--) {
31848             this.colYs.push( 0 );
31849         }
31850     
31851         this.maxY = 0;
31852     },
31853
31854     measureColumns : function()
31855     {
31856         this.getContainerWidth();
31857       // if columnWidth is 0, default to outerWidth of first item
31858         if ( !this.columnWidth ) {
31859             var firstItem = this.bricks.first();
31860             Roo.log(firstItem);
31861             this.columnWidth  = this.containerWidth;
31862             if (firstItem && firstItem.attr('originalwidth') ) {
31863                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31864             }
31865             // columnWidth fall back to item of first element
31866             Roo.log("set column width?");
31867                         this.initialColumnWidth = this.columnWidth  ;
31868
31869             // if first elem has no width, default to size of container
31870             
31871         }
31872         
31873         
31874         if (this.initialColumnWidth) {
31875             this.columnWidth = this.initialColumnWidth;
31876         }
31877         
31878         
31879             
31880         // column width is fixed at the top - however if container width get's smaller we should
31881         // reduce it...
31882         
31883         // this bit calcs how man columns..
31884             
31885         var columnWidth = this.columnWidth += this.gutter;
31886       
31887         // calculate columns
31888         var containerWidth = this.containerWidth + this.gutter;
31889         
31890         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31891         // fix rounding errors, typically with gutters
31892         var excess = columnWidth - containerWidth % columnWidth;
31893         
31894         
31895         // if overshoot is less than a pixel, round up, otherwise floor it
31896         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31897         cols = Math[ mathMethod ]( cols );
31898         this.cols = Math.max( cols, 1 );
31899         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31900         
31901          // padding positioning..
31902         var totalColWidth = this.cols * this.columnWidth;
31903         var padavail = this.containerWidth - totalColWidth;
31904         // so for 2 columns - we need 3 'pads'
31905         
31906         var padNeeded = (1+this.cols) * this.padWidth;
31907         
31908         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31909         
31910         this.columnWidth += padExtra
31911         //this.padWidth = Math.floor(padavail /  ( this.cols));
31912         
31913         // adjust colum width so that padding is fixed??
31914         
31915         // we have 3 columns ... total = width * 3
31916         // we have X left over... that should be used by 
31917         
31918         //if (this.expandC) {
31919             
31920         //}
31921         
31922         
31923         
31924     },
31925     
31926     getContainerWidth : function()
31927     {
31928        /* // container is parent if fit width
31929         var container = this.isFitWidth ? this.element.parentNode : this.element;
31930         // check that this.size and size are there
31931         // IE8 triggers resize on body size change, so they might not be
31932         
31933         var size = getSize( container );  //FIXME
31934         this.containerWidth = size && size.innerWidth; //FIXME
31935         */
31936          
31937         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31938         
31939     },
31940     
31941     _getItemLayoutPosition : function( item )  // what is item?
31942     {
31943         // we resize the item to our columnWidth..
31944       
31945         item.setWidth(this.columnWidth);
31946         item.autoBoxAdjust  = false;
31947         
31948         var sz = item.getSize();
31949  
31950         // how many columns does this brick span
31951         var remainder = this.containerWidth % this.columnWidth;
31952         
31953         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31954         // round if off by 1 pixel, otherwise use ceil
31955         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31956         colSpan = Math.min( colSpan, this.cols );
31957         
31958         // normally this should be '1' as we dont' currently allow multi width columns..
31959         
31960         var colGroup = this._getColGroup( colSpan );
31961         // get the minimum Y value from the columns
31962         var minimumY = Math.min.apply( Math, colGroup );
31963         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31964         
31965         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31966          
31967         // position the brick
31968         var position = {
31969             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31970             y: this.currentSize.y + minimumY + this.padHeight
31971         };
31972         
31973         Roo.log(position);
31974         // apply setHeight to necessary columns
31975         var setHeight = minimumY + sz.height + this.padHeight;
31976         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31977         
31978         var setSpan = this.cols + 1 - colGroup.length;
31979         for ( var i = 0; i < setSpan; i++ ) {
31980           this.colYs[ shortColIndex + i ] = setHeight ;
31981         }
31982       
31983         return position;
31984     },
31985     
31986     /**
31987      * @param {Number} colSpan - number of columns the element spans
31988      * @returns {Array} colGroup
31989      */
31990     _getColGroup : function( colSpan )
31991     {
31992         if ( colSpan < 2 ) {
31993           // if brick spans only one column, use all the column Ys
31994           return this.colYs;
31995         }
31996       
31997         var colGroup = [];
31998         // how many different places could this brick fit horizontally
31999         var groupCount = this.cols + 1 - colSpan;
32000         // for each group potential horizontal position
32001         for ( var i = 0; i < groupCount; i++ ) {
32002           // make an array of colY values for that one group
32003           var groupColYs = this.colYs.slice( i, i + colSpan );
32004           // and get the max value of the array
32005           colGroup[i] = Math.max.apply( Math, groupColYs );
32006         }
32007         return colGroup;
32008     },
32009     /*
32010     _manageStamp : function( stamp )
32011     {
32012         var stampSize =  stamp.getSize();
32013         var offset = stamp.getBox();
32014         // get the columns that this stamp affects
32015         var firstX = this.isOriginLeft ? offset.x : offset.right;
32016         var lastX = firstX + stampSize.width;
32017         var firstCol = Math.floor( firstX / this.columnWidth );
32018         firstCol = Math.max( 0, firstCol );
32019         
32020         var lastCol = Math.floor( lastX / this.columnWidth );
32021         // lastCol should not go over if multiple of columnWidth #425
32022         lastCol -= lastX % this.columnWidth ? 0 : 1;
32023         lastCol = Math.min( this.cols - 1, lastCol );
32024         
32025         // set colYs to bottom of the stamp
32026         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32027             stampSize.height;
32028             
32029         for ( var i = firstCol; i <= lastCol; i++ ) {
32030           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32031         }
32032     },
32033     */
32034     
32035     _getContainerSize : function()
32036     {
32037         this.maxY = Math.max.apply( Math, this.colYs );
32038         var size = {
32039             height: this.maxY
32040         };
32041       
32042         if ( this.isFitWidth ) {
32043             size.width = this._getContainerFitWidth();
32044         }
32045       
32046         return size;
32047     },
32048     
32049     _getContainerFitWidth : function()
32050     {
32051         var unusedCols = 0;
32052         // count unused columns
32053         var i = this.cols;
32054         while ( --i ) {
32055           if ( this.colYs[i] !== 0 ) {
32056             break;
32057           }
32058           unusedCols++;
32059         }
32060         // fit container to columns that have been used
32061         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32062     },
32063     
32064     needsResizeLayout : function()
32065     {
32066         var previousWidth = this.containerWidth;
32067         this.getContainerWidth();
32068         return previousWidth !== this.containerWidth;
32069     }
32070  
32071 });
32072
32073  
32074
32075  /*
32076  * - LGPL
32077  *
32078  * element
32079  * 
32080  */
32081
32082 /**
32083  * @class Roo.bootstrap.MasonryBrick
32084  * @extends Roo.bootstrap.Component
32085  * Bootstrap MasonryBrick class
32086  * 
32087  * @constructor
32088  * Create a new MasonryBrick
32089  * @param {Object} config The config object
32090  */
32091
32092 Roo.bootstrap.MasonryBrick = function(config){
32093     
32094     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32095     
32096     Roo.bootstrap.MasonryBrick.register(this);
32097     
32098     this.addEvents({
32099         // raw events
32100         /**
32101          * @event click
32102          * When a MasonryBrick is clcik
32103          * @param {Roo.bootstrap.MasonryBrick} this
32104          * @param {Roo.EventObject} e
32105          */
32106         "click" : true
32107     });
32108 };
32109
32110 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32111     
32112     /**
32113      * @cfg {String} title
32114      */   
32115     title : '',
32116     /**
32117      * @cfg {String} html
32118      */   
32119     html : '',
32120     /**
32121      * @cfg {String} bgimage
32122      */   
32123     bgimage : '',
32124     /**
32125      * @cfg {String} videourl
32126      */   
32127     videourl : '',
32128     /**
32129      * @cfg {String} cls
32130      */   
32131     cls : '',
32132     /**
32133      * @cfg {String} href
32134      */   
32135     href : '',
32136     /**
32137      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32138      */   
32139     size : 'xs',
32140     
32141     /**
32142      * @cfg {String} placetitle (center|bottom)
32143      */   
32144     placetitle : '',
32145     
32146     /**
32147      * @cfg {Boolean} isFitContainer defalut true
32148      */   
32149     isFitContainer : true, 
32150     
32151     /**
32152      * @cfg {Boolean} preventDefault defalut false
32153      */   
32154     preventDefault : false, 
32155     
32156     /**
32157      * @cfg {Boolean} inverse defalut false
32158      */   
32159     maskInverse : false, 
32160     
32161     getAutoCreate : function()
32162     {
32163         if(!this.isFitContainer){
32164             return this.getSplitAutoCreate();
32165         }
32166         
32167         var cls = 'masonry-brick masonry-brick-full';
32168         
32169         if(this.href.length){
32170             cls += ' masonry-brick-link';
32171         }
32172         
32173         if(this.bgimage.length){
32174             cls += ' masonry-brick-image';
32175         }
32176         
32177         if(this.maskInverse){
32178             cls += ' mask-inverse';
32179         }
32180         
32181         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32182             cls += ' enable-mask';
32183         }
32184         
32185         if(this.size){
32186             cls += ' masonry-' + this.size + '-brick';
32187         }
32188         
32189         if(this.placetitle.length){
32190             
32191             switch (this.placetitle) {
32192                 case 'center' :
32193                     cls += ' masonry-center-title';
32194                     break;
32195                 case 'bottom' :
32196                     cls += ' masonry-bottom-title';
32197                     break;
32198                 default:
32199                     break;
32200             }
32201             
32202         } else {
32203             if(!this.html.length && !this.bgimage.length){
32204                 cls += ' masonry-center-title';
32205             }
32206
32207             if(!this.html.length && this.bgimage.length){
32208                 cls += ' masonry-bottom-title';
32209             }
32210         }
32211         
32212         if(this.cls){
32213             cls += ' ' + this.cls;
32214         }
32215         
32216         var cfg = {
32217             tag: (this.href.length) ? 'a' : 'div',
32218             cls: cls,
32219             cn: [
32220                 {
32221                     tag: 'div',
32222                     cls: 'masonry-brick-mask'
32223                 },
32224                 {
32225                     tag: 'div',
32226                     cls: 'masonry-brick-paragraph',
32227                     cn: []
32228                 }
32229             ]
32230         };
32231         
32232         if(this.href.length){
32233             cfg.href = this.href;
32234         }
32235         
32236         var cn = cfg.cn[1].cn;
32237         
32238         if(this.title.length){
32239             cn.push({
32240                 tag: 'h4',
32241                 cls: 'masonry-brick-title',
32242                 html: this.title
32243             });
32244         }
32245         
32246         if(this.html.length){
32247             cn.push({
32248                 tag: 'p',
32249                 cls: 'masonry-brick-text',
32250                 html: this.html
32251             });
32252         }
32253         
32254         if (!this.title.length && !this.html.length) {
32255             cfg.cn[1].cls += ' hide';
32256         }
32257         
32258         if(this.bgimage.length){
32259             cfg.cn.push({
32260                 tag: 'img',
32261                 cls: 'masonry-brick-image-view',
32262                 src: this.bgimage
32263             });
32264         }
32265         
32266         if(this.videourl.length){
32267             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32268             // youtube support only?
32269             cfg.cn.push({
32270                 tag: 'iframe',
32271                 cls: 'masonry-brick-image-view',
32272                 src: vurl,
32273                 frameborder : 0,
32274                 allowfullscreen : true
32275             });
32276         }
32277         
32278         return cfg;
32279         
32280     },
32281     
32282     getSplitAutoCreate : function()
32283     {
32284         var cls = 'masonry-brick masonry-brick-split';
32285         
32286         if(this.href.length){
32287             cls += ' masonry-brick-link';
32288         }
32289         
32290         if(this.bgimage.length){
32291             cls += ' masonry-brick-image';
32292         }
32293         
32294         if(this.size){
32295             cls += ' masonry-' + this.size + '-brick';
32296         }
32297         
32298         switch (this.placetitle) {
32299             case 'center' :
32300                 cls += ' masonry-center-title';
32301                 break;
32302             case 'bottom' :
32303                 cls += ' masonry-bottom-title';
32304                 break;
32305             default:
32306                 if(!this.bgimage.length){
32307                     cls += ' masonry-center-title';
32308                 }
32309
32310                 if(this.bgimage.length){
32311                     cls += ' masonry-bottom-title';
32312                 }
32313                 break;
32314         }
32315         
32316         if(this.cls){
32317             cls += ' ' + this.cls;
32318         }
32319         
32320         var cfg = {
32321             tag: (this.href.length) ? 'a' : 'div',
32322             cls: cls,
32323             cn: [
32324                 {
32325                     tag: 'div',
32326                     cls: 'masonry-brick-split-head',
32327                     cn: [
32328                         {
32329                             tag: 'div',
32330                             cls: 'masonry-brick-paragraph',
32331                             cn: []
32332                         }
32333                     ]
32334                 },
32335                 {
32336                     tag: 'div',
32337                     cls: 'masonry-brick-split-body',
32338                     cn: []
32339                 }
32340             ]
32341         };
32342         
32343         if(this.href.length){
32344             cfg.href = this.href;
32345         }
32346         
32347         if(this.title.length){
32348             cfg.cn[0].cn[0].cn.push({
32349                 tag: 'h4',
32350                 cls: 'masonry-brick-title',
32351                 html: this.title
32352             });
32353         }
32354         
32355         if(this.html.length){
32356             cfg.cn[1].cn.push({
32357                 tag: 'p',
32358                 cls: 'masonry-brick-text',
32359                 html: this.html
32360             });
32361         }
32362
32363         if(this.bgimage.length){
32364             cfg.cn[0].cn.push({
32365                 tag: 'img',
32366                 cls: 'masonry-brick-image-view',
32367                 src: this.bgimage
32368             });
32369         }
32370         
32371         if(this.videourl.length){
32372             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32373             // youtube support only?
32374             cfg.cn[0].cn.cn.push({
32375                 tag: 'iframe',
32376                 cls: 'masonry-brick-image-view',
32377                 src: vurl,
32378                 frameborder : 0,
32379                 allowfullscreen : true
32380             });
32381         }
32382         
32383         return cfg;
32384     },
32385     
32386     initEvents: function() 
32387     {
32388         switch (this.size) {
32389             case 'xs' :
32390                 this.x = 1;
32391                 this.y = 1;
32392                 break;
32393             case 'sm' :
32394                 this.x = 2;
32395                 this.y = 2;
32396                 break;
32397             case 'md' :
32398             case 'md-left' :
32399             case 'md-right' :
32400                 this.x = 3;
32401                 this.y = 3;
32402                 break;
32403             case 'tall' :
32404                 this.x = 2;
32405                 this.y = 3;
32406                 break;
32407             case 'wide' :
32408                 this.x = 3;
32409                 this.y = 2;
32410                 break;
32411             case 'wide-thin' :
32412                 this.x = 3;
32413                 this.y = 1;
32414                 break;
32415                         
32416             default :
32417                 break;
32418         }
32419         
32420         if(Roo.isTouch){
32421             this.el.on('touchstart', this.onTouchStart, this);
32422             this.el.on('touchmove', this.onTouchMove, this);
32423             this.el.on('touchend', this.onTouchEnd, this);
32424             this.el.on('contextmenu', this.onContextMenu, this);
32425         } else {
32426             this.el.on('mouseenter'  ,this.enter, this);
32427             this.el.on('mouseleave', this.leave, this);
32428             this.el.on('click', this.onClick, this);
32429         }
32430         
32431         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32432             this.parent().bricks.push(this);   
32433         }
32434         
32435     },
32436     
32437     onClick: function(e, el)
32438     {
32439         var time = this.endTimer - this.startTimer;
32440         // Roo.log(e.preventDefault());
32441         if(Roo.isTouch){
32442             if(time > 1000){
32443                 e.preventDefault();
32444                 return;
32445             }
32446         }
32447         
32448         if(!this.preventDefault){
32449             return;
32450         }
32451         
32452         e.preventDefault();
32453         
32454         if (this.activcClass != '') {
32455             this.selectBrick();
32456         }
32457         
32458         this.fireEvent('click', this);
32459     },
32460     
32461     enter: function(e, el)
32462     {
32463         e.preventDefault();
32464         
32465         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32466             return;
32467         }
32468         
32469         if(this.bgimage.length && this.html.length){
32470             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32471         }
32472     },
32473     
32474     leave: function(e, el)
32475     {
32476         e.preventDefault();
32477         
32478         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32479             return;
32480         }
32481         
32482         if(this.bgimage.length && this.html.length){
32483             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32484         }
32485     },
32486     
32487     onTouchStart: function(e, el)
32488     {
32489 //        e.preventDefault();
32490         
32491         this.touchmoved = false;
32492         
32493         if(!this.isFitContainer){
32494             return;
32495         }
32496         
32497         if(!this.bgimage.length || !this.html.length){
32498             return;
32499         }
32500         
32501         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32502         
32503         this.timer = new Date().getTime();
32504         
32505     },
32506     
32507     onTouchMove: function(e, el)
32508     {
32509         this.touchmoved = true;
32510     },
32511     
32512     onContextMenu : function(e,el)
32513     {
32514         e.preventDefault();
32515         e.stopPropagation();
32516         return false;
32517     },
32518     
32519     onTouchEnd: function(e, el)
32520     {
32521 //        e.preventDefault();
32522         
32523         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32524         
32525             this.leave(e,el);
32526             
32527             return;
32528         }
32529         
32530         if(!this.bgimage.length || !this.html.length){
32531             
32532             if(this.href.length){
32533                 window.location.href = this.href;
32534             }
32535             
32536             return;
32537         }
32538         
32539         if(!this.isFitContainer){
32540             return;
32541         }
32542         
32543         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32544         
32545         window.location.href = this.href;
32546     },
32547     
32548     //selection on single brick only
32549     selectBrick : function() {
32550         
32551         if (!this.parentId) {
32552             return;
32553         }
32554         
32555         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32556         var index = m.selectedBrick.indexOf(this.id);
32557         
32558         if ( index > -1) {
32559             m.selectedBrick.splice(index,1);
32560             this.el.removeClass(this.activeClass);
32561             return;
32562         }
32563         
32564         for(var i = 0; i < m.selectedBrick.length; i++) {
32565             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32566             b.el.removeClass(b.activeClass);
32567         }
32568         
32569         m.selectedBrick = [];
32570         
32571         m.selectedBrick.push(this.id);
32572         this.el.addClass(this.activeClass);
32573         return;
32574     }
32575     
32576 });
32577
32578 Roo.apply(Roo.bootstrap.MasonryBrick, {
32579     
32580     //groups: {},
32581     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32582      /**
32583     * register a Masonry Brick
32584     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32585     */
32586     
32587     register : function(brick)
32588     {
32589         //this.groups[brick.id] = brick;
32590         this.groups.add(brick.id, brick);
32591     },
32592     /**
32593     * fetch a  masonry brick based on the masonry brick ID
32594     * @param {string} the masonry brick to add
32595     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32596     */
32597     
32598     get: function(brick_id) 
32599     {
32600         // if (typeof(this.groups[brick_id]) == 'undefined') {
32601         //     return false;
32602         // }
32603         // return this.groups[brick_id] ;
32604         
32605         if(this.groups.key(brick_id)) {
32606             return this.groups.key(brick_id);
32607         }
32608         
32609         return false;
32610     }
32611     
32612     
32613     
32614 });
32615
32616  /*
32617  * - LGPL
32618  *
32619  * element
32620  * 
32621  */
32622
32623 /**
32624  * @class Roo.bootstrap.Brick
32625  * @extends Roo.bootstrap.Component
32626  * Bootstrap Brick class
32627  * 
32628  * @constructor
32629  * Create a new Brick
32630  * @param {Object} config The config object
32631  */
32632
32633 Roo.bootstrap.Brick = function(config){
32634     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32635     
32636     this.addEvents({
32637         // raw events
32638         /**
32639          * @event click
32640          * When a Brick is click
32641          * @param {Roo.bootstrap.Brick} this
32642          * @param {Roo.EventObject} e
32643          */
32644         "click" : true
32645     });
32646 };
32647
32648 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32649     
32650     /**
32651      * @cfg {String} title
32652      */   
32653     title : '',
32654     /**
32655      * @cfg {String} html
32656      */   
32657     html : '',
32658     /**
32659      * @cfg {String} bgimage
32660      */   
32661     bgimage : '',
32662     /**
32663      * @cfg {String} cls
32664      */   
32665     cls : '',
32666     /**
32667      * @cfg {String} href
32668      */   
32669     href : '',
32670     /**
32671      * @cfg {String} video
32672      */   
32673     video : '',
32674     /**
32675      * @cfg {Boolean} square
32676      */   
32677     square : true,
32678     
32679     getAutoCreate : function()
32680     {
32681         var cls = 'roo-brick';
32682         
32683         if(this.href.length){
32684             cls += ' roo-brick-link';
32685         }
32686         
32687         if(this.bgimage.length){
32688             cls += ' roo-brick-image';
32689         }
32690         
32691         if(!this.html.length && !this.bgimage.length){
32692             cls += ' roo-brick-center-title';
32693         }
32694         
32695         if(!this.html.length && this.bgimage.length){
32696             cls += ' roo-brick-bottom-title';
32697         }
32698         
32699         if(this.cls){
32700             cls += ' ' + this.cls;
32701         }
32702         
32703         var cfg = {
32704             tag: (this.href.length) ? 'a' : 'div',
32705             cls: cls,
32706             cn: [
32707                 {
32708                     tag: 'div',
32709                     cls: 'roo-brick-paragraph',
32710                     cn: []
32711                 }
32712             ]
32713         };
32714         
32715         if(this.href.length){
32716             cfg.href = this.href;
32717         }
32718         
32719         var cn = cfg.cn[0].cn;
32720         
32721         if(this.title.length){
32722             cn.push({
32723                 tag: 'h4',
32724                 cls: 'roo-brick-title',
32725                 html: this.title
32726             });
32727         }
32728         
32729         if(this.html.length){
32730             cn.push({
32731                 tag: 'p',
32732                 cls: 'roo-brick-text',
32733                 html: this.html
32734             });
32735         } else {
32736             cn.cls += ' hide';
32737         }
32738         
32739         if(this.bgimage.length){
32740             cfg.cn.push({
32741                 tag: 'img',
32742                 cls: 'roo-brick-image-view',
32743                 src: this.bgimage
32744             });
32745         }
32746         
32747         return cfg;
32748     },
32749     
32750     initEvents: function() 
32751     {
32752         if(this.title.length || this.html.length){
32753             this.el.on('mouseenter'  ,this.enter, this);
32754             this.el.on('mouseleave', this.leave, this);
32755         }
32756         
32757         Roo.EventManager.onWindowResize(this.resize, this); 
32758         
32759         if(this.bgimage.length){
32760             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32761             this.imageEl.on('load', this.onImageLoad, this);
32762             return;
32763         }
32764         
32765         this.resize();
32766     },
32767     
32768     onImageLoad : function()
32769     {
32770         this.resize();
32771     },
32772     
32773     resize : function()
32774     {
32775         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32776         
32777         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32778         
32779         if(this.bgimage.length){
32780             var image = this.el.select('.roo-brick-image-view', true).first();
32781             
32782             image.setWidth(paragraph.getWidth());
32783             
32784             if(this.square){
32785                 image.setHeight(paragraph.getWidth());
32786             }
32787             
32788             this.el.setHeight(image.getHeight());
32789             paragraph.setHeight(image.getHeight());
32790             
32791         }
32792         
32793     },
32794     
32795     enter: function(e, el)
32796     {
32797         e.preventDefault();
32798         
32799         if(this.bgimage.length){
32800             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32801             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32802         }
32803     },
32804     
32805     leave: function(e, el)
32806     {
32807         e.preventDefault();
32808         
32809         if(this.bgimage.length){
32810             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32811             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32812         }
32813     }
32814     
32815 });
32816
32817  
32818
32819  /*
32820  * - LGPL
32821  *
32822  * Input
32823  * 
32824  */
32825
32826 /**
32827  * @class Roo.bootstrap.NumberField
32828  * @extends Roo.bootstrap.Input
32829  * Bootstrap NumberField class
32830  * 
32831  * 
32832  * 
32833  * 
32834  * @constructor
32835  * Create a new NumberField
32836  * @param {Object} config The config object
32837  */
32838
32839 Roo.bootstrap.NumberField = function(config){
32840     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32841 };
32842
32843 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32844     
32845     /**
32846      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32847      */
32848     allowDecimals : true,
32849     /**
32850      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32851      */
32852     decimalSeparator : ".",
32853     /**
32854      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32855      */
32856     decimalPrecision : 2,
32857     /**
32858      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32859      */
32860     allowNegative : true,
32861     /**
32862      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32863      */
32864     minValue : Number.NEGATIVE_INFINITY,
32865     /**
32866      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32867      */
32868     maxValue : Number.MAX_VALUE,
32869     /**
32870      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32871      */
32872     minText : "The minimum value for this field is {0}",
32873     /**
32874      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32875      */
32876     maxText : "The maximum value for this field is {0}",
32877     /**
32878      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32879      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32880      */
32881     nanText : "{0} is not a valid number",
32882     /**
32883      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32884      */
32885     castInt : true,
32886
32887     // private
32888     initEvents : function()
32889     {   
32890         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32891         
32892         var allowed = "0123456789";
32893         
32894         if(this.allowDecimals){
32895             allowed += this.decimalSeparator;
32896         }
32897         
32898         if(this.allowNegative){
32899             allowed += "-";
32900         }
32901         
32902         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32903         
32904         var keyPress = function(e){
32905             
32906             var k = e.getKey();
32907             
32908             var c = e.getCharCode();
32909             
32910             if(
32911                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32912                     allowed.indexOf(String.fromCharCode(c)) === -1
32913             ){
32914                 e.stopEvent();
32915                 return;
32916             }
32917             
32918             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32919                 return;
32920             }
32921             
32922             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32923                 e.stopEvent();
32924             }
32925         };
32926         
32927         this.el.on("keypress", keyPress, this);
32928     },
32929     
32930     validateValue : function(value)
32931     {
32932         
32933         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32934             return false;
32935         }
32936         
32937         var num = this.parseValue(value);
32938         
32939         if(isNaN(num)){
32940             this.markInvalid(String.format(this.nanText, value));
32941             return false;
32942         }
32943         
32944         if(num < this.minValue){
32945             this.markInvalid(String.format(this.minText, this.minValue));
32946             return false;
32947         }
32948         
32949         if(num > this.maxValue){
32950             this.markInvalid(String.format(this.maxText, this.maxValue));
32951             return false;
32952         }
32953         
32954         return true;
32955     },
32956
32957     getValue : function()
32958     {
32959         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32960     },
32961
32962     parseValue : function(value)
32963     {
32964         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32965         return isNaN(value) ? '' : value;
32966     },
32967
32968     fixPrecision : function(value)
32969     {
32970         var nan = isNaN(value);
32971         
32972         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32973             return nan ? '' : value;
32974         }
32975         return parseFloat(value).toFixed(this.decimalPrecision);
32976     },
32977
32978     setValue : function(v)
32979     {
32980         v = this.fixPrecision(v);
32981         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32982     },
32983
32984     decimalPrecisionFcn : function(v)
32985     {
32986         return Math.floor(v);
32987     },
32988
32989     beforeBlur : function()
32990     {
32991         if(!this.castInt){
32992             return;
32993         }
32994         
32995         var v = this.parseValue(this.getRawValue());
32996         if(v){
32997             this.setValue(v);
32998         }
32999     }
33000     
33001 });
33002
33003  
33004
33005 /*
33006 * Licence: LGPL
33007 */
33008
33009 /**
33010  * @class Roo.bootstrap.DocumentSlider
33011  * @extends Roo.bootstrap.Component
33012  * Bootstrap DocumentSlider class
33013  * 
33014  * @constructor
33015  * Create a new DocumentViewer
33016  * @param {Object} config The config object
33017  */
33018
33019 Roo.bootstrap.DocumentSlider = function(config){
33020     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33021     
33022     this.files = [];
33023     
33024     this.addEvents({
33025         /**
33026          * @event initial
33027          * Fire after initEvent
33028          * @param {Roo.bootstrap.DocumentSlider} this
33029          */
33030         "initial" : true,
33031         /**
33032          * @event update
33033          * Fire after update
33034          * @param {Roo.bootstrap.DocumentSlider} this
33035          */
33036         "update" : true,
33037         /**
33038          * @event click
33039          * Fire after click
33040          * @param {Roo.bootstrap.DocumentSlider} this
33041          */
33042         "click" : true
33043     });
33044 };
33045
33046 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33047     
33048     files : false,
33049     
33050     indicator : 0,
33051     
33052     getAutoCreate : function()
33053     {
33054         var cfg = {
33055             tag : 'div',
33056             cls : 'roo-document-slider',
33057             cn : [
33058                 {
33059                     tag : 'div',
33060                     cls : 'roo-document-slider-header',
33061                     cn : [
33062                         {
33063                             tag : 'div',
33064                             cls : 'roo-document-slider-header-title'
33065                         }
33066                     ]
33067                 },
33068                 {
33069                     tag : 'div',
33070                     cls : 'roo-document-slider-body',
33071                     cn : [
33072                         {
33073                             tag : 'div',
33074                             cls : 'roo-document-slider-prev',
33075                             cn : [
33076                                 {
33077                                     tag : 'i',
33078                                     cls : 'fa fa-chevron-left'
33079                                 }
33080                             ]
33081                         },
33082                         {
33083                             tag : 'div',
33084                             cls : 'roo-document-slider-thumb',
33085                             cn : [
33086                                 {
33087                                     tag : 'img',
33088                                     cls : 'roo-document-slider-image'
33089                                 }
33090                             ]
33091                         },
33092                         {
33093                             tag : 'div',
33094                             cls : 'roo-document-slider-next',
33095                             cn : [
33096                                 {
33097                                     tag : 'i',
33098                                     cls : 'fa fa-chevron-right'
33099                                 }
33100                             ]
33101                         }
33102                     ]
33103                 }
33104             ]
33105         };
33106         
33107         return cfg;
33108     },
33109     
33110     initEvents : function()
33111     {
33112         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33113         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33114         
33115         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33116         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33117         
33118         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33119         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33120         
33121         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33122         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33123         
33124         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33125         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33126         
33127         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33128         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33129         
33130         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33131         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33132         
33133         this.thumbEl.on('click', this.onClick, this);
33134         
33135         this.prevIndicator.on('click', this.prev, this);
33136         
33137         this.nextIndicator.on('click', this.next, this);
33138         
33139     },
33140     
33141     initial : function()
33142     {
33143         if(this.files.length){
33144             this.indicator = 1;
33145             this.update()
33146         }
33147         
33148         this.fireEvent('initial', this);
33149     },
33150     
33151     update : function()
33152     {
33153         this.imageEl.attr('src', this.files[this.indicator - 1]);
33154         
33155         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33156         
33157         this.prevIndicator.show();
33158         
33159         if(this.indicator == 1){
33160             this.prevIndicator.hide();
33161         }
33162         
33163         this.nextIndicator.show();
33164         
33165         if(this.indicator == this.files.length){
33166             this.nextIndicator.hide();
33167         }
33168         
33169         this.thumbEl.scrollTo('top');
33170         
33171         this.fireEvent('update', this);
33172     },
33173     
33174     onClick : function(e)
33175     {
33176         e.preventDefault();
33177         
33178         this.fireEvent('click', this);
33179     },
33180     
33181     prev : function(e)
33182     {
33183         e.preventDefault();
33184         
33185         this.indicator = Math.max(1, this.indicator - 1);
33186         
33187         this.update();
33188     },
33189     
33190     next : function(e)
33191     {
33192         e.preventDefault();
33193         
33194         this.indicator = Math.min(this.files.length, this.indicator + 1);
33195         
33196         this.update();
33197     }
33198 });
33199 /*
33200  * - LGPL
33201  *
33202  * RadioSet
33203  *
33204  *
33205  */
33206
33207 /**
33208  * @class Roo.bootstrap.RadioSet
33209  * @extends Roo.bootstrap.Input
33210  * Bootstrap RadioSet class
33211  * @cfg {String} indicatorpos (left|right) default left
33212  * @cfg {Boolean} inline (true|false) inline the element (default true)
33213  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33214  * @constructor
33215  * Create a new RadioSet
33216  * @param {Object} config The config object
33217  */
33218
33219 Roo.bootstrap.RadioSet = function(config){
33220     
33221     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33222     
33223     this.radioes = [];
33224     
33225     Roo.bootstrap.RadioSet.register(this);
33226     
33227     this.addEvents({
33228         /**
33229         * @event check
33230         * Fires when the element is checked or unchecked.
33231         * @param {Roo.bootstrap.RadioSet} this This radio
33232         * @param {Roo.bootstrap.Radio} item The checked item
33233         */
33234        check : true
33235     });
33236     
33237 };
33238
33239 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33240
33241     radioes : false,
33242     
33243     inline : true,
33244     
33245     weight : '',
33246     
33247     indicatorpos : 'left',
33248     
33249     getAutoCreate : function()
33250     {
33251         var label = {
33252             tag : 'label',
33253             cls : 'roo-radio-set-label',
33254             cn : [
33255                 {
33256                     tag : 'span',
33257                     html : this.fieldLabel
33258                 }
33259             ]
33260         };
33261         
33262         if(this.indicatorpos == 'left'){
33263             label.cn.unshift({
33264                 tag : 'i',
33265                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33266                 tooltip : 'This field is required'
33267             });
33268         } else {
33269             label.cn.push({
33270                 tag : 'i',
33271                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33272                 tooltip : 'This field is required'
33273             });
33274         }
33275         
33276         var items = {
33277             tag : 'div',
33278             cls : 'roo-radio-set-items'
33279         };
33280         
33281         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33282         
33283         if (align === 'left' && this.fieldLabel.length) {
33284             
33285             items = {
33286                 cls : "roo-radio-set-right", 
33287                 cn: [
33288                     items
33289                 ]
33290             };
33291             
33292             if(this.labelWidth > 12){
33293                 label.style = "width: " + this.labelWidth + 'px';
33294             }
33295             
33296             if(this.labelWidth < 13 && this.labelmd == 0){
33297                 this.labelmd = this.labelWidth;
33298             }
33299             
33300             if(this.labellg > 0){
33301                 label.cls += ' col-lg-' + this.labellg;
33302                 items.cls += ' col-lg-' + (12 - this.labellg);
33303             }
33304             
33305             if(this.labelmd > 0){
33306                 label.cls += ' col-md-' + this.labelmd;
33307                 items.cls += ' col-md-' + (12 - this.labelmd);
33308             }
33309             
33310             if(this.labelsm > 0){
33311                 label.cls += ' col-sm-' + this.labelsm;
33312                 items.cls += ' col-sm-' + (12 - this.labelsm);
33313             }
33314             
33315             if(this.labelxs > 0){
33316                 label.cls += ' col-xs-' + this.labelxs;
33317                 items.cls += ' col-xs-' + (12 - this.labelxs);
33318             }
33319         }
33320         
33321         var cfg = {
33322             tag : 'div',
33323             cls : 'roo-radio-set',
33324             cn : [
33325                 {
33326                     tag : 'input',
33327                     cls : 'roo-radio-set-input',
33328                     type : 'hidden',
33329                     name : this.name,
33330                     value : this.value ? this.value :  ''
33331                 },
33332                 label,
33333                 items
33334             ]
33335         };
33336         
33337         if(this.weight.length){
33338             cfg.cls += ' roo-radio-' + this.weight;
33339         }
33340         
33341         if(this.inline) {
33342             cfg.cls += ' roo-radio-set-inline';
33343         }
33344         
33345         var settings=this;
33346         ['xs','sm','md','lg'].map(function(size){
33347             if (settings[size]) {
33348                 cfg.cls += ' col-' + size + '-' + settings[size];
33349             }
33350         });
33351         
33352         return cfg;
33353         
33354     },
33355
33356     initEvents : function()
33357     {
33358         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33359         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33360         
33361         if(!this.fieldLabel.length){
33362             this.labelEl.hide();
33363         }
33364         
33365         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33366         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33367         
33368         this.indicatorEl().addClass('invisible');
33369         
33370         this.originalValue = this.getValue();
33371         
33372     },
33373     
33374     inputEl: function ()
33375     {
33376         return this.el.select('.roo-radio-set-input', true).first();
33377     },
33378     
33379     getChildContainer : function()
33380     {
33381         return this.itemsEl;
33382     },
33383     
33384     register : function(item)
33385     {
33386         this.radioes.push(item);
33387         
33388     },
33389     
33390     validate : function()
33391     {   
33392         var valid = false;
33393         
33394         Roo.each(this.radioes, function(i){
33395             if(!i.checked){
33396                 return;
33397             }
33398             
33399             valid = true;
33400             return false;
33401         });
33402         
33403         if(this.allowBlank) {
33404             return true;
33405         }
33406         
33407         if(this.disabled || valid){
33408             this.markValid();
33409             return true;
33410         }
33411         
33412         this.markInvalid();
33413         return false;
33414         
33415     },
33416     
33417     markValid : function()
33418     {
33419         if(this.labelEl.isVisible(true)){
33420             this.indicatorEl().removeClass('visible');
33421             this.indicatorEl().addClass('invisible');
33422         }
33423         
33424         this.el.removeClass([this.invalidClass, this.validClass]);
33425         this.el.addClass(this.validClass);
33426         
33427         this.fireEvent('valid', this);
33428     },
33429     
33430     markInvalid : function(msg)
33431     {
33432         if(this.allowBlank || this.disabled){
33433             return;
33434         }
33435         
33436         if(this.labelEl.isVisible(true)){
33437             this.indicatorEl().removeClass('invisible');
33438             this.indicatorEl().addClass('visible');
33439         }
33440         
33441         this.el.removeClass([this.invalidClass, this.validClass]);
33442         this.el.addClass(this.invalidClass);
33443         
33444         this.fireEvent('invalid', this, msg);
33445         
33446     },
33447     
33448     setValue : function(v, suppressEvent)
33449     {   
33450         this.value = v;
33451         if(this.rendered){
33452             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33453         }
33454         
33455         Roo.each(this.radioes, function(i){
33456             
33457             i.checked = false;
33458             i.el.removeClass('checked');
33459             
33460             if(i.value === v || i.value.toString() === v.toString()){
33461                 i.checked = true;
33462                 i.el.addClass('checked');
33463                 
33464                 if(suppressEvent !== true){
33465                     this.fireEvent('check', this, i);
33466                 }
33467             }
33468             
33469         }, this);
33470         
33471         this.validate();
33472     },
33473     
33474     clearInvalid : function(){
33475         
33476         if(!this.el || this.preventMark){
33477             return;
33478         }
33479         
33480         this.el.removeClass([this.invalidClass]);
33481         
33482         this.fireEvent('valid', this);
33483     }
33484     
33485 });
33486
33487 Roo.apply(Roo.bootstrap.RadioSet, {
33488     
33489     groups: {},
33490     
33491     register : function(set)
33492     {
33493         this.groups[set.name] = set;
33494     },
33495     
33496     get: function(name) 
33497     {
33498         if (typeof(this.groups[name]) == 'undefined') {
33499             return false;
33500         }
33501         
33502         return this.groups[name] ;
33503     }
33504     
33505 });
33506 /*
33507  * Based on:
33508  * Ext JS Library 1.1.1
33509  * Copyright(c) 2006-2007, Ext JS, LLC.
33510  *
33511  * Originally Released Under LGPL - original licence link has changed is not relivant.
33512  *
33513  * Fork - LGPL
33514  * <script type="text/javascript">
33515  */
33516
33517
33518 /**
33519  * @class Roo.bootstrap.SplitBar
33520  * @extends Roo.util.Observable
33521  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33522  * <br><br>
33523  * Usage:
33524  * <pre><code>
33525 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33526                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33527 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33528 split.minSize = 100;
33529 split.maxSize = 600;
33530 split.animate = true;
33531 split.on('moved', splitterMoved);
33532 </code></pre>
33533  * @constructor
33534  * Create a new SplitBar
33535  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33536  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33537  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33538  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33539                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33540                         position of the SplitBar).
33541  */
33542 Roo.bootstrap.SplitBar = function(cfg){
33543     
33544     /** @private */
33545     
33546     //{
33547     //  dragElement : elm
33548     //  resizingElement: el,
33549         // optional..
33550     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33551     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33552         // existingProxy ???
33553     //}
33554     
33555     this.el = Roo.get(cfg.dragElement, true);
33556     this.el.dom.unselectable = "on";
33557     /** @private */
33558     this.resizingEl = Roo.get(cfg.resizingElement, true);
33559
33560     /**
33561      * @private
33562      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33563      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33564      * @type Number
33565      */
33566     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33567     
33568     /**
33569      * The minimum size of the resizing element. (Defaults to 0)
33570      * @type Number
33571      */
33572     this.minSize = 0;
33573     
33574     /**
33575      * The maximum size of the resizing element. (Defaults to 2000)
33576      * @type Number
33577      */
33578     this.maxSize = 2000;
33579     
33580     /**
33581      * Whether to animate the transition to the new size
33582      * @type Boolean
33583      */
33584     this.animate = false;
33585     
33586     /**
33587      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33588      * @type Boolean
33589      */
33590     this.useShim = false;
33591     
33592     /** @private */
33593     this.shim = null;
33594     
33595     if(!cfg.existingProxy){
33596         /** @private */
33597         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33598     }else{
33599         this.proxy = Roo.get(cfg.existingProxy).dom;
33600     }
33601     /** @private */
33602     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33603     
33604     /** @private */
33605     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33606     
33607     /** @private */
33608     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33609     
33610     /** @private */
33611     this.dragSpecs = {};
33612     
33613     /**
33614      * @private The adapter to use to positon and resize elements
33615      */
33616     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33617     this.adapter.init(this);
33618     
33619     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33620         /** @private */
33621         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33622         this.el.addClass("roo-splitbar-h");
33623     }else{
33624         /** @private */
33625         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33626         this.el.addClass("roo-splitbar-v");
33627     }
33628     
33629     this.addEvents({
33630         /**
33631          * @event resize
33632          * Fires when the splitter is moved (alias for {@link #event-moved})
33633          * @param {Roo.bootstrap.SplitBar} this
33634          * @param {Number} newSize the new width or height
33635          */
33636         "resize" : true,
33637         /**
33638          * @event moved
33639          * Fires when the splitter is moved
33640          * @param {Roo.bootstrap.SplitBar} this
33641          * @param {Number} newSize the new width or height
33642          */
33643         "moved" : true,
33644         /**
33645          * @event beforeresize
33646          * Fires before the splitter is dragged
33647          * @param {Roo.bootstrap.SplitBar} this
33648          */
33649         "beforeresize" : true,
33650
33651         "beforeapply" : true
33652     });
33653
33654     Roo.util.Observable.call(this);
33655 };
33656
33657 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33658     onStartProxyDrag : function(x, y){
33659         this.fireEvent("beforeresize", this);
33660         if(!this.overlay){
33661             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33662             o.unselectable();
33663             o.enableDisplayMode("block");
33664             // all splitbars share the same overlay
33665             Roo.bootstrap.SplitBar.prototype.overlay = o;
33666         }
33667         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33668         this.overlay.show();
33669         Roo.get(this.proxy).setDisplayed("block");
33670         var size = this.adapter.getElementSize(this);
33671         this.activeMinSize = this.getMinimumSize();;
33672         this.activeMaxSize = this.getMaximumSize();;
33673         var c1 = size - this.activeMinSize;
33674         var c2 = Math.max(this.activeMaxSize - size, 0);
33675         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33676             this.dd.resetConstraints();
33677             this.dd.setXConstraint(
33678                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33679                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33680             );
33681             this.dd.setYConstraint(0, 0);
33682         }else{
33683             this.dd.resetConstraints();
33684             this.dd.setXConstraint(0, 0);
33685             this.dd.setYConstraint(
33686                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33687                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33688             );
33689          }
33690         this.dragSpecs.startSize = size;
33691         this.dragSpecs.startPoint = [x, y];
33692         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33693     },
33694     
33695     /** 
33696      * @private Called after the drag operation by the DDProxy
33697      */
33698     onEndProxyDrag : function(e){
33699         Roo.get(this.proxy).setDisplayed(false);
33700         var endPoint = Roo.lib.Event.getXY(e);
33701         if(this.overlay){
33702             this.overlay.hide();
33703         }
33704         var newSize;
33705         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33706             newSize = this.dragSpecs.startSize + 
33707                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33708                     endPoint[0] - this.dragSpecs.startPoint[0] :
33709                     this.dragSpecs.startPoint[0] - endPoint[0]
33710                 );
33711         }else{
33712             newSize = this.dragSpecs.startSize + 
33713                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33714                     endPoint[1] - this.dragSpecs.startPoint[1] :
33715                     this.dragSpecs.startPoint[1] - endPoint[1]
33716                 );
33717         }
33718         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33719         if(newSize != this.dragSpecs.startSize){
33720             if(this.fireEvent('beforeapply', this, newSize) !== false){
33721                 this.adapter.setElementSize(this, newSize);
33722                 this.fireEvent("moved", this, newSize);
33723                 this.fireEvent("resize", this, newSize);
33724             }
33725         }
33726     },
33727     
33728     /**
33729      * Get the adapter this SplitBar uses
33730      * @return The adapter object
33731      */
33732     getAdapter : function(){
33733         return this.adapter;
33734     },
33735     
33736     /**
33737      * Set the adapter this SplitBar uses
33738      * @param {Object} adapter A SplitBar adapter object
33739      */
33740     setAdapter : function(adapter){
33741         this.adapter = adapter;
33742         this.adapter.init(this);
33743     },
33744     
33745     /**
33746      * Gets the minimum size for the resizing element
33747      * @return {Number} The minimum size
33748      */
33749     getMinimumSize : function(){
33750         return this.minSize;
33751     },
33752     
33753     /**
33754      * Sets the minimum size for the resizing element
33755      * @param {Number} minSize The minimum size
33756      */
33757     setMinimumSize : function(minSize){
33758         this.minSize = minSize;
33759     },
33760     
33761     /**
33762      * Gets the maximum size for the resizing element
33763      * @return {Number} The maximum size
33764      */
33765     getMaximumSize : function(){
33766         return this.maxSize;
33767     },
33768     
33769     /**
33770      * Sets the maximum size for the resizing element
33771      * @param {Number} maxSize The maximum size
33772      */
33773     setMaximumSize : function(maxSize){
33774         this.maxSize = maxSize;
33775     },
33776     
33777     /**
33778      * Sets the initialize size for the resizing element
33779      * @param {Number} size The initial size
33780      */
33781     setCurrentSize : function(size){
33782         var oldAnimate = this.animate;
33783         this.animate = false;
33784         this.adapter.setElementSize(this, size);
33785         this.animate = oldAnimate;
33786     },
33787     
33788     /**
33789      * Destroy this splitbar. 
33790      * @param {Boolean} removeEl True to remove the element
33791      */
33792     destroy : function(removeEl){
33793         if(this.shim){
33794             this.shim.remove();
33795         }
33796         this.dd.unreg();
33797         this.proxy.parentNode.removeChild(this.proxy);
33798         if(removeEl){
33799             this.el.remove();
33800         }
33801     }
33802 });
33803
33804 /**
33805  * @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.
33806  */
33807 Roo.bootstrap.SplitBar.createProxy = function(dir){
33808     var proxy = new Roo.Element(document.createElement("div"));
33809     proxy.unselectable();
33810     var cls = 'roo-splitbar-proxy';
33811     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33812     document.body.appendChild(proxy.dom);
33813     return proxy.dom;
33814 };
33815
33816 /** 
33817  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33818  * Default Adapter. It assumes the splitter and resizing element are not positioned
33819  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33820  */
33821 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33822 };
33823
33824 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33825     // do nothing for now
33826     init : function(s){
33827     
33828     },
33829     /**
33830      * Called before drag operations to get the current size of the resizing element. 
33831      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33832      */
33833      getElementSize : function(s){
33834         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33835             return s.resizingEl.getWidth();
33836         }else{
33837             return s.resizingEl.getHeight();
33838         }
33839     },
33840     
33841     /**
33842      * Called after drag operations to set the size of the resizing element.
33843      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33844      * @param {Number} newSize The new size to set
33845      * @param {Function} onComplete A function to be invoked when resizing is complete
33846      */
33847     setElementSize : function(s, newSize, onComplete){
33848         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33849             if(!s.animate){
33850                 s.resizingEl.setWidth(newSize);
33851                 if(onComplete){
33852                     onComplete(s, newSize);
33853                 }
33854             }else{
33855                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33856             }
33857         }else{
33858             
33859             if(!s.animate){
33860                 s.resizingEl.setHeight(newSize);
33861                 if(onComplete){
33862                     onComplete(s, newSize);
33863                 }
33864             }else{
33865                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33866             }
33867         }
33868     }
33869 };
33870
33871 /** 
33872  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33873  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33874  * Adapter that  moves the splitter element to align with the resized sizing element. 
33875  * Used with an absolute positioned SplitBar.
33876  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33877  * document.body, make sure you assign an id to the body element.
33878  */
33879 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33880     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33881     this.container = Roo.get(container);
33882 };
33883
33884 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33885     init : function(s){
33886         this.basic.init(s);
33887     },
33888     
33889     getElementSize : function(s){
33890         return this.basic.getElementSize(s);
33891     },
33892     
33893     setElementSize : function(s, newSize, onComplete){
33894         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33895     },
33896     
33897     moveSplitter : function(s){
33898         var yes = Roo.bootstrap.SplitBar;
33899         switch(s.placement){
33900             case yes.LEFT:
33901                 s.el.setX(s.resizingEl.getRight());
33902                 break;
33903             case yes.RIGHT:
33904                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33905                 break;
33906             case yes.TOP:
33907                 s.el.setY(s.resizingEl.getBottom());
33908                 break;
33909             case yes.BOTTOM:
33910                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33911                 break;
33912         }
33913     }
33914 };
33915
33916 /**
33917  * Orientation constant - Create a vertical SplitBar
33918  * @static
33919  * @type Number
33920  */
33921 Roo.bootstrap.SplitBar.VERTICAL = 1;
33922
33923 /**
33924  * Orientation constant - Create a horizontal SplitBar
33925  * @static
33926  * @type Number
33927  */
33928 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33929
33930 /**
33931  * Placement constant - The resizing element is to the left of the splitter element
33932  * @static
33933  * @type Number
33934  */
33935 Roo.bootstrap.SplitBar.LEFT = 1;
33936
33937 /**
33938  * Placement constant - The resizing element is to the right of the splitter element
33939  * @static
33940  * @type Number
33941  */
33942 Roo.bootstrap.SplitBar.RIGHT = 2;
33943
33944 /**
33945  * Placement constant - The resizing element is positioned above the splitter element
33946  * @static
33947  * @type Number
33948  */
33949 Roo.bootstrap.SplitBar.TOP = 3;
33950
33951 /**
33952  * Placement constant - The resizing element is positioned under splitter element
33953  * @static
33954  * @type Number
33955  */
33956 Roo.bootstrap.SplitBar.BOTTOM = 4;
33957 Roo.namespace("Roo.bootstrap.layout");/*
33958  * Based on:
33959  * Ext JS Library 1.1.1
33960  * Copyright(c) 2006-2007, Ext JS, LLC.
33961  *
33962  * Originally Released Under LGPL - original licence link has changed is not relivant.
33963  *
33964  * Fork - LGPL
33965  * <script type="text/javascript">
33966  */
33967
33968 /**
33969  * @class Roo.bootstrap.layout.Manager
33970  * @extends Roo.bootstrap.Component
33971  * Base class for layout managers.
33972  */
33973 Roo.bootstrap.layout.Manager = function(config)
33974 {
33975     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33976
33977
33978
33979
33980
33981     /** false to disable window resize monitoring @type Boolean */
33982     this.monitorWindowResize = true;
33983     this.regions = {};
33984     this.addEvents({
33985         /**
33986          * @event layout
33987          * Fires when a layout is performed.
33988          * @param {Roo.LayoutManager} this
33989          */
33990         "layout" : true,
33991         /**
33992          * @event regionresized
33993          * Fires when the user resizes a region.
33994          * @param {Roo.LayoutRegion} region The resized region
33995          * @param {Number} newSize The new size (width for east/west, height for north/south)
33996          */
33997         "regionresized" : true,
33998         /**
33999          * @event regioncollapsed
34000          * Fires when a region is collapsed.
34001          * @param {Roo.LayoutRegion} region The collapsed region
34002          */
34003         "regioncollapsed" : true,
34004         /**
34005          * @event regionexpanded
34006          * Fires when a region is expanded.
34007          * @param {Roo.LayoutRegion} region The expanded region
34008          */
34009         "regionexpanded" : true
34010     });
34011     this.updating = false;
34012
34013     if (config.el) {
34014         this.el = Roo.get(config.el);
34015         this.initEvents();
34016     }
34017
34018 };
34019
34020 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34021
34022
34023     regions : null,
34024
34025     monitorWindowResize : true,
34026
34027
34028     updating : false,
34029
34030
34031     onRender : function(ct, position)
34032     {
34033         if(!this.el){
34034             this.el = Roo.get(ct);
34035             this.initEvents();
34036         }
34037         //this.fireEvent('render',this);
34038     },
34039
34040
34041     initEvents: function()
34042     {
34043
34044
34045         // ie scrollbar fix
34046         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34047             document.body.scroll = "no";
34048         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34049             this.el.position('relative');
34050         }
34051         this.id = this.el.id;
34052         this.el.addClass("roo-layout-container");
34053         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34054         if(this.el.dom != document.body ) {
34055             this.el.on('resize', this.layout,this);
34056             this.el.on('show', this.layout,this);
34057         }
34058
34059     },
34060
34061     /**
34062      * Returns true if this layout is currently being updated
34063      * @return {Boolean}
34064      */
34065     isUpdating : function(){
34066         return this.updating;
34067     },
34068
34069     /**
34070      * Suspend the LayoutManager from doing auto-layouts while
34071      * making multiple add or remove calls
34072      */
34073     beginUpdate : function(){
34074         this.updating = true;
34075     },
34076
34077     /**
34078      * Restore auto-layouts and optionally disable the manager from performing a layout
34079      * @param {Boolean} noLayout true to disable a layout update
34080      */
34081     endUpdate : function(noLayout){
34082         this.updating = false;
34083         if(!noLayout){
34084             this.layout();
34085         }
34086     },
34087
34088     layout: function(){
34089         // abstract...
34090     },
34091
34092     onRegionResized : function(region, newSize){
34093         this.fireEvent("regionresized", region, newSize);
34094         this.layout();
34095     },
34096
34097     onRegionCollapsed : function(region){
34098         this.fireEvent("regioncollapsed", region);
34099     },
34100
34101     onRegionExpanded : function(region){
34102         this.fireEvent("regionexpanded", region);
34103     },
34104
34105     /**
34106      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34107      * performs box-model adjustments.
34108      * @return {Object} The size as an object {width: (the width), height: (the height)}
34109      */
34110     getViewSize : function()
34111     {
34112         var size;
34113         if(this.el.dom != document.body){
34114             size = this.el.getSize();
34115         }else{
34116             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34117         }
34118         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34119         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34120         return size;
34121     },
34122
34123     /**
34124      * Returns the Element this layout is bound to.
34125      * @return {Roo.Element}
34126      */
34127     getEl : function(){
34128         return this.el;
34129     },
34130
34131     /**
34132      * Returns the specified region.
34133      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34134      * @return {Roo.LayoutRegion}
34135      */
34136     getRegion : function(target){
34137         return this.regions[target.toLowerCase()];
34138     },
34139
34140     onWindowResize : function(){
34141         if(this.monitorWindowResize){
34142             this.layout();
34143         }
34144     }
34145 });
34146 /*
34147  * Based on:
34148  * Ext JS Library 1.1.1
34149  * Copyright(c) 2006-2007, Ext JS, LLC.
34150  *
34151  * Originally Released Under LGPL - original licence link has changed is not relivant.
34152  *
34153  * Fork - LGPL
34154  * <script type="text/javascript">
34155  */
34156 /**
34157  * @class Roo.bootstrap.layout.Border
34158  * @extends Roo.bootstrap.layout.Manager
34159  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34160  * please see: examples/bootstrap/nested.html<br><br>
34161  
34162 <b>The container the layout is rendered into can be either the body element or any other element.
34163 If it is not the body element, the container needs to either be an absolute positioned element,
34164 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34165 the container size if it is not the body element.</b>
34166
34167 * @constructor
34168 * Create a new Border
34169 * @param {Object} config Configuration options
34170  */
34171 Roo.bootstrap.layout.Border = function(config){
34172     config = config || {};
34173     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34174     
34175     
34176     
34177     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34178         if(config[region]){
34179             config[region].region = region;
34180             this.addRegion(config[region]);
34181         }
34182     },this);
34183     
34184 };
34185
34186 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34187
34188 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34189     /**
34190      * Creates and adds a new region if it doesn't already exist.
34191      * @param {String} target The target region key (north, south, east, west or center).
34192      * @param {Object} config The regions config object
34193      * @return {BorderLayoutRegion} The new region
34194      */
34195     addRegion : function(config)
34196     {
34197         if(!this.regions[config.region]){
34198             var r = this.factory(config);
34199             this.bindRegion(r);
34200         }
34201         return this.regions[config.region];
34202     },
34203
34204     // private (kinda)
34205     bindRegion : function(r){
34206         this.regions[r.config.region] = r;
34207         
34208         r.on("visibilitychange",    this.layout, this);
34209         r.on("paneladded",          this.layout, this);
34210         r.on("panelremoved",        this.layout, this);
34211         r.on("invalidated",         this.layout, this);
34212         r.on("resized",             this.onRegionResized, this);
34213         r.on("collapsed",           this.onRegionCollapsed, this);
34214         r.on("expanded",            this.onRegionExpanded, this);
34215     },
34216
34217     /**
34218      * Performs a layout update.
34219      */
34220     layout : function()
34221     {
34222         if(this.updating) {
34223             return;
34224         }
34225         
34226         // render all the rebions if they have not been done alreayd?
34227         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34228             if(this.regions[region] && !this.regions[region].bodyEl){
34229                 this.regions[region].onRender(this.el)
34230             }
34231         },this);
34232         
34233         var size = this.getViewSize();
34234         var w = size.width;
34235         var h = size.height;
34236         var centerW = w;
34237         var centerH = h;
34238         var centerY = 0;
34239         var centerX = 0;
34240         //var x = 0, y = 0;
34241
34242         var rs = this.regions;
34243         var north = rs["north"];
34244         var south = rs["south"]; 
34245         var west = rs["west"];
34246         var east = rs["east"];
34247         var center = rs["center"];
34248         //if(this.hideOnLayout){ // not supported anymore
34249             //c.el.setStyle("display", "none");
34250         //}
34251         if(north && north.isVisible()){
34252             var b = north.getBox();
34253             var m = north.getMargins();
34254             b.width = w - (m.left+m.right);
34255             b.x = m.left;
34256             b.y = m.top;
34257             centerY = b.height + b.y + m.bottom;
34258             centerH -= centerY;
34259             north.updateBox(this.safeBox(b));
34260         }
34261         if(south && south.isVisible()){
34262             var b = south.getBox();
34263             var m = south.getMargins();
34264             b.width = w - (m.left+m.right);
34265             b.x = m.left;
34266             var totalHeight = (b.height + m.top + m.bottom);
34267             b.y = h - totalHeight + m.top;
34268             centerH -= totalHeight;
34269             south.updateBox(this.safeBox(b));
34270         }
34271         if(west && west.isVisible()){
34272             var b = west.getBox();
34273             var m = west.getMargins();
34274             b.height = centerH - (m.top+m.bottom);
34275             b.x = m.left;
34276             b.y = centerY + m.top;
34277             var totalWidth = (b.width + m.left + m.right);
34278             centerX += totalWidth;
34279             centerW -= totalWidth;
34280             west.updateBox(this.safeBox(b));
34281         }
34282         if(east && east.isVisible()){
34283             var b = east.getBox();
34284             var m = east.getMargins();
34285             b.height = centerH - (m.top+m.bottom);
34286             var totalWidth = (b.width + m.left + m.right);
34287             b.x = w - totalWidth + m.left;
34288             b.y = centerY + m.top;
34289             centerW -= totalWidth;
34290             east.updateBox(this.safeBox(b));
34291         }
34292         if(center){
34293             var m = center.getMargins();
34294             var centerBox = {
34295                 x: centerX + m.left,
34296                 y: centerY + m.top,
34297                 width: centerW - (m.left+m.right),
34298                 height: centerH - (m.top+m.bottom)
34299             };
34300             //if(this.hideOnLayout){
34301                 //center.el.setStyle("display", "block");
34302             //}
34303             center.updateBox(this.safeBox(centerBox));
34304         }
34305         this.el.repaint();
34306         this.fireEvent("layout", this);
34307     },
34308
34309     // private
34310     safeBox : function(box){
34311         box.width = Math.max(0, box.width);
34312         box.height = Math.max(0, box.height);
34313         return box;
34314     },
34315
34316     /**
34317      * Adds a ContentPanel (or subclass) to this layout.
34318      * @param {String} target The target region key (north, south, east, west or center).
34319      * @param {Roo.ContentPanel} panel The panel to add
34320      * @return {Roo.ContentPanel} The added panel
34321      */
34322     add : function(target, panel){
34323          
34324         target = target.toLowerCase();
34325         return this.regions[target].add(panel);
34326     },
34327
34328     /**
34329      * Remove a ContentPanel (or subclass) to this layout.
34330      * @param {String} target The target region key (north, south, east, west or center).
34331      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34332      * @return {Roo.ContentPanel} The removed panel
34333      */
34334     remove : function(target, panel){
34335         target = target.toLowerCase();
34336         return this.regions[target].remove(panel);
34337     },
34338
34339     /**
34340      * Searches all regions for a panel with the specified id
34341      * @param {String} panelId
34342      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34343      */
34344     findPanel : function(panelId){
34345         var rs = this.regions;
34346         for(var target in rs){
34347             if(typeof rs[target] != "function"){
34348                 var p = rs[target].getPanel(panelId);
34349                 if(p){
34350                     return p;
34351                 }
34352             }
34353         }
34354         return null;
34355     },
34356
34357     /**
34358      * Searches all regions for a panel with the specified id and activates (shows) it.
34359      * @param {String/ContentPanel} panelId The panels id or the panel itself
34360      * @return {Roo.ContentPanel} The shown panel or null
34361      */
34362     showPanel : function(panelId) {
34363       var rs = this.regions;
34364       for(var target in rs){
34365          var r = rs[target];
34366          if(typeof r != "function"){
34367             if(r.hasPanel(panelId)){
34368                return r.showPanel(panelId);
34369             }
34370          }
34371       }
34372       return null;
34373    },
34374
34375    /**
34376      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34377      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34378      */
34379    /*
34380     restoreState : function(provider){
34381         if(!provider){
34382             provider = Roo.state.Manager;
34383         }
34384         var sm = new Roo.LayoutStateManager();
34385         sm.init(this, provider);
34386     },
34387 */
34388  
34389  
34390     /**
34391      * Adds a xtype elements to the layout.
34392      * <pre><code>
34393
34394 layout.addxtype({
34395        xtype : 'ContentPanel',
34396        region: 'west',
34397        items: [ .... ]
34398    }
34399 );
34400
34401 layout.addxtype({
34402         xtype : 'NestedLayoutPanel',
34403         region: 'west',
34404         layout: {
34405            center: { },
34406            west: { }   
34407         },
34408         items : [ ... list of content panels or nested layout panels.. ]
34409    }
34410 );
34411 </code></pre>
34412      * @param {Object} cfg Xtype definition of item to add.
34413      */
34414     addxtype : function(cfg)
34415     {
34416         // basically accepts a pannel...
34417         // can accept a layout region..!?!?
34418         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34419         
34420         
34421         // theory?  children can only be panels??
34422         
34423         //if (!cfg.xtype.match(/Panel$/)) {
34424         //    return false;
34425         //}
34426         var ret = false;
34427         
34428         if (typeof(cfg.region) == 'undefined') {
34429             Roo.log("Failed to add Panel, region was not set");
34430             Roo.log(cfg);
34431             return false;
34432         }
34433         var region = cfg.region;
34434         delete cfg.region;
34435         
34436           
34437         var xitems = [];
34438         if (cfg.items) {
34439             xitems = cfg.items;
34440             delete cfg.items;
34441         }
34442         var nb = false;
34443         
34444         switch(cfg.xtype) 
34445         {
34446             case 'Content':  // ContentPanel (el, cfg)
34447             case 'Scroll':  // ContentPanel (el, cfg)
34448             case 'View': 
34449                 cfg.autoCreate = true;
34450                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34451                 //} else {
34452                 //    var el = this.el.createChild();
34453                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34454                 //}
34455                 
34456                 this.add(region, ret);
34457                 break;
34458             
34459             /*
34460             case 'TreePanel': // our new panel!
34461                 cfg.el = this.el.createChild();
34462                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34463                 this.add(region, ret);
34464                 break;
34465             */
34466             
34467             case 'Nest': 
34468                 // create a new Layout (which is  a Border Layout...
34469                 
34470                 var clayout = cfg.layout;
34471                 clayout.el  = this.el.createChild();
34472                 clayout.items   = clayout.items  || [];
34473                 
34474                 delete cfg.layout;
34475                 
34476                 // replace this exitems with the clayout ones..
34477                 xitems = clayout.items;
34478                  
34479                 // force background off if it's in center...
34480                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34481                     cfg.background = false;
34482                 }
34483                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34484                 
34485                 
34486                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34487                 //console.log('adding nested layout panel '  + cfg.toSource());
34488                 this.add(region, ret);
34489                 nb = {}; /// find first...
34490                 break;
34491             
34492             case 'Grid':
34493                 
34494                 // needs grid and region
34495                 
34496                 //var el = this.getRegion(region).el.createChild();
34497                 /*
34498                  *var el = this.el.createChild();
34499                 // create the grid first...
34500                 cfg.grid.container = el;
34501                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34502                 */
34503                 
34504                 if (region == 'center' && this.active ) {
34505                     cfg.background = false;
34506                 }
34507                 
34508                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34509                 
34510                 this.add(region, ret);
34511                 /*
34512                 if (cfg.background) {
34513                     // render grid on panel activation (if panel background)
34514                     ret.on('activate', function(gp) {
34515                         if (!gp.grid.rendered) {
34516                     //        gp.grid.render(el);
34517                         }
34518                     });
34519                 } else {
34520                   //  cfg.grid.render(el);
34521                 }
34522                 */
34523                 break;
34524            
34525            
34526             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34527                 // it was the old xcomponent building that caused this before.
34528                 // espeically if border is the top element in the tree.
34529                 ret = this;
34530                 break; 
34531                 
34532                     
34533                 
34534                 
34535                 
34536             default:
34537                 /*
34538                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34539                     
34540                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34541                     this.add(region, ret);
34542                 } else {
34543                 */
34544                     Roo.log(cfg);
34545                     throw "Can not add '" + cfg.xtype + "' to Border";
34546                     return null;
34547              
34548                                 
34549              
34550         }
34551         this.beginUpdate();
34552         // add children..
34553         var region = '';
34554         var abn = {};
34555         Roo.each(xitems, function(i)  {
34556             region = nb && i.region ? i.region : false;
34557             
34558             var add = ret.addxtype(i);
34559            
34560             if (region) {
34561                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34562                 if (!i.background) {
34563                     abn[region] = nb[region] ;
34564                 }
34565             }
34566             
34567         });
34568         this.endUpdate();
34569
34570         // make the last non-background panel active..
34571         //if (nb) { Roo.log(abn); }
34572         if (nb) {
34573             
34574             for(var r in abn) {
34575                 region = this.getRegion(r);
34576                 if (region) {
34577                     // tried using nb[r], but it does not work..
34578                      
34579                     region.showPanel(abn[r]);
34580                    
34581                 }
34582             }
34583         }
34584         return ret;
34585         
34586     },
34587     
34588     
34589 // private
34590     factory : function(cfg)
34591     {
34592         
34593         var validRegions = Roo.bootstrap.layout.Border.regions;
34594
34595         var target = cfg.region;
34596         cfg.mgr = this;
34597         
34598         var r = Roo.bootstrap.layout;
34599         Roo.log(target);
34600         switch(target){
34601             case "north":
34602                 return new r.North(cfg);
34603             case "south":
34604                 return new r.South(cfg);
34605             case "east":
34606                 return new r.East(cfg);
34607             case "west":
34608                 return new r.West(cfg);
34609             case "center":
34610                 return new r.Center(cfg);
34611         }
34612         throw 'Layout region "'+target+'" not supported.';
34613     }
34614     
34615     
34616 });
34617  /*
34618  * Based on:
34619  * Ext JS Library 1.1.1
34620  * Copyright(c) 2006-2007, Ext JS, LLC.
34621  *
34622  * Originally Released Under LGPL - original licence link has changed is not relivant.
34623  *
34624  * Fork - LGPL
34625  * <script type="text/javascript">
34626  */
34627  
34628 /**
34629  * @class Roo.bootstrap.layout.Basic
34630  * @extends Roo.util.Observable
34631  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34632  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34633  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34634  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34635  * @cfg {string}   region  the region that it inhabits..
34636  * @cfg {bool}   skipConfig skip config?
34637  * 
34638
34639  */
34640 Roo.bootstrap.layout.Basic = function(config){
34641     
34642     this.mgr = config.mgr;
34643     
34644     this.position = config.region;
34645     
34646     var skipConfig = config.skipConfig;
34647     
34648     this.events = {
34649         /**
34650          * @scope Roo.BasicLayoutRegion
34651          */
34652         
34653         /**
34654          * @event beforeremove
34655          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34656          * @param {Roo.LayoutRegion} this
34657          * @param {Roo.ContentPanel} panel The panel
34658          * @param {Object} e The cancel event object
34659          */
34660         "beforeremove" : true,
34661         /**
34662          * @event invalidated
34663          * Fires when the layout for this region is changed.
34664          * @param {Roo.LayoutRegion} this
34665          */
34666         "invalidated" : true,
34667         /**
34668          * @event visibilitychange
34669          * Fires when this region is shown or hidden 
34670          * @param {Roo.LayoutRegion} this
34671          * @param {Boolean} visibility true or false
34672          */
34673         "visibilitychange" : true,
34674         /**
34675          * @event paneladded
34676          * Fires when a panel is added. 
34677          * @param {Roo.LayoutRegion} this
34678          * @param {Roo.ContentPanel} panel The panel
34679          */
34680         "paneladded" : true,
34681         /**
34682          * @event panelremoved
34683          * Fires when a panel is removed. 
34684          * @param {Roo.LayoutRegion} this
34685          * @param {Roo.ContentPanel} panel The panel
34686          */
34687         "panelremoved" : true,
34688         /**
34689          * @event beforecollapse
34690          * Fires when this region before collapse.
34691          * @param {Roo.LayoutRegion} this
34692          */
34693         "beforecollapse" : true,
34694         /**
34695          * @event collapsed
34696          * Fires when this region is collapsed.
34697          * @param {Roo.LayoutRegion} this
34698          */
34699         "collapsed" : true,
34700         /**
34701          * @event expanded
34702          * Fires when this region is expanded.
34703          * @param {Roo.LayoutRegion} this
34704          */
34705         "expanded" : true,
34706         /**
34707          * @event slideshow
34708          * Fires when this region is slid into view.
34709          * @param {Roo.LayoutRegion} this
34710          */
34711         "slideshow" : true,
34712         /**
34713          * @event slidehide
34714          * Fires when this region slides out of view. 
34715          * @param {Roo.LayoutRegion} this
34716          */
34717         "slidehide" : true,
34718         /**
34719          * @event panelactivated
34720          * Fires when a panel is activated. 
34721          * @param {Roo.LayoutRegion} this
34722          * @param {Roo.ContentPanel} panel The activated panel
34723          */
34724         "panelactivated" : true,
34725         /**
34726          * @event resized
34727          * Fires when the user resizes this region. 
34728          * @param {Roo.LayoutRegion} this
34729          * @param {Number} newSize The new size (width for east/west, height for north/south)
34730          */
34731         "resized" : true
34732     };
34733     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34734     this.panels = new Roo.util.MixedCollection();
34735     this.panels.getKey = this.getPanelId.createDelegate(this);
34736     this.box = null;
34737     this.activePanel = null;
34738     // ensure listeners are added...
34739     
34740     if (config.listeners || config.events) {
34741         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34742             listeners : config.listeners || {},
34743             events : config.events || {}
34744         });
34745     }
34746     
34747     if(skipConfig !== true){
34748         this.applyConfig(config);
34749     }
34750 };
34751
34752 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34753 {
34754     getPanelId : function(p){
34755         return p.getId();
34756     },
34757     
34758     applyConfig : function(config){
34759         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34760         this.config = config;
34761         
34762     },
34763     
34764     /**
34765      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34766      * the width, for horizontal (north, south) the height.
34767      * @param {Number} newSize The new width or height
34768      */
34769     resizeTo : function(newSize){
34770         var el = this.el ? this.el :
34771                  (this.activePanel ? this.activePanel.getEl() : null);
34772         if(el){
34773             switch(this.position){
34774                 case "east":
34775                 case "west":
34776                     el.setWidth(newSize);
34777                     this.fireEvent("resized", this, newSize);
34778                 break;
34779                 case "north":
34780                 case "south":
34781                     el.setHeight(newSize);
34782                     this.fireEvent("resized", this, newSize);
34783                 break;                
34784             }
34785         }
34786     },
34787     
34788     getBox : function(){
34789         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34790     },
34791     
34792     getMargins : function(){
34793         return this.margins;
34794     },
34795     
34796     updateBox : function(box){
34797         this.box = box;
34798         var el = this.activePanel.getEl();
34799         el.dom.style.left = box.x + "px";
34800         el.dom.style.top = box.y + "px";
34801         this.activePanel.setSize(box.width, box.height);
34802     },
34803     
34804     /**
34805      * Returns the container element for this region.
34806      * @return {Roo.Element}
34807      */
34808     getEl : function(){
34809         return this.activePanel;
34810     },
34811     
34812     /**
34813      * Returns true if this region is currently visible.
34814      * @return {Boolean}
34815      */
34816     isVisible : function(){
34817         return this.activePanel ? true : false;
34818     },
34819     
34820     setActivePanel : function(panel){
34821         panel = this.getPanel(panel);
34822         if(this.activePanel && this.activePanel != panel){
34823             this.activePanel.setActiveState(false);
34824             this.activePanel.getEl().setLeftTop(-10000,-10000);
34825         }
34826         this.activePanel = panel;
34827         panel.setActiveState(true);
34828         if(this.box){
34829             panel.setSize(this.box.width, this.box.height);
34830         }
34831         this.fireEvent("panelactivated", this, panel);
34832         this.fireEvent("invalidated");
34833     },
34834     
34835     /**
34836      * Show the specified panel.
34837      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34838      * @return {Roo.ContentPanel} The shown panel or null
34839      */
34840     showPanel : function(panel){
34841         panel = this.getPanel(panel);
34842         if(panel){
34843             this.setActivePanel(panel);
34844         }
34845         return panel;
34846     },
34847     
34848     /**
34849      * Get the active panel for this region.
34850      * @return {Roo.ContentPanel} The active panel or null
34851      */
34852     getActivePanel : function(){
34853         return this.activePanel;
34854     },
34855     
34856     /**
34857      * Add the passed ContentPanel(s)
34858      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34859      * @return {Roo.ContentPanel} The panel added (if only one was added)
34860      */
34861     add : function(panel){
34862         if(arguments.length > 1){
34863             for(var i = 0, len = arguments.length; i < len; i++) {
34864                 this.add(arguments[i]);
34865             }
34866             return null;
34867         }
34868         if(this.hasPanel(panel)){
34869             this.showPanel(panel);
34870             return panel;
34871         }
34872         var el = panel.getEl();
34873         if(el.dom.parentNode != this.mgr.el.dom){
34874             this.mgr.el.dom.appendChild(el.dom);
34875         }
34876         if(panel.setRegion){
34877             panel.setRegion(this);
34878         }
34879         this.panels.add(panel);
34880         el.setStyle("position", "absolute");
34881         if(!panel.background){
34882             this.setActivePanel(panel);
34883             if(this.config.initialSize && this.panels.getCount()==1){
34884                 this.resizeTo(this.config.initialSize);
34885             }
34886         }
34887         this.fireEvent("paneladded", this, panel);
34888         return panel;
34889     },
34890     
34891     /**
34892      * Returns true if the panel is in this region.
34893      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34894      * @return {Boolean}
34895      */
34896     hasPanel : function(panel){
34897         if(typeof panel == "object"){ // must be panel obj
34898             panel = panel.getId();
34899         }
34900         return this.getPanel(panel) ? true : false;
34901     },
34902     
34903     /**
34904      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34905      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34906      * @param {Boolean} preservePanel Overrides the config preservePanel option
34907      * @return {Roo.ContentPanel} The panel that was removed
34908      */
34909     remove : function(panel, preservePanel){
34910         panel = this.getPanel(panel);
34911         if(!panel){
34912             return null;
34913         }
34914         var e = {};
34915         this.fireEvent("beforeremove", this, panel, e);
34916         if(e.cancel === true){
34917             return null;
34918         }
34919         var panelId = panel.getId();
34920         this.panels.removeKey(panelId);
34921         return panel;
34922     },
34923     
34924     /**
34925      * Returns the panel specified or null if it's not in this region.
34926      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34927      * @return {Roo.ContentPanel}
34928      */
34929     getPanel : function(id){
34930         if(typeof id == "object"){ // must be panel obj
34931             return id;
34932         }
34933         return this.panels.get(id);
34934     },
34935     
34936     /**
34937      * Returns this regions position (north/south/east/west/center).
34938      * @return {String} 
34939      */
34940     getPosition: function(){
34941         return this.position;    
34942     }
34943 });/*
34944  * Based on:
34945  * Ext JS Library 1.1.1
34946  * Copyright(c) 2006-2007, Ext JS, LLC.
34947  *
34948  * Originally Released Under LGPL - original licence link has changed is not relivant.
34949  *
34950  * Fork - LGPL
34951  * <script type="text/javascript">
34952  */
34953  
34954 /**
34955  * @class Roo.bootstrap.layout.Region
34956  * @extends Roo.bootstrap.layout.Basic
34957  * This class represents a region in a layout manager.
34958  
34959  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34960  * @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})
34961  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34962  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34963  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34964  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34965  * @cfg {String}    title           The title for the region (overrides panel titles)
34966  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34967  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34968  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34969  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34970  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34971  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34972  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34973  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34974  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34975  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34976
34977  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34978  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34979  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34980  * @cfg {Number}    width           For East/West panels
34981  * @cfg {Number}    height          For North/South panels
34982  * @cfg {Boolean}   split           To show the splitter
34983  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34984  * 
34985  * @cfg {string}   cls             Extra CSS classes to add to region
34986  * 
34987  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34988  * @cfg {string}   region  the region that it inhabits..
34989  *
34990
34991  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34992  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34993
34994  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34995  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34996  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34997  */
34998 Roo.bootstrap.layout.Region = function(config)
34999 {
35000     this.applyConfig(config);
35001
35002     var mgr = config.mgr;
35003     var pos = config.region;
35004     config.skipConfig = true;
35005     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35006     
35007     if (mgr.el) {
35008         this.onRender(mgr.el);   
35009     }
35010      
35011     this.visible = true;
35012     this.collapsed = false;
35013     this.unrendered_panels = [];
35014 };
35015
35016 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35017
35018     position: '', // set by wrapper (eg. north/south etc..)
35019     unrendered_panels : null,  // unrendered panels.
35020     createBody : function(){
35021         /** This region's body element 
35022         * @type Roo.Element */
35023         this.bodyEl = this.el.createChild({
35024                 tag: "div",
35025                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35026         });
35027     },
35028
35029     onRender: function(ctr, pos)
35030     {
35031         var dh = Roo.DomHelper;
35032         /** This region's container element 
35033         * @type Roo.Element */
35034         this.el = dh.append(ctr.dom, {
35035                 tag: "div",
35036                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35037             }, true);
35038         /** This region's title element 
35039         * @type Roo.Element */
35040     
35041         this.titleEl = dh.append(this.el.dom,
35042             {
35043                     tag: "div",
35044                     unselectable: "on",
35045                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35046                     children:[
35047                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35048                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35049                     ]}, true);
35050         
35051         this.titleEl.enableDisplayMode();
35052         /** This region's title text element 
35053         * @type HTMLElement */
35054         this.titleTextEl = this.titleEl.dom.firstChild;
35055         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35056         /*
35057         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35058         this.closeBtn.enableDisplayMode();
35059         this.closeBtn.on("click", this.closeClicked, this);
35060         this.closeBtn.hide();
35061     */
35062         this.createBody(this.config);
35063         if(this.config.hideWhenEmpty){
35064             this.hide();
35065             this.on("paneladded", this.validateVisibility, this);
35066             this.on("panelremoved", this.validateVisibility, this);
35067         }
35068         if(this.autoScroll){
35069             this.bodyEl.setStyle("overflow", "auto");
35070         }else{
35071             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35072         }
35073         //if(c.titlebar !== false){
35074             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35075                 this.titleEl.hide();
35076             }else{
35077                 this.titleEl.show();
35078                 if(this.config.title){
35079                     this.titleTextEl.innerHTML = this.config.title;
35080                 }
35081             }
35082         //}
35083         if(this.config.collapsed){
35084             this.collapse(true);
35085         }
35086         if(this.config.hidden){
35087             this.hide();
35088         }
35089         
35090         if (this.unrendered_panels && this.unrendered_panels.length) {
35091             for (var i =0;i< this.unrendered_panels.length; i++) {
35092                 this.add(this.unrendered_panels[i]);
35093             }
35094             this.unrendered_panels = null;
35095             
35096         }
35097         
35098     },
35099     
35100     applyConfig : function(c)
35101     {
35102         /*
35103          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35104             var dh = Roo.DomHelper;
35105             if(c.titlebar !== false){
35106                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35107                 this.collapseBtn.on("click", this.collapse, this);
35108                 this.collapseBtn.enableDisplayMode();
35109                 /*
35110                 if(c.showPin === true || this.showPin){
35111                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35112                     this.stickBtn.enableDisplayMode();
35113                     this.stickBtn.on("click", this.expand, this);
35114                     this.stickBtn.hide();
35115                 }
35116                 
35117             }
35118             */
35119             /** This region's collapsed element
35120             * @type Roo.Element */
35121             /*
35122              *
35123             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35124                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35125             ]}, true);
35126             
35127             if(c.floatable !== false){
35128                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35129                this.collapsedEl.on("click", this.collapseClick, this);
35130             }
35131
35132             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35133                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35134                    id: "message", unselectable: "on", style:{"float":"left"}});
35135                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35136              }
35137             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35138             this.expandBtn.on("click", this.expand, this);
35139             
35140         }
35141         
35142         if(this.collapseBtn){
35143             this.collapseBtn.setVisible(c.collapsible == true);
35144         }
35145         
35146         this.cmargins = c.cmargins || this.cmargins ||
35147                          (this.position == "west" || this.position == "east" ?
35148                              {top: 0, left: 2, right:2, bottom: 0} :
35149                              {top: 2, left: 0, right:0, bottom: 2});
35150         */
35151         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35152         
35153         
35154         this.bottomTabs = c.tabPosition != "top";
35155         
35156         this.autoScroll = c.autoScroll || false;
35157         
35158         
35159        
35160         
35161         this.duration = c.duration || .30;
35162         this.slideDuration = c.slideDuration || .45;
35163         this.config = c;
35164        
35165     },
35166     /**
35167      * Returns true if this region is currently visible.
35168      * @return {Boolean}
35169      */
35170     isVisible : function(){
35171         return this.visible;
35172     },
35173
35174     /**
35175      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35176      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35177      */
35178     //setCollapsedTitle : function(title){
35179     //    title = title || "&#160;";
35180      //   if(this.collapsedTitleTextEl){
35181       //      this.collapsedTitleTextEl.innerHTML = title;
35182        // }
35183     //},
35184
35185     getBox : function(){
35186         var b;
35187       //  if(!this.collapsed){
35188             b = this.el.getBox(false, true);
35189        // }else{
35190           //  b = this.collapsedEl.getBox(false, true);
35191         //}
35192         return b;
35193     },
35194
35195     getMargins : function(){
35196         return this.margins;
35197         //return this.collapsed ? this.cmargins : this.margins;
35198     },
35199 /*
35200     highlight : function(){
35201         this.el.addClass("x-layout-panel-dragover");
35202     },
35203
35204     unhighlight : function(){
35205         this.el.removeClass("x-layout-panel-dragover");
35206     },
35207 */
35208     updateBox : function(box)
35209     {
35210         if (!this.bodyEl) {
35211             return; // not rendered yet..
35212         }
35213         
35214         this.box = box;
35215         if(!this.collapsed){
35216             this.el.dom.style.left = box.x + "px";
35217             this.el.dom.style.top = box.y + "px";
35218             this.updateBody(box.width, box.height);
35219         }else{
35220             this.collapsedEl.dom.style.left = box.x + "px";
35221             this.collapsedEl.dom.style.top = box.y + "px";
35222             this.collapsedEl.setSize(box.width, box.height);
35223         }
35224         if(this.tabs){
35225             this.tabs.autoSizeTabs();
35226         }
35227     },
35228
35229     updateBody : function(w, h)
35230     {
35231         if(w !== null){
35232             this.el.setWidth(w);
35233             w -= this.el.getBorderWidth("rl");
35234             if(this.config.adjustments){
35235                 w += this.config.adjustments[0];
35236             }
35237         }
35238         if(h !== null && h > 0){
35239             this.el.setHeight(h);
35240             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35241             h -= this.el.getBorderWidth("tb");
35242             if(this.config.adjustments){
35243                 h += this.config.adjustments[1];
35244             }
35245             this.bodyEl.setHeight(h);
35246             if(this.tabs){
35247                 h = this.tabs.syncHeight(h);
35248             }
35249         }
35250         if(this.panelSize){
35251             w = w !== null ? w : this.panelSize.width;
35252             h = h !== null ? h : this.panelSize.height;
35253         }
35254         if(this.activePanel){
35255             var el = this.activePanel.getEl();
35256             w = w !== null ? w : el.getWidth();
35257             h = h !== null ? h : el.getHeight();
35258             this.panelSize = {width: w, height: h};
35259             this.activePanel.setSize(w, h);
35260         }
35261         if(Roo.isIE && this.tabs){
35262             this.tabs.el.repaint();
35263         }
35264     },
35265
35266     /**
35267      * Returns the container element for this region.
35268      * @return {Roo.Element}
35269      */
35270     getEl : function(){
35271         return this.el;
35272     },
35273
35274     /**
35275      * Hides this region.
35276      */
35277     hide : function(){
35278         //if(!this.collapsed){
35279             this.el.dom.style.left = "-2000px";
35280             this.el.hide();
35281         //}else{
35282          //   this.collapsedEl.dom.style.left = "-2000px";
35283          //   this.collapsedEl.hide();
35284        // }
35285         this.visible = false;
35286         this.fireEvent("visibilitychange", this, false);
35287     },
35288
35289     /**
35290      * Shows this region if it was previously hidden.
35291      */
35292     show : function(){
35293         //if(!this.collapsed){
35294             this.el.show();
35295         //}else{
35296         //    this.collapsedEl.show();
35297        // }
35298         this.visible = true;
35299         this.fireEvent("visibilitychange", this, true);
35300     },
35301 /*
35302     closeClicked : function(){
35303         if(this.activePanel){
35304             this.remove(this.activePanel);
35305         }
35306     },
35307
35308     collapseClick : function(e){
35309         if(this.isSlid){
35310            e.stopPropagation();
35311            this.slideIn();
35312         }else{
35313            e.stopPropagation();
35314            this.slideOut();
35315         }
35316     },
35317 */
35318     /**
35319      * Collapses this region.
35320      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35321      */
35322     /*
35323     collapse : function(skipAnim, skipCheck = false){
35324         if(this.collapsed) {
35325             return;
35326         }
35327         
35328         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35329             
35330             this.collapsed = true;
35331             if(this.split){
35332                 this.split.el.hide();
35333             }
35334             if(this.config.animate && skipAnim !== true){
35335                 this.fireEvent("invalidated", this);
35336                 this.animateCollapse();
35337             }else{
35338                 this.el.setLocation(-20000,-20000);
35339                 this.el.hide();
35340                 this.collapsedEl.show();
35341                 this.fireEvent("collapsed", this);
35342                 this.fireEvent("invalidated", this);
35343             }
35344         }
35345         
35346     },
35347 */
35348     animateCollapse : function(){
35349         // overridden
35350     },
35351
35352     /**
35353      * Expands this region if it was previously collapsed.
35354      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35355      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35356      */
35357     /*
35358     expand : function(e, skipAnim){
35359         if(e) {
35360             e.stopPropagation();
35361         }
35362         if(!this.collapsed || this.el.hasActiveFx()) {
35363             return;
35364         }
35365         if(this.isSlid){
35366             this.afterSlideIn();
35367             skipAnim = true;
35368         }
35369         this.collapsed = false;
35370         if(this.config.animate && skipAnim !== true){
35371             this.animateExpand();
35372         }else{
35373             this.el.show();
35374             if(this.split){
35375                 this.split.el.show();
35376             }
35377             this.collapsedEl.setLocation(-2000,-2000);
35378             this.collapsedEl.hide();
35379             this.fireEvent("invalidated", this);
35380             this.fireEvent("expanded", this);
35381         }
35382     },
35383 */
35384     animateExpand : function(){
35385         // overridden
35386     },
35387
35388     initTabs : function()
35389     {
35390         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35391         
35392         var ts = new Roo.bootstrap.panel.Tabs({
35393                 el: this.bodyEl.dom,
35394                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35395                 disableTooltips: this.config.disableTabTips,
35396                 toolbar : this.config.toolbar
35397             });
35398         
35399         if(this.config.hideTabs){
35400             ts.stripWrap.setDisplayed(false);
35401         }
35402         this.tabs = ts;
35403         ts.resizeTabs = this.config.resizeTabs === true;
35404         ts.minTabWidth = this.config.minTabWidth || 40;
35405         ts.maxTabWidth = this.config.maxTabWidth || 250;
35406         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35407         ts.monitorResize = false;
35408         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35409         ts.bodyEl.addClass('roo-layout-tabs-body');
35410         this.panels.each(this.initPanelAsTab, this);
35411     },
35412
35413     initPanelAsTab : function(panel){
35414         var ti = this.tabs.addTab(
35415             panel.getEl().id,
35416             panel.getTitle(),
35417             null,
35418             this.config.closeOnTab && panel.isClosable(),
35419             panel.tpl
35420         );
35421         if(panel.tabTip !== undefined){
35422             ti.setTooltip(panel.tabTip);
35423         }
35424         ti.on("activate", function(){
35425               this.setActivePanel(panel);
35426         }, this);
35427         
35428         if(this.config.closeOnTab){
35429             ti.on("beforeclose", function(t, e){
35430                 e.cancel = true;
35431                 this.remove(panel);
35432             }, this);
35433         }
35434         
35435         panel.tabItem = ti;
35436         
35437         return ti;
35438     },
35439
35440     updatePanelTitle : function(panel, title)
35441     {
35442         if(this.activePanel == panel){
35443             this.updateTitle(title);
35444         }
35445         if(this.tabs){
35446             var ti = this.tabs.getTab(panel.getEl().id);
35447             ti.setText(title);
35448             if(panel.tabTip !== undefined){
35449                 ti.setTooltip(panel.tabTip);
35450             }
35451         }
35452     },
35453
35454     updateTitle : function(title){
35455         if(this.titleTextEl && !this.config.title){
35456             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35457         }
35458     },
35459
35460     setActivePanel : function(panel)
35461     {
35462         panel = this.getPanel(panel);
35463         if(this.activePanel && this.activePanel != panel){
35464             if(this.activePanel.setActiveState(false) === false){
35465                 return;
35466             }
35467         }
35468         this.activePanel = panel;
35469         panel.setActiveState(true);
35470         if(this.panelSize){
35471             panel.setSize(this.panelSize.width, this.panelSize.height);
35472         }
35473         if(this.closeBtn){
35474             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35475         }
35476         this.updateTitle(panel.getTitle());
35477         if(this.tabs){
35478             this.fireEvent("invalidated", this);
35479         }
35480         this.fireEvent("panelactivated", this, panel);
35481     },
35482
35483     /**
35484      * Shows the specified panel.
35485      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35486      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35487      */
35488     showPanel : function(panel)
35489     {
35490         panel = this.getPanel(panel);
35491         if(panel){
35492             if(this.tabs){
35493                 var tab = this.tabs.getTab(panel.getEl().id);
35494                 if(tab.isHidden()){
35495                     this.tabs.unhideTab(tab.id);
35496                 }
35497                 tab.activate();
35498             }else{
35499                 this.setActivePanel(panel);
35500             }
35501         }
35502         return panel;
35503     },
35504
35505     /**
35506      * Get the active panel for this region.
35507      * @return {Roo.ContentPanel} The active panel or null
35508      */
35509     getActivePanel : function(){
35510         return this.activePanel;
35511     },
35512
35513     validateVisibility : function(){
35514         if(this.panels.getCount() < 1){
35515             this.updateTitle("&#160;");
35516             this.closeBtn.hide();
35517             this.hide();
35518         }else{
35519             if(!this.isVisible()){
35520                 this.show();
35521             }
35522         }
35523     },
35524
35525     /**
35526      * Adds the passed ContentPanel(s) to this region.
35527      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35528      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35529      */
35530     add : function(panel)
35531     {
35532         if(arguments.length > 1){
35533             for(var i = 0, len = arguments.length; i < len; i++) {
35534                 this.add(arguments[i]);
35535             }
35536             return null;
35537         }
35538         
35539         // if we have not been rendered yet, then we can not really do much of this..
35540         if (!this.bodyEl) {
35541             this.unrendered_panels.push(panel);
35542             return panel;
35543         }
35544         
35545         
35546         
35547         
35548         if(this.hasPanel(panel)){
35549             this.showPanel(panel);
35550             return panel;
35551         }
35552         panel.setRegion(this);
35553         this.panels.add(panel);
35554        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35555             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35556             // and hide them... ???
35557             this.bodyEl.dom.appendChild(panel.getEl().dom);
35558             if(panel.background !== true){
35559                 this.setActivePanel(panel);
35560             }
35561             this.fireEvent("paneladded", this, panel);
35562             return panel;
35563         }
35564         */
35565         if(!this.tabs){
35566             this.initTabs();
35567         }else{
35568             this.initPanelAsTab(panel);
35569         }
35570         
35571         
35572         if(panel.background !== true){
35573             this.tabs.activate(panel.getEl().id);
35574         }
35575         this.fireEvent("paneladded", this, panel);
35576         return panel;
35577     },
35578
35579     /**
35580      * Hides the tab for the specified panel.
35581      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35582      */
35583     hidePanel : function(panel){
35584         if(this.tabs && (panel = this.getPanel(panel))){
35585             this.tabs.hideTab(panel.getEl().id);
35586         }
35587     },
35588
35589     /**
35590      * Unhides the tab for a previously hidden panel.
35591      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35592      */
35593     unhidePanel : function(panel){
35594         if(this.tabs && (panel = this.getPanel(panel))){
35595             this.tabs.unhideTab(panel.getEl().id);
35596         }
35597     },
35598
35599     clearPanels : function(){
35600         while(this.panels.getCount() > 0){
35601              this.remove(this.panels.first());
35602         }
35603     },
35604
35605     /**
35606      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35607      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35608      * @param {Boolean} preservePanel Overrides the config preservePanel option
35609      * @return {Roo.ContentPanel} The panel that was removed
35610      */
35611     remove : function(panel, preservePanel)
35612     {
35613         panel = this.getPanel(panel);
35614         if(!panel){
35615             return null;
35616         }
35617         var e = {};
35618         this.fireEvent("beforeremove", this, panel, e);
35619         if(e.cancel === true){
35620             return null;
35621         }
35622         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35623         var panelId = panel.getId();
35624         this.panels.removeKey(panelId);
35625         if(preservePanel){
35626             document.body.appendChild(panel.getEl().dom);
35627         }
35628         if(this.tabs){
35629             this.tabs.removeTab(panel.getEl().id);
35630         }else if (!preservePanel){
35631             this.bodyEl.dom.removeChild(panel.getEl().dom);
35632         }
35633         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35634             var p = this.panels.first();
35635             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35636             tempEl.appendChild(p.getEl().dom);
35637             this.bodyEl.update("");
35638             this.bodyEl.dom.appendChild(p.getEl().dom);
35639             tempEl = null;
35640             this.updateTitle(p.getTitle());
35641             this.tabs = null;
35642             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35643             this.setActivePanel(p);
35644         }
35645         panel.setRegion(null);
35646         if(this.activePanel == panel){
35647             this.activePanel = null;
35648         }
35649         if(this.config.autoDestroy !== false && preservePanel !== true){
35650             try{panel.destroy();}catch(e){}
35651         }
35652         this.fireEvent("panelremoved", this, panel);
35653         return panel;
35654     },
35655
35656     /**
35657      * Returns the TabPanel component used by this region
35658      * @return {Roo.TabPanel}
35659      */
35660     getTabs : function(){
35661         return this.tabs;
35662     },
35663
35664     createTool : function(parentEl, className){
35665         var btn = Roo.DomHelper.append(parentEl, {
35666             tag: "div",
35667             cls: "x-layout-tools-button",
35668             children: [ {
35669                 tag: "div",
35670                 cls: "roo-layout-tools-button-inner " + className,
35671                 html: "&#160;"
35672             }]
35673         }, true);
35674         btn.addClassOnOver("roo-layout-tools-button-over");
35675         return btn;
35676     }
35677 });/*
35678  * Based on:
35679  * Ext JS Library 1.1.1
35680  * Copyright(c) 2006-2007, Ext JS, LLC.
35681  *
35682  * Originally Released Under LGPL - original licence link has changed is not relivant.
35683  *
35684  * Fork - LGPL
35685  * <script type="text/javascript">
35686  */
35687  
35688
35689
35690 /**
35691  * @class Roo.SplitLayoutRegion
35692  * @extends Roo.LayoutRegion
35693  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35694  */
35695 Roo.bootstrap.layout.Split = function(config){
35696     this.cursor = config.cursor;
35697     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35698 };
35699
35700 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35701 {
35702     splitTip : "Drag to resize.",
35703     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35704     useSplitTips : false,
35705
35706     applyConfig : function(config){
35707         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35708     },
35709     
35710     onRender : function(ctr,pos) {
35711         
35712         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35713         if(!this.config.split){
35714             return;
35715         }
35716         if(!this.split){
35717             
35718             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35719                             tag: "div",
35720                             id: this.el.id + "-split",
35721                             cls: "roo-layout-split roo-layout-split-"+this.position,
35722                             html: "&#160;"
35723             });
35724             /** The SplitBar for this region 
35725             * @type Roo.SplitBar */
35726             // does not exist yet...
35727             Roo.log([this.position, this.orientation]);
35728             
35729             this.split = new Roo.bootstrap.SplitBar({
35730                 dragElement : splitEl,
35731                 resizingElement: this.el,
35732                 orientation : this.orientation
35733             });
35734             
35735             this.split.on("moved", this.onSplitMove, this);
35736             this.split.useShim = this.config.useShim === true;
35737             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35738             if(this.useSplitTips){
35739                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35740             }
35741             //if(config.collapsible){
35742             //    this.split.el.on("dblclick", this.collapse,  this);
35743             //}
35744         }
35745         if(typeof this.config.minSize != "undefined"){
35746             this.split.minSize = this.config.minSize;
35747         }
35748         if(typeof this.config.maxSize != "undefined"){
35749             this.split.maxSize = this.config.maxSize;
35750         }
35751         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35752             this.hideSplitter();
35753         }
35754         
35755     },
35756
35757     getHMaxSize : function(){
35758          var cmax = this.config.maxSize || 10000;
35759          var center = this.mgr.getRegion("center");
35760          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35761     },
35762
35763     getVMaxSize : function(){
35764          var cmax = this.config.maxSize || 10000;
35765          var center = this.mgr.getRegion("center");
35766          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35767     },
35768
35769     onSplitMove : function(split, newSize){
35770         this.fireEvent("resized", this, newSize);
35771     },
35772     
35773     /** 
35774      * Returns the {@link Roo.SplitBar} for this region.
35775      * @return {Roo.SplitBar}
35776      */
35777     getSplitBar : function(){
35778         return this.split;
35779     },
35780     
35781     hide : function(){
35782         this.hideSplitter();
35783         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35784     },
35785
35786     hideSplitter : function(){
35787         if(this.split){
35788             this.split.el.setLocation(-2000,-2000);
35789             this.split.el.hide();
35790         }
35791     },
35792
35793     show : function(){
35794         if(this.split){
35795             this.split.el.show();
35796         }
35797         Roo.bootstrap.layout.Split.superclass.show.call(this);
35798     },
35799     
35800     beforeSlide: function(){
35801         if(Roo.isGecko){// firefox overflow auto bug workaround
35802             this.bodyEl.clip();
35803             if(this.tabs) {
35804                 this.tabs.bodyEl.clip();
35805             }
35806             if(this.activePanel){
35807                 this.activePanel.getEl().clip();
35808                 
35809                 if(this.activePanel.beforeSlide){
35810                     this.activePanel.beforeSlide();
35811                 }
35812             }
35813         }
35814     },
35815     
35816     afterSlide : function(){
35817         if(Roo.isGecko){// firefox overflow auto bug workaround
35818             this.bodyEl.unclip();
35819             if(this.tabs) {
35820                 this.tabs.bodyEl.unclip();
35821             }
35822             if(this.activePanel){
35823                 this.activePanel.getEl().unclip();
35824                 if(this.activePanel.afterSlide){
35825                     this.activePanel.afterSlide();
35826                 }
35827             }
35828         }
35829     },
35830
35831     initAutoHide : function(){
35832         if(this.autoHide !== false){
35833             if(!this.autoHideHd){
35834                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35835                 this.autoHideHd = {
35836                     "mouseout": function(e){
35837                         if(!e.within(this.el, true)){
35838                             st.delay(500);
35839                         }
35840                     },
35841                     "mouseover" : function(e){
35842                         st.cancel();
35843                     },
35844                     scope : this
35845                 };
35846             }
35847             this.el.on(this.autoHideHd);
35848         }
35849     },
35850
35851     clearAutoHide : function(){
35852         if(this.autoHide !== false){
35853             this.el.un("mouseout", this.autoHideHd.mouseout);
35854             this.el.un("mouseover", this.autoHideHd.mouseover);
35855         }
35856     },
35857
35858     clearMonitor : function(){
35859         Roo.get(document).un("click", this.slideInIf, this);
35860     },
35861
35862     // these names are backwards but not changed for compat
35863     slideOut : function(){
35864         if(this.isSlid || this.el.hasActiveFx()){
35865             return;
35866         }
35867         this.isSlid = true;
35868         if(this.collapseBtn){
35869             this.collapseBtn.hide();
35870         }
35871         this.closeBtnState = this.closeBtn.getStyle('display');
35872         this.closeBtn.hide();
35873         if(this.stickBtn){
35874             this.stickBtn.show();
35875         }
35876         this.el.show();
35877         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35878         this.beforeSlide();
35879         this.el.setStyle("z-index", 10001);
35880         this.el.slideIn(this.getSlideAnchor(), {
35881             callback: function(){
35882                 this.afterSlide();
35883                 this.initAutoHide();
35884                 Roo.get(document).on("click", this.slideInIf, this);
35885                 this.fireEvent("slideshow", this);
35886             },
35887             scope: this,
35888             block: true
35889         });
35890     },
35891
35892     afterSlideIn : function(){
35893         this.clearAutoHide();
35894         this.isSlid = false;
35895         this.clearMonitor();
35896         this.el.setStyle("z-index", "");
35897         if(this.collapseBtn){
35898             this.collapseBtn.show();
35899         }
35900         this.closeBtn.setStyle('display', this.closeBtnState);
35901         if(this.stickBtn){
35902             this.stickBtn.hide();
35903         }
35904         this.fireEvent("slidehide", this);
35905     },
35906
35907     slideIn : function(cb){
35908         if(!this.isSlid || this.el.hasActiveFx()){
35909             Roo.callback(cb);
35910             return;
35911         }
35912         this.isSlid = false;
35913         this.beforeSlide();
35914         this.el.slideOut(this.getSlideAnchor(), {
35915             callback: function(){
35916                 this.el.setLeftTop(-10000, -10000);
35917                 this.afterSlide();
35918                 this.afterSlideIn();
35919                 Roo.callback(cb);
35920             },
35921             scope: this,
35922             block: true
35923         });
35924     },
35925     
35926     slideInIf : function(e){
35927         if(!e.within(this.el)){
35928             this.slideIn();
35929         }
35930     },
35931
35932     animateCollapse : function(){
35933         this.beforeSlide();
35934         this.el.setStyle("z-index", 20000);
35935         var anchor = this.getSlideAnchor();
35936         this.el.slideOut(anchor, {
35937             callback : function(){
35938                 this.el.setStyle("z-index", "");
35939                 this.collapsedEl.slideIn(anchor, {duration:.3});
35940                 this.afterSlide();
35941                 this.el.setLocation(-10000,-10000);
35942                 this.el.hide();
35943                 this.fireEvent("collapsed", this);
35944             },
35945             scope: this,
35946             block: true
35947         });
35948     },
35949
35950     animateExpand : function(){
35951         this.beforeSlide();
35952         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35953         this.el.setStyle("z-index", 20000);
35954         this.collapsedEl.hide({
35955             duration:.1
35956         });
35957         this.el.slideIn(this.getSlideAnchor(), {
35958             callback : function(){
35959                 this.el.setStyle("z-index", "");
35960                 this.afterSlide();
35961                 if(this.split){
35962                     this.split.el.show();
35963                 }
35964                 this.fireEvent("invalidated", this);
35965                 this.fireEvent("expanded", this);
35966             },
35967             scope: this,
35968             block: true
35969         });
35970     },
35971
35972     anchors : {
35973         "west" : "left",
35974         "east" : "right",
35975         "north" : "top",
35976         "south" : "bottom"
35977     },
35978
35979     sanchors : {
35980         "west" : "l",
35981         "east" : "r",
35982         "north" : "t",
35983         "south" : "b"
35984     },
35985
35986     canchors : {
35987         "west" : "tl-tr",
35988         "east" : "tr-tl",
35989         "north" : "tl-bl",
35990         "south" : "bl-tl"
35991     },
35992
35993     getAnchor : function(){
35994         return this.anchors[this.position];
35995     },
35996
35997     getCollapseAnchor : function(){
35998         return this.canchors[this.position];
35999     },
36000
36001     getSlideAnchor : function(){
36002         return this.sanchors[this.position];
36003     },
36004
36005     getAlignAdj : function(){
36006         var cm = this.cmargins;
36007         switch(this.position){
36008             case "west":
36009                 return [0, 0];
36010             break;
36011             case "east":
36012                 return [0, 0];
36013             break;
36014             case "north":
36015                 return [0, 0];
36016             break;
36017             case "south":
36018                 return [0, 0];
36019             break;
36020         }
36021     },
36022
36023     getExpandAdj : function(){
36024         var c = this.collapsedEl, cm = this.cmargins;
36025         switch(this.position){
36026             case "west":
36027                 return [-(cm.right+c.getWidth()+cm.left), 0];
36028             break;
36029             case "east":
36030                 return [cm.right+c.getWidth()+cm.left, 0];
36031             break;
36032             case "north":
36033                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36034             break;
36035             case "south":
36036                 return [0, cm.top+cm.bottom+c.getHeight()];
36037             break;
36038         }
36039     }
36040 });/*
36041  * Based on:
36042  * Ext JS Library 1.1.1
36043  * Copyright(c) 2006-2007, Ext JS, LLC.
36044  *
36045  * Originally Released Under LGPL - original licence link has changed is not relivant.
36046  *
36047  * Fork - LGPL
36048  * <script type="text/javascript">
36049  */
36050 /*
36051  * These classes are private internal classes
36052  */
36053 Roo.bootstrap.layout.Center = function(config){
36054     config.region = "center";
36055     Roo.bootstrap.layout.Region.call(this, config);
36056     this.visible = true;
36057     this.minWidth = config.minWidth || 20;
36058     this.minHeight = config.minHeight || 20;
36059 };
36060
36061 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36062     hide : function(){
36063         // center panel can't be hidden
36064     },
36065     
36066     show : function(){
36067         // center panel can't be hidden
36068     },
36069     
36070     getMinWidth: function(){
36071         return this.minWidth;
36072     },
36073     
36074     getMinHeight: function(){
36075         return this.minHeight;
36076     }
36077 });
36078
36079
36080
36081
36082  
36083
36084
36085
36086
36087
36088 Roo.bootstrap.layout.North = function(config)
36089 {
36090     config.region = 'north';
36091     config.cursor = 'n-resize';
36092     
36093     Roo.bootstrap.layout.Split.call(this, config);
36094     
36095     
36096     if(this.split){
36097         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36098         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36099         this.split.el.addClass("roo-layout-split-v");
36100     }
36101     var size = config.initialSize || config.height;
36102     if(typeof size != "undefined"){
36103         this.el.setHeight(size);
36104     }
36105 };
36106 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36107 {
36108     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36109     
36110     
36111     
36112     getBox : function(){
36113         if(this.collapsed){
36114             return this.collapsedEl.getBox();
36115         }
36116         var box = this.el.getBox();
36117         if(this.split){
36118             box.height += this.split.el.getHeight();
36119         }
36120         return box;
36121     },
36122     
36123     updateBox : function(box){
36124         if(this.split && !this.collapsed){
36125             box.height -= this.split.el.getHeight();
36126             this.split.el.setLeft(box.x);
36127             this.split.el.setTop(box.y+box.height);
36128             this.split.el.setWidth(box.width);
36129         }
36130         if(this.collapsed){
36131             this.updateBody(box.width, null);
36132         }
36133         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36134     }
36135 });
36136
36137
36138
36139
36140
36141 Roo.bootstrap.layout.South = function(config){
36142     config.region = 'south';
36143     config.cursor = 's-resize';
36144     Roo.bootstrap.layout.Split.call(this, config);
36145     if(this.split){
36146         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36147         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36148         this.split.el.addClass("roo-layout-split-v");
36149     }
36150     var size = config.initialSize || config.height;
36151     if(typeof size != "undefined"){
36152         this.el.setHeight(size);
36153     }
36154 };
36155
36156 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36157     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36158     getBox : function(){
36159         if(this.collapsed){
36160             return this.collapsedEl.getBox();
36161         }
36162         var box = this.el.getBox();
36163         if(this.split){
36164             var sh = this.split.el.getHeight();
36165             box.height += sh;
36166             box.y -= sh;
36167         }
36168         return box;
36169     },
36170     
36171     updateBox : function(box){
36172         if(this.split && !this.collapsed){
36173             var sh = this.split.el.getHeight();
36174             box.height -= sh;
36175             box.y += sh;
36176             this.split.el.setLeft(box.x);
36177             this.split.el.setTop(box.y-sh);
36178             this.split.el.setWidth(box.width);
36179         }
36180         if(this.collapsed){
36181             this.updateBody(box.width, null);
36182         }
36183         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36184     }
36185 });
36186
36187 Roo.bootstrap.layout.East = function(config){
36188     config.region = "east";
36189     config.cursor = "e-resize";
36190     Roo.bootstrap.layout.Split.call(this, config);
36191     if(this.split){
36192         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36193         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36194         this.split.el.addClass("roo-layout-split-h");
36195     }
36196     var size = config.initialSize || config.width;
36197     if(typeof size != "undefined"){
36198         this.el.setWidth(size);
36199     }
36200 };
36201 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36202     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36203     getBox : function(){
36204         if(this.collapsed){
36205             return this.collapsedEl.getBox();
36206         }
36207         var box = this.el.getBox();
36208         if(this.split){
36209             var sw = this.split.el.getWidth();
36210             box.width += sw;
36211             box.x -= sw;
36212         }
36213         return box;
36214     },
36215
36216     updateBox : function(box){
36217         if(this.split && !this.collapsed){
36218             var sw = this.split.el.getWidth();
36219             box.width -= sw;
36220             this.split.el.setLeft(box.x);
36221             this.split.el.setTop(box.y);
36222             this.split.el.setHeight(box.height);
36223             box.x += sw;
36224         }
36225         if(this.collapsed){
36226             this.updateBody(null, box.height);
36227         }
36228         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36229     }
36230 });
36231
36232 Roo.bootstrap.layout.West = function(config){
36233     config.region = "west";
36234     config.cursor = "w-resize";
36235     
36236     Roo.bootstrap.layout.Split.call(this, config);
36237     if(this.split){
36238         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36239         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36240         this.split.el.addClass("roo-layout-split-h");
36241     }
36242     
36243 };
36244 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36245     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36246     
36247     onRender: function(ctr, pos)
36248     {
36249         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36250         var size = this.config.initialSize || this.config.width;
36251         if(typeof size != "undefined"){
36252             this.el.setWidth(size);
36253         }
36254     },
36255     
36256     getBox : function(){
36257         if(this.collapsed){
36258             return this.collapsedEl.getBox();
36259         }
36260         var box = this.el.getBox();
36261         if(this.split){
36262             box.width += this.split.el.getWidth();
36263         }
36264         return box;
36265     },
36266     
36267     updateBox : function(box){
36268         if(this.split && !this.collapsed){
36269             var sw = this.split.el.getWidth();
36270             box.width -= sw;
36271             this.split.el.setLeft(box.x+box.width);
36272             this.split.el.setTop(box.y);
36273             this.split.el.setHeight(box.height);
36274         }
36275         if(this.collapsed){
36276             this.updateBody(null, box.height);
36277         }
36278         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36279     }
36280 });
36281 Roo.namespace("Roo.bootstrap.panel");/*
36282  * Based on:
36283  * Ext JS Library 1.1.1
36284  * Copyright(c) 2006-2007, Ext JS, LLC.
36285  *
36286  * Originally Released Under LGPL - original licence link has changed is not relivant.
36287  *
36288  * Fork - LGPL
36289  * <script type="text/javascript">
36290  */
36291 /**
36292  * @class Roo.ContentPanel
36293  * @extends Roo.util.Observable
36294  * A basic ContentPanel element.
36295  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36296  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36297  * @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
36298  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36299  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36300  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36301  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36302  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36303  * @cfg {String} title          The title for this panel
36304  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36305  * @cfg {String} url            Calls {@link #setUrl} with this value
36306  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36307  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36308  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36309  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36310  * @cfg {Boolean} badges render the badges
36311
36312  * @constructor
36313  * Create a new ContentPanel.
36314  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36315  * @param {String/Object} config A string to set only the title or a config object
36316  * @param {String} content (optional) Set the HTML content for this panel
36317  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36318  */
36319 Roo.bootstrap.panel.Content = function( config){
36320     
36321     this.tpl = config.tpl || false;
36322     
36323     var el = config.el;
36324     var content = config.content;
36325
36326     if(config.autoCreate){ // xtype is available if this is called from factory
36327         el = Roo.id();
36328     }
36329     this.el = Roo.get(el);
36330     if(!this.el && config && config.autoCreate){
36331         if(typeof config.autoCreate == "object"){
36332             if(!config.autoCreate.id){
36333                 config.autoCreate.id = config.id||el;
36334             }
36335             this.el = Roo.DomHelper.append(document.body,
36336                         config.autoCreate, true);
36337         }else{
36338             var elcfg =  {   tag: "div",
36339                             cls: "roo-layout-inactive-content",
36340                             id: config.id||el
36341                             };
36342             if (config.html) {
36343                 elcfg.html = config.html;
36344                 
36345             }
36346                         
36347             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36348         }
36349     } 
36350     this.closable = false;
36351     this.loaded = false;
36352     this.active = false;
36353    
36354       
36355     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36356         
36357         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36358         
36359         this.wrapEl = this.el; //this.el.wrap();
36360         var ti = [];
36361         if (config.toolbar.items) {
36362             ti = config.toolbar.items ;
36363             delete config.toolbar.items ;
36364         }
36365         
36366         var nitems = [];
36367         this.toolbar.render(this.wrapEl, 'before');
36368         for(var i =0;i < ti.length;i++) {
36369           //  Roo.log(['add child', items[i]]);
36370             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36371         }
36372         this.toolbar.items = nitems;
36373         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36374         delete config.toolbar;
36375         
36376     }
36377     /*
36378     // xtype created footer. - not sure if will work as we normally have to render first..
36379     if (this.footer && !this.footer.el && this.footer.xtype) {
36380         if (!this.wrapEl) {
36381             this.wrapEl = this.el.wrap();
36382         }
36383     
36384         this.footer.container = this.wrapEl.createChild();
36385          
36386         this.footer = Roo.factory(this.footer, Roo);
36387         
36388     }
36389     */
36390     
36391      if(typeof config == "string"){
36392         this.title = config;
36393     }else{
36394         Roo.apply(this, config);
36395     }
36396     
36397     if(this.resizeEl){
36398         this.resizeEl = Roo.get(this.resizeEl, true);
36399     }else{
36400         this.resizeEl = this.el;
36401     }
36402     // handle view.xtype
36403     
36404  
36405     
36406     
36407     this.addEvents({
36408         /**
36409          * @event activate
36410          * Fires when this panel is activated. 
36411          * @param {Roo.ContentPanel} this
36412          */
36413         "activate" : true,
36414         /**
36415          * @event deactivate
36416          * Fires when this panel is activated. 
36417          * @param {Roo.ContentPanel} this
36418          */
36419         "deactivate" : true,
36420
36421         /**
36422          * @event resize
36423          * Fires when this panel is resized if fitToFrame is true.
36424          * @param {Roo.ContentPanel} this
36425          * @param {Number} width The width after any component adjustments
36426          * @param {Number} height The height after any component adjustments
36427          */
36428         "resize" : true,
36429         
36430          /**
36431          * @event render
36432          * Fires when this tab is created
36433          * @param {Roo.ContentPanel} this
36434          */
36435         "render" : true
36436         
36437         
36438         
36439     });
36440     
36441
36442     
36443     
36444     if(this.autoScroll){
36445         this.resizeEl.setStyle("overflow", "auto");
36446     } else {
36447         // fix randome scrolling
36448         //this.el.on('scroll', function() {
36449         //    Roo.log('fix random scolling');
36450         //    this.scrollTo('top',0); 
36451         //});
36452     }
36453     content = content || this.content;
36454     if(content){
36455         this.setContent(content);
36456     }
36457     if(config && config.url){
36458         this.setUrl(this.url, this.params, this.loadOnce);
36459     }
36460     
36461     
36462     
36463     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36464     
36465     if (this.view && typeof(this.view.xtype) != 'undefined') {
36466         this.view.el = this.el.appendChild(document.createElement("div"));
36467         this.view = Roo.factory(this.view); 
36468         this.view.render  &&  this.view.render(false, '');  
36469     }
36470     
36471     
36472     this.fireEvent('render', this);
36473 };
36474
36475 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36476     
36477     tabTip : '',
36478     
36479     setRegion : function(region){
36480         this.region = region;
36481         this.setActiveClass(region && !this.background);
36482     },
36483     
36484     
36485     setActiveClass: function(state)
36486     {
36487         if(state){
36488            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36489            this.el.setStyle('position','relative');
36490         }else{
36491            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36492            this.el.setStyle('position', 'absolute');
36493         } 
36494     },
36495     
36496     /**
36497      * Returns the toolbar for this Panel if one was configured. 
36498      * @return {Roo.Toolbar} 
36499      */
36500     getToolbar : function(){
36501         return this.toolbar;
36502     },
36503     
36504     setActiveState : function(active)
36505     {
36506         this.active = active;
36507         this.setActiveClass(active);
36508         if(!active){
36509             if(this.fireEvent("deactivate", this) === false){
36510                 return false;
36511             }
36512             return true;
36513         }
36514         this.fireEvent("activate", this);
36515         return true;
36516     },
36517     /**
36518      * Updates this panel's element
36519      * @param {String} content The new content
36520      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36521     */
36522     setContent : function(content, loadScripts){
36523         this.el.update(content, loadScripts);
36524     },
36525
36526     ignoreResize : function(w, h){
36527         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36528             return true;
36529         }else{
36530             this.lastSize = {width: w, height: h};
36531             return false;
36532         }
36533     },
36534     /**
36535      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36536      * @return {Roo.UpdateManager} The UpdateManager
36537      */
36538     getUpdateManager : function(){
36539         return this.el.getUpdateManager();
36540     },
36541      /**
36542      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36543      * @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:
36544 <pre><code>
36545 panel.load({
36546     url: "your-url.php",
36547     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36548     callback: yourFunction,
36549     scope: yourObject, //(optional scope)
36550     discardUrl: false,
36551     nocache: false,
36552     text: "Loading...",
36553     timeout: 30,
36554     scripts: false
36555 });
36556 </code></pre>
36557      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36558      * 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.
36559      * @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}
36560      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36561      * @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.
36562      * @return {Roo.ContentPanel} this
36563      */
36564     load : function(){
36565         var um = this.el.getUpdateManager();
36566         um.update.apply(um, arguments);
36567         return this;
36568     },
36569
36570
36571     /**
36572      * 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.
36573      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36574      * @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)
36575      * @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)
36576      * @return {Roo.UpdateManager} The UpdateManager
36577      */
36578     setUrl : function(url, params, loadOnce){
36579         if(this.refreshDelegate){
36580             this.removeListener("activate", this.refreshDelegate);
36581         }
36582         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36583         this.on("activate", this.refreshDelegate);
36584         return this.el.getUpdateManager();
36585     },
36586     
36587     _handleRefresh : function(url, params, loadOnce){
36588         if(!loadOnce || !this.loaded){
36589             var updater = this.el.getUpdateManager();
36590             updater.update(url, params, this._setLoaded.createDelegate(this));
36591         }
36592     },
36593     
36594     _setLoaded : function(){
36595         this.loaded = true;
36596     }, 
36597     
36598     /**
36599      * Returns this panel's id
36600      * @return {String} 
36601      */
36602     getId : function(){
36603         return this.el.id;
36604     },
36605     
36606     /** 
36607      * Returns this panel's element - used by regiosn to add.
36608      * @return {Roo.Element} 
36609      */
36610     getEl : function(){
36611         return this.wrapEl || this.el;
36612     },
36613     
36614    
36615     
36616     adjustForComponents : function(width, height)
36617     {
36618         //Roo.log('adjustForComponents ');
36619         if(this.resizeEl != this.el){
36620             width -= this.el.getFrameWidth('lr');
36621             height -= this.el.getFrameWidth('tb');
36622         }
36623         if(this.toolbar){
36624             var te = this.toolbar.getEl();
36625             te.setWidth(width);
36626             height -= te.getHeight();
36627         }
36628         if(this.footer){
36629             var te = this.footer.getEl();
36630             te.setWidth(width);
36631             height -= te.getHeight();
36632         }
36633         
36634         
36635         if(this.adjustments){
36636             width += this.adjustments[0];
36637             height += this.adjustments[1];
36638         }
36639         return {"width": width, "height": height};
36640     },
36641     
36642     setSize : function(width, height){
36643         if(this.fitToFrame && !this.ignoreResize(width, height)){
36644             if(this.fitContainer && this.resizeEl != this.el){
36645                 this.el.setSize(width, height);
36646             }
36647             var size = this.adjustForComponents(width, height);
36648             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36649             this.fireEvent('resize', this, size.width, size.height);
36650         }
36651     },
36652     
36653     /**
36654      * Returns this panel's title
36655      * @return {String} 
36656      */
36657     getTitle : function(){
36658         
36659         if (typeof(this.title) != 'object') {
36660             return this.title;
36661         }
36662         
36663         var t = '';
36664         for (var k in this.title) {
36665             if (!this.title.hasOwnProperty(k)) {
36666                 continue;
36667             }
36668             
36669             if (k.indexOf('-') >= 0) {
36670                 var s = k.split('-');
36671                 for (var i = 0; i<s.length; i++) {
36672                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36673                 }
36674             } else {
36675                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36676             }
36677         }
36678         return t;
36679     },
36680     
36681     /**
36682      * Set this panel's title
36683      * @param {String} title
36684      */
36685     setTitle : function(title){
36686         this.title = title;
36687         if(this.region){
36688             this.region.updatePanelTitle(this, title);
36689         }
36690     },
36691     
36692     /**
36693      * Returns true is this panel was configured to be closable
36694      * @return {Boolean} 
36695      */
36696     isClosable : function(){
36697         return this.closable;
36698     },
36699     
36700     beforeSlide : function(){
36701         this.el.clip();
36702         this.resizeEl.clip();
36703     },
36704     
36705     afterSlide : function(){
36706         this.el.unclip();
36707         this.resizeEl.unclip();
36708     },
36709     
36710     /**
36711      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36712      *   Will fail silently if the {@link #setUrl} method has not been called.
36713      *   This does not activate the panel, just updates its content.
36714      */
36715     refresh : function(){
36716         if(this.refreshDelegate){
36717            this.loaded = false;
36718            this.refreshDelegate();
36719         }
36720     },
36721     
36722     /**
36723      * Destroys this panel
36724      */
36725     destroy : function(){
36726         this.el.removeAllListeners();
36727         var tempEl = document.createElement("span");
36728         tempEl.appendChild(this.el.dom);
36729         tempEl.innerHTML = "";
36730         this.el.remove();
36731         this.el = null;
36732     },
36733     
36734     /**
36735      * form - if the content panel contains a form - this is a reference to it.
36736      * @type {Roo.form.Form}
36737      */
36738     form : false,
36739     /**
36740      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36741      *    This contains a reference to it.
36742      * @type {Roo.View}
36743      */
36744     view : false,
36745     
36746       /**
36747      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36748      * <pre><code>
36749
36750 layout.addxtype({
36751        xtype : 'Form',
36752        items: [ .... ]
36753    }
36754 );
36755
36756 </code></pre>
36757      * @param {Object} cfg Xtype definition of item to add.
36758      */
36759     
36760     
36761     getChildContainer: function () {
36762         return this.getEl();
36763     }
36764     
36765     
36766     /*
36767         var  ret = new Roo.factory(cfg);
36768         return ret;
36769         
36770         
36771         // add form..
36772         if (cfg.xtype.match(/^Form$/)) {
36773             
36774             var el;
36775             //if (this.footer) {
36776             //    el = this.footer.container.insertSibling(false, 'before');
36777             //} else {
36778                 el = this.el.createChild();
36779             //}
36780
36781             this.form = new  Roo.form.Form(cfg);
36782             
36783             
36784             if ( this.form.allItems.length) {
36785                 this.form.render(el.dom);
36786             }
36787             return this.form;
36788         }
36789         // should only have one of theses..
36790         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36791             // views.. should not be just added - used named prop 'view''
36792             
36793             cfg.el = this.el.appendChild(document.createElement("div"));
36794             // factory?
36795             
36796             var ret = new Roo.factory(cfg);
36797              
36798              ret.render && ret.render(false, ''); // render blank..
36799             this.view = ret;
36800             return ret;
36801         }
36802         return false;
36803     }
36804     \*/
36805 });
36806  
36807 /**
36808  * @class Roo.bootstrap.panel.Grid
36809  * @extends Roo.bootstrap.panel.Content
36810  * @constructor
36811  * Create a new GridPanel.
36812  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36813  * @param {Object} config A the config object
36814   
36815  */
36816
36817
36818
36819 Roo.bootstrap.panel.Grid = function(config)
36820 {
36821     
36822       
36823     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36824         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36825
36826     config.el = this.wrapper;
36827     //this.el = this.wrapper;
36828     
36829       if (config.container) {
36830         // ctor'ed from a Border/panel.grid
36831         
36832         
36833         this.wrapper.setStyle("overflow", "hidden");
36834         this.wrapper.addClass('roo-grid-container');
36835
36836     }
36837     
36838     
36839     if(config.toolbar){
36840         var tool_el = this.wrapper.createChild();    
36841         this.toolbar = Roo.factory(config.toolbar);
36842         var ti = [];
36843         if (config.toolbar.items) {
36844             ti = config.toolbar.items ;
36845             delete config.toolbar.items ;
36846         }
36847         
36848         var nitems = [];
36849         this.toolbar.render(tool_el);
36850         for(var i =0;i < ti.length;i++) {
36851           //  Roo.log(['add child', items[i]]);
36852             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36853         }
36854         this.toolbar.items = nitems;
36855         
36856         delete config.toolbar;
36857     }
36858     
36859     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36860     config.grid.scrollBody = true;;
36861     config.grid.monitorWindowResize = false; // turn off autosizing
36862     config.grid.autoHeight = false;
36863     config.grid.autoWidth = false;
36864     
36865     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36866     
36867     if (config.background) {
36868         // render grid on panel activation (if panel background)
36869         this.on('activate', function(gp) {
36870             if (!gp.grid.rendered) {
36871                 gp.grid.render(this.wrapper);
36872                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36873             }
36874         });
36875             
36876     } else {
36877         this.grid.render(this.wrapper);
36878         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36879
36880     }
36881     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36882     // ??? needed ??? config.el = this.wrapper;
36883     
36884     
36885     
36886   
36887     // xtype created footer. - not sure if will work as we normally have to render first..
36888     if (this.footer && !this.footer.el && this.footer.xtype) {
36889         
36890         var ctr = this.grid.getView().getFooterPanel(true);
36891         this.footer.dataSource = this.grid.dataSource;
36892         this.footer = Roo.factory(this.footer, Roo);
36893         this.footer.render(ctr);
36894         
36895     }
36896     
36897     
36898     
36899     
36900      
36901 };
36902
36903 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36904     getId : function(){
36905         return this.grid.id;
36906     },
36907     
36908     /**
36909      * Returns the grid for this panel
36910      * @return {Roo.bootstrap.Table} 
36911      */
36912     getGrid : function(){
36913         return this.grid;    
36914     },
36915     
36916     setSize : function(width, height){
36917         if(!this.ignoreResize(width, height)){
36918             var grid = this.grid;
36919             var size = this.adjustForComponents(width, height);
36920             var gridel = grid.getGridEl();
36921             gridel.setSize(size.width, size.height);
36922             /*
36923             var thd = grid.getGridEl().select('thead',true).first();
36924             var tbd = grid.getGridEl().select('tbody', true).first();
36925             if (tbd) {
36926                 tbd.setSize(width, height - thd.getHeight());
36927             }
36928             */
36929             grid.autoSize();
36930         }
36931     },
36932      
36933     
36934     
36935     beforeSlide : function(){
36936         this.grid.getView().scroller.clip();
36937     },
36938     
36939     afterSlide : function(){
36940         this.grid.getView().scroller.unclip();
36941     },
36942     
36943     destroy : function(){
36944         this.grid.destroy();
36945         delete this.grid;
36946         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36947     }
36948 });
36949
36950 /**
36951  * @class Roo.bootstrap.panel.Nest
36952  * @extends Roo.bootstrap.panel.Content
36953  * @constructor
36954  * Create a new Panel, that can contain a layout.Border.
36955  * 
36956  * 
36957  * @param {Roo.BorderLayout} layout The layout for this panel
36958  * @param {String/Object} config A string to set only the title or a config object
36959  */
36960 Roo.bootstrap.panel.Nest = function(config)
36961 {
36962     // construct with only one argument..
36963     /* FIXME - implement nicer consturctors
36964     if (layout.layout) {
36965         config = layout;
36966         layout = config.layout;
36967         delete config.layout;
36968     }
36969     if (layout.xtype && !layout.getEl) {
36970         // then layout needs constructing..
36971         layout = Roo.factory(layout, Roo);
36972     }
36973     */
36974     
36975     config.el =  config.layout.getEl();
36976     
36977     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36978     
36979     config.layout.monitorWindowResize = false; // turn off autosizing
36980     this.layout = config.layout;
36981     this.layout.getEl().addClass("roo-layout-nested-layout");
36982     
36983     
36984     
36985     
36986 };
36987
36988 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36989
36990     setSize : function(width, height){
36991         if(!this.ignoreResize(width, height)){
36992             var size = this.adjustForComponents(width, height);
36993             var el = this.layout.getEl();
36994             if (size.height < 1) {
36995                 el.setWidth(size.width);   
36996             } else {
36997                 el.setSize(size.width, size.height);
36998             }
36999             var touch = el.dom.offsetWidth;
37000             this.layout.layout();
37001             // ie requires a double layout on the first pass
37002             if(Roo.isIE && !this.initialized){
37003                 this.initialized = true;
37004                 this.layout.layout();
37005             }
37006         }
37007     },
37008     
37009     // activate all subpanels if not currently active..
37010     
37011     setActiveState : function(active){
37012         this.active = active;
37013         this.setActiveClass(active);
37014         
37015         if(!active){
37016             this.fireEvent("deactivate", this);
37017             return;
37018         }
37019         
37020         this.fireEvent("activate", this);
37021         // not sure if this should happen before or after..
37022         if (!this.layout) {
37023             return; // should not happen..
37024         }
37025         var reg = false;
37026         for (var r in this.layout.regions) {
37027             reg = this.layout.getRegion(r);
37028             if (reg.getActivePanel()) {
37029                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37030                 reg.setActivePanel(reg.getActivePanel());
37031                 continue;
37032             }
37033             if (!reg.panels.length) {
37034                 continue;
37035             }
37036             reg.showPanel(reg.getPanel(0));
37037         }
37038         
37039         
37040         
37041         
37042     },
37043     
37044     /**
37045      * Returns the nested BorderLayout for this panel
37046      * @return {Roo.BorderLayout} 
37047      */
37048     getLayout : function(){
37049         return this.layout;
37050     },
37051     
37052      /**
37053      * Adds a xtype elements to the layout of the nested panel
37054      * <pre><code>
37055
37056 panel.addxtype({
37057        xtype : 'ContentPanel',
37058        region: 'west',
37059        items: [ .... ]
37060    }
37061 );
37062
37063 panel.addxtype({
37064         xtype : 'NestedLayoutPanel',
37065         region: 'west',
37066         layout: {
37067            center: { },
37068            west: { }   
37069         },
37070         items : [ ... list of content panels or nested layout panels.. ]
37071    }
37072 );
37073 </code></pre>
37074      * @param {Object} cfg Xtype definition of item to add.
37075      */
37076     addxtype : function(cfg) {
37077         return this.layout.addxtype(cfg);
37078     
37079     }
37080 });        /*
37081  * Based on:
37082  * Ext JS Library 1.1.1
37083  * Copyright(c) 2006-2007, Ext JS, LLC.
37084  *
37085  * Originally Released Under LGPL - original licence link has changed is not relivant.
37086  *
37087  * Fork - LGPL
37088  * <script type="text/javascript">
37089  */
37090 /**
37091  * @class Roo.TabPanel
37092  * @extends Roo.util.Observable
37093  * A lightweight tab container.
37094  * <br><br>
37095  * Usage:
37096  * <pre><code>
37097 // basic tabs 1, built from existing content
37098 var tabs = new Roo.TabPanel("tabs1");
37099 tabs.addTab("script", "View Script");
37100 tabs.addTab("markup", "View Markup");
37101 tabs.activate("script");
37102
37103 // more advanced tabs, built from javascript
37104 var jtabs = new Roo.TabPanel("jtabs");
37105 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37106
37107 // set up the UpdateManager
37108 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37109 var updater = tab2.getUpdateManager();
37110 updater.setDefaultUrl("ajax1.htm");
37111 tab2.on('activate', updater.refresh, updater, true);
37112
37113 // Use setUrl for Ajax loading
37114 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37115 tab3.setUrl("ajax2.htm", null, true);
37116
37117 // Disabled tab
37118 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37119 tab4.disable();
37120
37121 jtabs.activate("jtabs-1");
37122  * </code></pre>
37123  * @constructor
37124  * Create a new TabPanel.
37125  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37126  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37127  */
37128 Roo.bootstrap.panel.Tabs = function(config){
37129     /**
37130     * The container element for this TabPanel.
37131     * @type Roo.Element
37132     */
37133     this.el = Roo.get(config.el);
37134     delete config.el;
37135     if(config){
37136         if(typeof config == "boolean"){
37137             this.tabPosition = config ? "bottom" : "top";
37138         }else{
37139             Roo.apply(this, config);
37140         }
37141     }
37142     
37143     if(this.tabPosition == "bottom"){
37144         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37145         this.el.addClass("roo-tabs-bottom");
37146     }
37147     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37148     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37149     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37150     if(Roo.isIE){
37151         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37152     }
37153     if(this.tabPosition != "bottom"){
37154         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37155          * @type Roo.Element
37156          */
37157         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37158         this.el.addClass("roo-tabs-top");
37159     }
37160     this.items = [];
37161
37162     this.bodyEl.setStyle("position", "relative");
37163
37164     this.active = null;
37165     this.activateDelegate = this.activate.createDelegate(this);
37166
37167     this.addEvents({
37168         /**
37169          * @event tabchange
37170          * Fires when the active tab changes
37171          * @param {Roo.TabPanel} this
37172          * @param {Roo.TabPanelItem} activePanel The new active tab
37173          */
37174         "tabchange": true,
37175         /**
37176          * @event beforetabchange
37177          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37178          * @param {Roo.TabPanel} this
37179          * @param {Object} e Set cancel to true on this object to cancel the tab change
37180          * @param {Roo.TabPanelItem} tab The tab being changed to
37181          */
37182         "beforetabchange" : true
37183     });
37184
37185     Roo.EventManager.onWindowResize(this.onResize, this);
37186     this.cpad = this.el.getPadding("lr");
37187     this.hiddenCount = 0;
37188
37189
37190     // toolbar on the tabbar support...
37191     if (this.toolbar) {
37192         alert("no toolbar support yet");
37193         this.toolbar  = false;
37194         /*
37195         var tcfg = this.toolbar;
37196         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37197         this.toolbar = new Roo.Toolbar(tcfg);
37198         if (Roo.isSafari) {
37199             var tbl = tcfg.container.child('table', true);
37200             tbl.setAttribute('width', '100%');
37201         }
37202         */
37203         
37204     }
37205    
37206
37207
37208     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37209 };
37210
37211 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37212     /*
37213      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37214      */
37215     tabPosition : "top",
37216     /*
37217      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37218      */
37219     currentTabWidth : 0,
37220     /*
37221      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37222      */
37223     minTabWidth : 40,
37224     /*
37225      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37226      */
37227     maxTabWidth : 250,
37228     /*
37229      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37230      */
37231     preferredTabWidth : 175,
37232     /*
37233      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37234      */
37235     resizeTabs : false,
37236     /*
37237      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37238      */
37239     monitorResize : true,
37240     /*
37241      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37242      */
37243     toolbar : false,
37244
37245     /**
37246      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37247      * @param {String} id The id of the div to use <b>or create</b>
37248      * @param {String} text The text for the tab
37249      * @param {String} content (optional) Content to put in the TabPanelItem body
37250      * @param {Boolean} closable (optional) True to create a close icon on the tab
37251      * @return {Roo.TabPanelItem} The created TabPanelItem
37252      */
37253     addTab : function(id, text, content, closable, tpl)
37254     {
37255         var item = new Roo.bootstrap.panel.TabItem({
37256             panel: this,
37257             id : id,
37258             text : text,
37259             closable : closable,
37260             tpl : tpl
37261         });
37262         this.addTabItem(item);
37263         if(content){
37264             item.setContent(content);
37265         }
37266         return item;
37267     },
37268
37269     /**
37270      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37271      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37272      * @return {Roo.TabPanelItem}
37273      */
37274     getTab : function(id){
37275         return this.items[id];
37276     },
37277
37278     /**
37279      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37280      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37281      */
37282     hideTab : function(id){
37283         var t = this.items[id];
37284         if(!t.isHidden()){
37285            t.setHidden(true);
37286            this.hiddenCount++;
37287            this.autoSizeTabs();
37288         }
37289     },
37290
37291     /**
37292      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37293      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37294      */
37295     unhideTab : function(id){
37296         var t = this.items[id];
37297         if(t.isHidden()){
37298            t.setHidden(false);
37299            this.hiddenCount--;
37300            this.autoSizeTabs();
37301         }
37302     },
37303
37304     /**
37305      * Adds an existing {@link Roo.TabPanelItem}.
37306      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37307      */
37308     addTabItem : function(item){
37309         this.items[item.id] = item;
37310         this.items.push(item);
37311       //  if(this.resizeTabs){
37312     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37313   //         this.autoSizeTabs();
37314 //        }else{
37315 //            item.autoSize();
37316        // }
37317     },
37318
37319     /**
37320      * Removes a {@link Roo.TabPanelItem}.
37321      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37322      */
37323     removeTab : function(id){
37324         var items = this.items;
37325         var tab = items[id];
37326         if(!tab) { return; }
37327         var index = items.indexOf(tab);
37328         if(this.active == tab && items.length > 1){
37329             var newTab = this.getNextAvailable(index);
37330             if(newTab) {
37331                 newTab.activate();
37332             }
37333         }
37334         this.stripEl.dom.removeChild(tab.pnode.dom);
37335         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37336             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37337         }
37338         items.splice(index, 1);
37339         delete this.items[tab.id];
37340         tab.fireEvent("close", tab);
37341         tab.purgeListeners();
37342         this.autoSizeTabs();
37343     },
37344
37345     getNextAvailable : function(start){
37346         var items = this.items;
37347         var index = start;
37348         // look for a next tab that will slide over to
37349         // replace the one being removed
37350         while(index < items.length){
37351             var item = items[++index];
37352             if(item && !item.isHidden()){
37353                 return item;
37354             }
37355         }
37356         // if one isn't found select the previous tab (on the left)
37357         index = start;
37358         while(index >= 0){
37359             var item = items[--index];
37360             if(item && !item.isHidden()){
37361                 return item;
37362             }
37363         }
37364         return null;
37365     },
37366
37367     /**
37368      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37369      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37370      */
37371     disableTab : function(id){
37372         var tab = this.items[id];
37373         if(tab && this.active != tab){
37374             tab.disable();
37375         }
37376     },
37377
37378     /**
37379      * Enables a {@link Roo.TabPanelItem} that is disabled.
37380      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37381      */
37382     enableTab : function(id){
37383         var tab = this.items[id];
37384         tab.enable();
37385     },
37386
37387     /**
37388      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37389      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37390      * @return {Roo.TabPanelItem} The TabPanelItem.
37391      */
37392     activate : function(id){
37393         var tab = this.items[id];
37394         if(!tab){
37395             return null;
37396         }
37397         if(tab == this.active || tab.disabled){
37398             return tab;
37399         }
37400         var e = {};
37401         this.fireEvent("beforetabchange", this, e, tab);
37402         if(e.cancel !== true && !tab.disabled){
37403             if(this.active){
37404                 this.active.hide();
37405             }
37406             this.active = this.items[id];
37407             this.active.show();
37408             this.fireEvent("tabchange", this, this.active);
37409         }
37410         return tab;
37411     },
37412
37413     /**
37414      * Gets the active {@link Roo.TabPanelItem}.
37415      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37416      */
37417     getActiveTab : function(){
37418         return this.active;
37419     },
37420
37421     /**
37422      * Updates the tab body element to fit the height of the container element
37423      * for overflow scrolling
37424      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37425      */
37426     syncHeight : function(targetHeight){
37427         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37428         var bm = this.bodyEl.getMargins();
37429         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37430         this.bodyEl.setHeight(newHeight);
37431         return newHeight;
37432     },
37433
37434     onResize : function(){
37435         if(this.monitorResize){
37436             this.autoSizeTabs();
37437         }
37438     },
37439
37440     /**
37441      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37442      */
37443     beginUpdate : function(){
37444         this.updating = true;
37445     },
37446
37447     /**
37448      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37449      */
37450     endUpdate : function(){
37451         this.updating = false;
37452         this.autoSizeTabs();
37453     },
37454
37455     /**
37456      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37457      */
37458     autoSizeTabs : function(){
37459         var count = this.items.length;
37460         var vcount = count - this.hiddenCount;
37461         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37462             return;
37463         }
37464         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37465         var availWidth = Math.floor(w / vcount);
37466         var b = this.stripBody;
37467         if(b.getWidth() > w){
37468             var tabs = this.items;
37469             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37470             if(availWidth < this.minTabWidth){
37471                 /*if(!this.sleft){    // incomplete scrolling code
37472                     this.createScrollButtons();
37473                 }
37474                 this.showScroll();
37475                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37476             }
37477         }else{
37478             if(this.currentTabWidth < this.preferredTabWidth){
37479                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37480             }
37481         }
37482     },
37483
37484     /**
37485      * Returns the number of tabs in this TabPanel.
37486      * @return {Number}
37487      */
37488      getCount : function(){
37489          return this.items.length;
37490      },
37491
37492     /**
37493      * Resizes all the tabs to the passed width
37494      * @param {Number} The new width
37495      */
37496     setTabWidth : function(width){
37497         this.currentTabWidth = width;
37498         for(var i = 0, len = this.items.length; i < len; i++) {
37499                 if(!this.items[i].isHidden()) {
37500                 this.items[i].setWidth(width);
37501             }
37502         }
37503     },
37504
37505     /**
37506      * Destroys this TabPanel
37507      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37508      */
37509     destroy : function(removeEl){
37510         Roo.EventManager.removeResizeListener(this.onResize, this);
37511         for(var i = 0, len = this.items.length; i < len; i++){
37512             this.items[i].purgeListeners();
37513         }
37514         if(removeEl === true){
37515             this.el.update("");
37516             this.el.remove();
37517         }
37518     },
37519     
37520     createStrip : function(container)
37521     {
37522         var strip = document.createElement("nav");
37523         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37524         container.appendChild(strip);
37525         return strip;
37526     },
37527     
37528     createStripList : function(strip)
37529     {
37530         // div wrapper for retard IE
37531         // returns the "tr" element.
37532         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37533         //'<div class="x-tabs-strip-wrap">'+
37534           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37535           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37536         return strip.firstChild; //.firstChild.firstChild.firstChild;
37537     },
37538     createBody : function(container)
37539     {
37540         var body = document.createElement("div");
37541         Roo.id(body, "tab-body");
37542         //Roo.fly(body).addClass("x-tabs-body");
37543         Roo.fly(body).addClass("tab-content");
37544         container.appendChild(body);
37545         return body;
37546     },
37547     createItemBody :function(bodyEl, id){
37548         var body = Roo.getDom(id);
37549         if(!body){
37550             body = document.createElement("div");
37551             body.id = id;
37552         }
37553         //Roo.fly(body).addClass("x-tabs-item-body");
37554         Roo.fly(body).addClass("tab-pane");
37555          bodyEl.insertBefore(body, bodyEl.firstChild);
37556         return body;
37557     },
37558     /** @private */
37559     createStripElements :  function(stripEl, text, closable, tpl)
37560     {
37561         var td = document.createElement("li"); // was td..
37562         
37563         
37564         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37565         
37566         
37567         stripEl.appendChild(td);
37568         /*if(closable){
37569             td.className = "x-tabs-closable";
37570             if(!this.closeTpl){
37571                 this.closeTpl = new Roo.Template(
37572                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37573                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37574                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37575                 );
37576             }
37577             var el = this.closeTpl.overwrite(td, {"text": text});
37578             var close = el.getElementsByTagName("div")[0];
37579             var inner = el.getElementsByTagName("em")[0];
37580             return {"el": el, "close": close, "inner": inner};
37581         } else {
37582         */
37583         // not sure what this is..
37584 //            if(!this.tabTpl){
37585                 //this.tabTpl = new Roo.Template(
37586                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37587                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37588                 //);
37589 //                this.tabTpl = new Roo.Template(
37590 //                   '<a href="#">' +
37591 //                   '<span unselectable="on"' +
37592 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37593 //                            ' >{text}</span></a>'
37594 //                );
37595 //                
37596 //            }
37597
37598
37599             var template = tpl || this.tabTpl || false;
37600             
37601             if(!template){
37602                 
37603                 template = new Roo.Template(
37604                    '<a href="#">' +
37605                    '<span unselectable="on"' +
37606                             (this.disableTooltips ? '' : ' title="{text}"') +
37607                             ' >{text}</span></a>'
37608                 );
37609             }
37610             
37611             switch (typeof(template)) {
37612                 case 'object' :
37613                     break;
37614                 case 'string' :
37615                     template = new Roo.Template(template);
37616                     break;
37617                 default :
37618                     break;
37619             }
37620             
37621             var el = template.overwrite(td, {"text": text});
37622             
37623             var inner = el.getElementsByTagName("span")[0];
37624             
37625             return {"el": el, "inner": inner};
37626             
37627     }
37628         
37629     
37630 });
37631
37632 /**
37633  * @class Roo.TabPanelItem
37634  * @extends Roo.util.Observable
37635  * Represents an individual item (tab plus body) in a TabPanel.
37636  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37637  * @param {String} id The id of this TabPanelItem
37638  * @param {String} text The text for the tab of this TabPanelItem
37639  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37640  */
37641 Roo.bootstrap.panel.TabItem = function(config){
37642     /**
37643      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37644      * @type Roo.TabPanel
37645      */
37646     this.tabPanel = config.panel;
37647     /**
37648      * The id for this TabPanelItem
37649      * @type String
37650      */
37651     this.id = config.id;
37652     /** @private */
37653     this.disabled = false;
37654     /** @private */
37655     this.text = config.text;
37656     /** @private */
37657     this.loaded = false;
37658     this.closable = config.closable;
37659
37660     /**
37661      * The body element for this TabPanelItem.
37662      * @type Roo.Element
37663      */
37664     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37665     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37666     this.bodyEl.setStyle("display", "block");
37667     this.bodyEl.setStyle("zoom", "1");
37668     //this.hideAction();
37669
37670     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37671     /** @private */
37672     this.el = Roo.get(els.el);
37673     this.inner = Roo.get(els.inner, true);
37674     this.textEl = Roo.get(this.el.dom.firstChild, true);
37675     this.pnode = Roo.get(els.el.parentNode, true);
37676 //    this.el.on("mousedown", this.onTabMouseDown, this);
37677     this.el.on("click", this.onTabClick, this);
37678     /** @private */
37679     if(config.closable){
37680         var c = Roo.get(els.close, true);
37681         c.dom.title = this.closeText;
37682         c.addClassOnOver("close-over");
37683         c.on("click", this.closeClick, this);
37684      }
37685
37686     this.addEvents({
37687          /**
37688          * @event activate
37689          * Fires when this tab becomes the active tab.
37690          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37691          * @param {Roo.TabPanelItem} this
37692          */
37693         "activate": true,
37694         /**
37695          * @event beforeclose
37696          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37697          * @param {Roo.TabPanelItem} this
37698          * @param {Object} e Set cancel to true on this object to cancel the close.
37699          */
37700         "beforeclose": true,
37701         /**
37702          * @event close
37703          * Fires when this tab is closed.
37704          * @param {Roo.TabPanelItem} this
37705          */
37706          "close": true,
37707         /**
37708          * @event deactivate
37709          * Fires when this tab is no longer the active tab.
37710          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37711          * @param {Roo.TabPanelItem} this
37712          */
37713          "deactivate" : true
37714     });
37715     this.hidden = false;
37716
37717     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37718 };
37719
37720 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37721            {
37722     purgeListeners : function(){
37723        Roo.util.Observable.prototype.purgeListeners.call(this);
37724        this.el.removeAllListeners();
37725     },
37726     /**
37727      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37728      */
37729     show : function(){
37730         this.pnode.addClass("active");
37731         this.showAction();
37732         if(Roo.isOpera){
37733             this.tabPanel.stripWrap.repaint();
37734         }
37735         this.fireEvent("activate", this.tabPanel, this);
37736     },
37737
37738     /**
37739      * Returns true if this tab is the active tab.
37740      * @return {Boolean}
37741      */
37742     isActive : function(){
37743         return this.tabPanel.getActiveTab() == this;
37744     },
37745
37746     /**
37747      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37748      */
37749     hide : function(){
37750         this.pnode.removeClass("active");
37751         this.hideAction();
37752         this.fireEvent("deactivate", this.tabPanel, this);
37753     },
37754
37755     hideAction : function(){
37756         this.bodyEl.hide();
37757         this.bodyEl.setStyle("position", "absolute");
37758         this.bodyEl.setLeft("-20000px");
37759         this.bodyEl.setTop("-20000px");
37760     },
37761
37762     showAction : function(){
37763         this.bodyEl.setStyle("position", "relative");
37764         this.bodyEl.setTop("");
37765         this.bodyEl.setLeft("");
37766         this.bodyEl.show();
37767     },
37768
37769     /**
37770      * Set the tooltip for the tab.
37771      * @param {String} tooltip The tab's tooltip
37772      */
37773     setTooltip : function(text){
37774         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37775             this.textEl.dom.qtip = text;
37776             this.textEl.dom.removeAttribute('title');
37777         }else{
37778             this.textEl.dom.title = text;
37779         }
37780     },
37781
37782     onTabClick : function(e){
37783         e.preventDefault();
37784         this.tabPanel.activate(this.id);
37785     },
37786
37787     onTabMouseDown : function(e){
37788         e.preventDefault();
37789         this.tabPanel.activate(this.id);
37790     },
37791 /*
37792     getWidth : function(){
37793         return this.inner.getWidth();
37794     },
37795
37796     setWidth : function(width){
37797         var iwidth = width - this.pnode.getPadding("lr");
37798         this.inner.setWidth(iwidth);
37799         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37800         this.pnode.setWidth(width);
37801     },
37802 */
37803     /**
37804      * Show or hide the tab
37805      * @param {Boolean} hidden True to hide or false to show.
37806      */
37807     setHidden : function(hidden){
37808         this.hidden = hidden;
37809         this.pnode.setStyle("display", hidden ? "none" : "");
37810     },
37811
37812     /**
37813      * Returns true if this tab is "hidden"
37814      * @return {Boolean}
37815      */
37816     isHidden : function(){
37817         return this.hidden;
37818     },
37819
37820     /**
37821      * Returns the text for this tab
37822      * @return {String}
37823      */
37824     getText : function(){
37825         return this.text;
37826     },
37827     /*
37828     autoSize : function(){
37829         //this.el.beginMeasure();
37830         this.textEl.setWidth(1);
37831         /*
37832          *  #2804 [new] Tabs in Roojs
37833          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37834          */
37835         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37836         //this.el.endMeasure();
37837     //},
37838
37839     /**
37840      * Sets the text for the tab (Note: this also sets the tooltip text)
37841      * @param {String} text The tab's text and tooltip
37842      */
37843     setText : function(text){
37844         this.text = text;
37845         this.textEl.update(text);
37846         this.setTooltip(text);
37847         //if(!this.tabPanel.resizeTabs){
37848         //    this.autoSize();
37849         //}
37850     },
37851     /**
37852      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37853      */
37854     activate : function(){
37855         this.tabPanel.activate(this.id);
37856     },
37857
37858     /**
37859      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37860      */
37861     disable : function(){
37862         if(this.tabPanel.active != this){
37863             this.disabled = true;
37864             this.pnode.addClass("disabled");
37865         }
37866     },
37867
37868     /**
37869      * Enables this TabPanelItem if it was previously disabled.
37870      */
37871     enable : function(){
37872         this.disabled = false;
37873         this.pnode.removeClass("disabled");
37874     },
37875
37876     /**
37877      * Sets the content for this TabPanelItem.
37878      * @param {String} content The content
37879      * @param {Boolean} loadScripts true to look for and load scripts
37880      */
37881     setContent : function(content, loadScripts){
37882         this.bodyEl.update(content, loadScripts);
37883     },
37884
37885     /**
37886      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37887      * @return {Roo.UpdateManager} The UpdateManager
37888      */
37889     getUpdateManager : function(){
37890         return this.bodyEl.getUpdateManager();
37891     },
37892
37893     /**
37894      * Set a URL to be used to load the content for this TabPanelItem.
37895      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37896      * @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)
37897      * @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)
37898      * @return {Roo.UpdateManager} The UpdateManager
37899      */
37900     setUrl : function(url, params, loadOnce){
37901         if(this.refreshDelegate){
37902             this.un('activate', this.refreshDelegate);
37903         }
37904         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37905         this.on("activate", this.refreshDelegate);
37906         return this.bodyEl.getUpdateManager();
37907     },
37908
37909     /** @private */
37910     _handleRefresh : function(url, params, loadOnce){
37911         if(!loadOnce || !this.loaded){
37912             var updater = this.bodyEl.getUpdateManager();
37913             updater.update(url, params, this._setLoaded.createDelegate(this));
37914         }
37915     },
37916
37917     /**
37918      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37919      *   Will fail silently if the setUrl method has not been called.
37920      *   This does not activate the panel, just updates its content.
37921      */
37922     refresh : function(){
37923         if(this.refreshDelegate){
37924            this.loaded = false;
37925            this.refreshDelegate();
37926         }
37927     },
37928
37929     /** @private */
37930     _setLoaded : function(){
37931         this.loaded = true;
37932     },
37933
37934     /** @private */
37935     closeClick : function(e){
37936         var o = {};
37937         e.stopEvent();
37938         this.fireEvent("beforeclose", this, o);
37939         if(o.cancel !== true){
37940             this.tabPanel.removeTab(this.id);
37941         }
37942     },
37943     /**
37944      * The text displayed in the tooltip for the close icon.
37945      * @type String
37946      */
37947     closeText : "Close this tab"
37948 });
37949 /**
37950 *    This script refer to:
37951 *    Title: International Telephone Input
37952 *    Author: Jack O'Connor
37953 *    Code version:  v12.1.12
37954 *    Availability: https://github.com/jackocnr/intl-tel-input.git
37955 **/
37956
37957 Roo.bootstrap.PhoneInputData = function() {
37958     var d = [
37959       [
37960         "Afghanistan (‫افغانستان‬‎)",
37961         "af",
37962         "93"
37963       ],
37964       [
37965         "Albania (Shqipëri)",
37966         "al",
37967         "355"
37968       ],
37969       [
37970         "Algeria (‫الجزائر‬‎)",
37971         "dz",
37972         "213"
37973       ],
37974       [
37975         "American Samoa",
37976         "as",
37977         "1684"
37978       ],
37979       [
37980         "Andorra",
37981         "ad",
37982         "376"
37983       ],
37984       [
37985         "Angola",
37986         "ao",
37987         "244"
37988       ],
37989       [
37990         "Anguilla",
37991         "ai",
37992         "1264"
37993       ],
37994       [
37995         "Antigua and Barbuda",
37996         "ag",
37997         "1268"
37998       ],
37999       [
38000         "Argentina",
38001         "ar",
38002         "54"
38003       ],
38004       [
38005         "Armenia (Հայաստան)",
38006         "am",
38007         "374"
38008       ],
38009       [
38010         "Aruba",
38011         "aw",
38012         "297"
38013       ],
38014       [
38015         "Australia",
38016         "au",
38017         "61",
38018         0
38019       ],
38020       [
38021         "Austria (Österreich)",
38022         "at",
38023         "43"
38024       ],
38025       [
38026         "Azerbaijan (Azərbaycan)",
38027         "az",
38028         "994"
38029       ],
38030       [
38031         "Bahamas",
38032         "bs",
38033         "1242"
38034       ],
38035       [
38036         "Bahrain (‫البحرين‬‎)",
38037         "bh",
38038         "973"
38039       ],
38040       [
38041         "Bangladesh (বাংলাদেশ)",
38042         "bd",
38043         "880"
38044       ],
38045       [
38046         "Barbados",
38047         "bb",
38048         "1246"
38049       ],
38050       [
38051         "Belarus (Беларусь)",
38052         "by",
38053         "375"
38054       ],
38055       [
38056         "Belgium (België)",
38057         "be",
38058         "32"
38059       ],
38060       [
38061         "Belize",
38062         "bz",
38063         "501"
38064       ],
38065       [
38066         "Benin (Bénin)",
38067         "bj",
38068         "229"
38069       ],
38070       [
38071         "Bermuda",
38072         "bm",
38073         "1441"
38074       ],
38075       [
38076         "Bhutan (འབྲུག)",
38077         "bt",
38078         "975"
38079       ],
38080       [
38081         "Bolivia",
38082         "bo",
38083         "591"
38084       ],
38085       [
38086         "Bosnia and Herzegovina (Босна и Херцеговина)",
38087         "ba",
38088         "387"
38089       ],
38090       [
38091         "Botswana",
38092         "bw",
38093         "267"
38094       ],
38095       [
38096         "Brazil (Brasil)",
38097         "br",
38098         "55"
38099       ],
38100       [
38101         "British Indian Ocean Territory",
38102         "io",
38103         "246"
38104       ],
38105       [
38106         "British Virgin Islands",
38107         "vg",
38108         "1284"
38109       ],
38110       [
38111         "Brunei",
38112         "bn",
38113         "673"
38114       ],
38115       [
38116         "Bulgaria (България)",
38117         "bg",
38118         "359"
38119       ],
38120       [
38121         "Burkina Faso",
38122         "bf",
38123         "226"
38124       ],
38125       [
38126         "Burundi (Uburundi)",
38127         "bi",
38128         "257"
38129       ],
38130       [
38131         "Cambodia (កម្ពុជា)",
38132         "kh",
38133         "855"
38134       ],
38135       [
38136         "Cameroon (Cameroun)",
38137         "cm",
38138         "237"
38139       ],
38140       [
38141         "Canada",
38142         "ca",
38143         "1",
38144         1,
38145         ["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"]
38146       ],
38147       [
38148         "Cape Verde (Kabu Verdi)",
38149         "cv",
38150         "238"
38151       ],
38152       [
38153         "Caribbean Netherlands",
38154         "bq",
38155         "599",
38156         1
38157       ],
38158       [
38159         "Cayman Islands",
38160         "ky",
38161         "1345"
38162       ],
38163       [
38164         "Central African Republic (République centrafricaine)",
38165         "cf",
38166         "236"
38167       ],
38168       [
38169         "Chad (Tchad)",
38170         "td",
38171         "235"
38172       ],
38173       [
38174         "Chile",
38175         "cl",
38176         "56"
38177       ],
38178       [
38179         "China (中国)",
38180         "cn",
38181         "86"
38182       ],
38183       [
38184         "Christmas Island",
38185         "cx",
38186         "61",
38187         2
38188       ],
38189       [
38190         "Cocos (Keeling) Islands",
38191         "cc",
38192         "61",
38193         1
38194       ],
38195       [
38196         "Colombia",
38197         "co",
38198         "57"
38199       ],
38200       [
38201         "Comoros (‫جزر القمر‬‎)",
38202         "km",
38203         "269"
38204       ],
38205       [
38206         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38207         "cd",
38208         "243"
38209       ],
38210       [
38211         "Congo (Republic) (Congo-Brazzaville)",
38212         "cg",
38213         "242"
38214       ],
38215       [
38216         "Cook Islands",
38217         "ck",
38218         "682"
38219       ],
38220       [
38221         "Costa Rica",
38222         "cr",
38223         "506"
38224       ],
38225       [
38226         "Côte d’Ivoire",
38227         "ci",
38228         "225"
38229       ],
38230       [
38231         "Croatia (Hrvatska)",
38232         "hr",
38233         "385"
38234       ],
38235       [
38236         "Cuba",
38237         "cu",
38238         "53"
38239       ],
38240       [
38241         "Curaçao",
38242         "cw",
38243         "599",
38244         0
38245       ],
38246       [
38247         "Cyprus (Κύπρος)",
38248         "cy",
38249         "357"
38250       ],
38251       [
38252         "Czech Republic (Česká republika)",
38253         "cz",
38254         "420"
38255       ],
38256       [
38257         "Denmark (Danmark)",
38258         "dk",
38259         "45"
38260       ],
38261       [
38262         "Djibouti",
38263         "dj",
38264         "253"
38265       ],
38266       [
38267         "Dominica",
38268         "dm",
38269         "1767"
38270       ],
38271       [
38272         "Dominican Republic (República Dominicana)",
38273         "do",
38274         "1",
38275         2,
38276         ["809", "829", "849"]
38277       ],
38278       [
38279         "Ecuador",
38280         "ec",
38281         "593"
38282       ],
38283       [
38284         "Egypt (‫مصر‬‎)",
38285         "eg",
38286         "20"
38287       ],
38288       [
38289         "El Salvador",
38290         "sv",
38291         "503"
38292       ],
38293       [
38294         "Equatorial Guinea (Guinea Ecuatorial)",
38295         "gq",
38296         "240"
38297       ],
38298       [
38299         "Eritrea",
38300         "er",
38301         "291"
38302       ],
38303       [
38304         "Estonia (Eesti)",
38305         "ee",
38306         "372"
38307       ],
38308       [
38309         "Ethiopia",
38310         "et",
38311         "251"
38312       ],
38313       [
38314         "Falkland Islands (Islas Malvinas)",
38315         "fk",
38316         "500"
38317       ],
38318       [
38319         "Faroe Islands (Føroyar)",
38320         "fo",
38321         "298"
38322       ],
38323       [
38324         "Fiji",
38325         "fj",
38326         "679"
38327       ],
38328       [
38329         "Finland (Suomi)",
38330         "fi",
38331         "358",
38332         0
38333       ],
38334       [
38335         "France",
38336         "fr",
38337         "33"
38338       ],
38339       [
38340         "French Guiana (Guyane française)",
38341         "gf",
38342         "594"
38343       ],
38344       [
38345         "French Polynesia (Polynésie française)",
38346         "pf",
38347         "689"
38348       ],
38349       [
38350         "Gabon",
38351         "ga",
38352         "241"
38353       ],
38354       [
38355         "Gambia",
38356         "gm",
38357         "220"
38358       ],
38359       [
38360         "Georgia (საქართველო)",
38361         "ge",
38362         "995"
38363       ],
38364       [
38365         "Germany (Deutschland)",
38366         "de",
38367         "49"
38368       ],
38369       [
38370         "Ghana (Gaana)",
38371         "gh",
38372         "233"
38373       ],
38374       [
38375         "Gibraltar",
38376         "gi",
38377         "350"
38378       ],
38379       [
38380         "Greece (Ελλάδα)",
38381         "gr",
38382         "30"
38383       ],
38384       [
38385         "Greenland (Kalaallit Nunaat)",
38386         "gl",
38387         "299"
38388       ],
38389       [
38390         "Grenada",
38391         "gd",
38392         "1473"
38393       ],
38394       [
38395         "Guadeloupe",
38396         "gp",
38397         "590",
38398         0
38399       ],
38400       [
38401         "Guam",
38402         "gu",
38403         "1671"
38404       ],
38405       [
38406         "Guatemala",
38407         "gt",
38408         "502"
38409       ],
38410       [
38411         "Guernsey",
38412         "gg",
38413         "44",
38414         1
38415       ],
38416       [
38417         "Guinea (Guinée)",
38418         "gn",
38419         "224"
38420       ],
38421       [
38422         "Guinea-Bissau (Guiné Bissau)",
38423         "gw",
38424         "245"
38425       ],
38426       [
38427         "Guyana",
38428         "gy",
38429         "592"
38430       ],
38431       [
38432         "Haiti",
38433         "ht",
38434         "509"
38435       ],
38436       [
38437         "Honduras",
38438         "hn",
38439         "504"
38440       ],
38441       [
38442         "Hong Kong (香港)",
38443         "hk",
38444         "852"
38445       ],
38446       [
38447         "Hungary (Magyarország)",
38448         "hu",
38449         "36"
38450       ],
38451       [
38452         "Iceland (Ísland)",
38453         "is",
38454         "354"
38455       ],
38456       [
38457         "India (भारत)",
38458         "in",
38459         "91"
38460       ],
38461       [
38462         "Indonesia",
38463         "id",
38464         "62"
38465       ],
38466       [
38467         "Iran (‫ایران‬‎)",
38468         "ir",
38469         "98"
38470       ],
38471       [
38472         "Iraq (‫العراق‬‎)",
38473         "iq",
38474         "964"
38475       ],
38476       [
38477         "Ireland",
38478         "ie",
38479         "353"
38480       ],
38481       [
38482         "Isle of Man",
38483         "im",
38484         "44",
38485         2
38486       ],
38487       [
38488         "Israel (‫ישראל‬‎)",
38489         "il",
38490         "972"
38491       ],
38492       [
38493         "Italy (Italia)",
38494         "it",
38495         "39",
38496         0
38497       ],
38498       [
38499         "Jamaica",
38500         "jm",
38501         "1876"
38502       ],
38503       [
38504         "Japan (日本)",
38505         "jp",
38506         "81"
38507       ],
38508       [
38509         "Jersey",
38510         "je",
38511         "44",
38512         3
38513       ],
38514       [
38515         "Jordan (‫الأردن‬‎)",
38516         "jo",
38517         "962"
38518       ],
38519       [
38520         "Kazakhstan (Казахстан)",
38521         "kz",
38522         "7",
38523         1
38524       ],
38525       [
38526         "Kenya",
38527         "ke",
38528         "254"
38529       ],
38530       [
38531         "Kiribati",
38532         "ki",
38533         "686"
38534       ],
38535       [
38536         "Kosovo",
38537         "xk",
38538         "383"
38539       ],
38540       [
38541         "Kuwait (‫الكويت‬‎)",
38542         "kw",
38543         "965"
38544       ],
38545       [
38546         "Kyrgyzstan (Кыргызстан)",
38547         "kg",
38548         "996"
38549       ],
38550       [
38551         "Laos (ລາວ)",
38552         "la",
38553         "856"
38554       ],
38555       [
38556         "Latvia (Latvija)",
38557         "lv",
38558         "371"
38559       ],
38560       [
38561         "Lebanon (‫لبنان‬‎)",
38562         "lb",
38563         "961"
38564       ],
38565       [
38566         "Lesotho",
38567         "ls",
38568         "266"
38569       ],
38570       [
38571         "Liberia",
38572         "lr",
38573         "231"
38574       ],
38575       [
38576         "Libya (‫ليبيا‬‎)",
38577         "ly",
38578         "218"
38579       ],
38580       [
38581         "Liechtenstein",
38582         "li",
38583         "423"
38584       ],
38585       [
38586         "Lithuania (Lietuva)",
38587         "lt",
38588         "370"
38589       ],
38590       [
38591         "Luxembourg",
38592         "lu",
38593         "352"
38594       ],
38595       [
38596         "Macau (澳門)",
38597         "mo",
38598         "853"
38599       ],
38600       [
38601         "Macedonia (FYROM) (Македонија)",
38602         "mk",
38603         "389"
38604       ],
38605       [
38606         "Madagascar (Madagasikara)",
38607         "mg",
38608         "261"
38609       ],
38610       [
38611         "Malawi",
38612         "mw",
38613         "265"
38614       ],
38615       [
38616         "Malaysia",
38617         "my",
38618         "60"
38619       ],
38620       [
38621         "Maldives",
38622         "mv",
38623         "960"
38624       ],
38625       [
38626         "Mali",
38627         "ml",
38628         "223"
38629       ],
38630       [
38631         "Malta",
38632         "mt",
38633         "356"
38634       ],
38635       [
38636         "Marshall Islands",
38637         "mh",
38638         "692"
38639       ],
38640       [
38641         "Martinique",
38642         "mq",
38643         "596"
38644       ],
38645       [
38646         "Mauritania (‫موريتانيا‬‎)",
38647         "mr",
38648         "222"
38649       ],
38650       [
38651         "Mauritius (Moris)",
38652         "mu",
38653         "230"
38654       ],
38655       [
38656         "Mayotte",
38657         "yt",
38658         "262",
38659         1
38660       ],
38661       [
38662         "Mexico (México)",
38663         "mx",
38664         "52"
38665       ],
38666       [
38667         "Micronesia",
38668         "fm",
38669         "691"
38670       ],
38671       [
38672         "Moldova (Republica Moldova)",
38673         "md",
38674         "373"
38675       ],
38676       [
38677         "Monaco",
38678         "mc",
38679         "377"
38680       ],
38681       [
38682         "Mongolia (Монгол)",
38683         "mn",
38684         "976"
38685       ],
38686       [
38687         "Montenegro (Crna Gora)",
38688         "me",
38689         "382"
38690       ],
38691       [
38692         "Montserrat",
38693         "ms",
38694         "1664"
38695       ],
38696       [
38697         "Morocco (‫المغرب‬‎)",
38698         "ma",
38699         "212",
38700         0
38701       ],
38702       [
38703         "Mozambique (Moçambique)",
38704         "mz",
38705         "258"
38706       ],
38707       [
38708         "Myanmar (Burma) (မြန်မာ)",
38709         "mm",
38710         "95"
38711       ],
38712       [
38713         "Namibia (Namibië)",
38714         "na",
38715         "264"
38716       ],
38717       [
38718         "Nauru",
38719         "nr",
38720         "674"
38721       ],
38722       [
38723         "Nepal (नेपाल)",
38724         "np",
38725         "977"
38726       ],
38727       [
38728         "Netherlands (Nederland)",
38729         "nl",
38730         "31"
38731       ],
38732       [
38733         "New Caledonia (Nouvelle-Calédonie)",
38734         "nc",
38735         "687"
38736       ],
38737       [
38738         "New Zealand",
38739         "nz",
38740         "64"
38741       ],
38742       [
38743         "Nicaragua",
38744         "ni",
38745         "505"
38746       ],
38747       [
38748         "Niger (Nijar)",
38749         "ne",
38750         "227"
38751       ],
38752       [
38753         "Nigeria",
38754         "ng",
38755         "234"
38756       ],
38757       [
38758         "Niue",
38759         "nu",
38760         "683"
38761       ],
38762       [
38763         "Norfolk Island",
38764         "nf",
38765         "672"
38766       ],
38767       [
38768         "North Korea (조선 민주주의 인민 공화국)",
38769         "kp",
38770         "850"
38771       ],
38772       [
38773         "Northern Mariana Islands",
38774         "mp",
38775         "1670"
38776       ],
38777       [
38778         "Norway (Norge)",
38779         "no",
38780         "47",
38781         0
38782       ],
38783       [
38784         "Oman (‫عُمان‬‎)",
38785         "om",
38786         "968"
38787       ],
38788       [
38789         "Pakistan (‫پاکستان‬‎)",
38790         "pk",
38791         "92"
38792       ],
38793       [
38794         "Palau",
38795         "pw",
38796         "680"
38797       ],
38798       [
38799         "Palestine (‫فلسطين‬‎)",
38800         "ps",
38801         "970"
38802       ],
38803       [
38804         "Panama (Panamá)",
38805         "pa",
38806         "507"
38807       ],
38808       [
38809         "Papua New Guinea",
38810         "pg",
38811         "675"
38812       ],
38813       [
38814         "Paraguay",
38815         "py",
38816         "595"
38817       ],
38818       [
38819         "Peru (Perú)",
38820         "pe",
38821         "51"
38822       ],
38823       [
38824         "Philippines",
38825         "ph",
38826         "63"
38827       ],
38828       [
38829         "Poland (Polska)",
38830         "pl",
38831         "48"
38832       ],
38833       [
38834         "Portugal",
38835         "pt",
38836         "351"
38837       ],
38838       [
38839         "Puerto Rico",
38840         "pr",
38841         "1",
38842         3,
38843         ["787", "939"]
38844       ],
38845       [
38846         "Qatar (‫قطر‬‎)",
38847         "qa",
38848         "974"
38849       ],
38850       [
38851         "Réunion (La Réunion)",
38852         "re",
38853         "262",
38854         0
38855       ],
38856       [
38857         "Romania (România)",
38858         "ro",
38859         "40"
38860       ],
38861       [
38862         "Russia (Россия)",
38863         "ru",
38864         "7",
38865         0
38866       ],
38867       [
38868         "Rwanda",
38869         "rw",
38870         "250"
38871       ],
38872       [
38873         "Saint Barthélemy",
38874         "bl",
38875         "590",
38876         1
38877       ],
38878       [
38879         "Saint Helena",
38880         "sh",
38881         "290"
38882       ],
38883       [
38884         "Saint Kitts and Nevis",
38885         "kn",
38886         "1869"
38887       ],
38888       [
38889         "Saint Lucia",
38890         "lc",
38891         "1758"
38892       ],
38893       [
38894         "Saint Martin (Saint-Martin (partie française))",
38895         "mf",
38896         "590",
38897         2
38898       ],
38899       [
38900         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38901         "pm",
38902         "508"
38903       ],
38904       [
38905         "Saint Vincent and the Grenadines",
38906         "vc",
38907         "1784"
38908       ],
38909       [
38910         "Samoa",
38911         "ws",
38912         "685"
38913       ],
38914       [
38915         "San Marino",
38916         "sm",
38917         "378"
38918       ],
38919       [
38920         "São Tomé and Príncipe (São Tomé e Príncipe)",
38921         "st",
38922         "239"
38923       ],
38924       [
38925         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
38926         "sa",
38927         "966"
38928       ],
38929       [
38930         "Senegal (Sénégal)",
38931         "sn",
38932         "221"
38933       ],
38934       [
38935         "Serbia (Србија)",
38936         "rs",
38937         "381"
38938       ],
38939       [
38940         "Seychelles",
38941         "sc",
38942         "248"
38943       ],
38944       [
38945         "Sierra Leone",
38946         "sl",
38947         "232"
38948       ],
38949       [
38950         "Singapore",
38951         "sg",
38952         "65"
38953       ],
38954       [
38955         "Sint Maarten",
38956         "sx",
38957         "1721"
38958       ],
38959       [
38960         "Slovakia (Slovensko)",
38961         "sk",
38962         "421"
38963       ],
38964       [
38965         "Slovenia (Slovenija)",
38966         "si",
38967         "386"
38968       ],
38969       [
38970         "Solomon Islands",
38971         "sb",
38972         "677"
38973       ],
38974       [
38975         "Somalia (Soomaaliya)",
38976         "so",
38977         "252"
38978       ],
38979       [
38980         "South Africa",
38981         "za",
38982         "27"
38983       ],
38984       [
38985         "South Korea (대한민국)",
38986         "kr",
38987         "82"
38988       ],
38989       [
38990         "South Sudan (‫جنوب السودان‬‎)",
38991         "ss",
38992         "211"
38993       ],
38994       [
38995         "Spain (España)",
38996         "es",
38997         "34"
38998       ],
38999       [
39000         "Sri Lanka (ශ්‍රී ලංකාව)",
39001         "lk",
39002         "94"
39003       ],
39004       [
39005         "Sudan (‫السودان‬‎)",
39006         "sd",
39007         "249"
39008       ],
39009       [
39010         "Suriname",
39011         "sr",
39012         "597"
39013       ],
39014       [
39015         "Svalbard and Jan Mayen",
39016         "sj",
39017         "47",
39018         1
39019       ],
39020       [
39021         "Swaziland",
39022         "sz",
39023         "268"
39024       ],
39025       [
39026         "Sweden (Sverige)",
39027         "se",
39028         "46"
39029       ],
39030       [
39031         "Switzerland (Schweiz)",
39032         "ch",
39033         "41"
39034       ],
39035       [
39036         "Syria (‫سوريا‬‎)",
39037         "sy",
39038         "963"
39039       ],
39040       [
39041         "Taiwan (台灣)",
39042         "tw",
39043         "886"
39044       ],
39045       [
39046         "Tajikistan",
39047         "tj",
39048         "992"
39049       ],
39050       [
39051         "Tanzania",
39052         "tz",
39053         "255"
39054       ],
39055       [
39056         "Thailand (ไทย)",
39057         "th",
39058         "66"
39059       ],
39060       [
39061         "Timor-Leste",
39062         "tl",
39063         "670"
39064       ],
39065       [
39066         "Togo",
39067         "tg",
39068         "228"
39069       ],
39070       [
39071         "Tokelau",
39072         "tk",
39073         "690"
39074       ],
39075       [
39076         "Tonga",
39077         "to",
39078         "676"
39079       ],
39080       [
39081         "Trinidad and Tobago",
39082         "tt",
39083         "1868"
39084       ],
39085       [
39086         "Tunisia (‫تونس‬‎)",
39087         "tn",
39088         "216"
39089       ],
39090       [
39091         "Turkey (Türkiye)",
39092         "tr",
39093         "90"
39094       ],
39095       [
39096         "Turkmenistan",
39097         "tm",
39098         "993"
39099       ],
39100       [
39101         "Turks and Caicos Islands",
39102         "tc",
39103         "1649"
39104       ],
39105       [
39106         "Tuvalu",
39107         "tv",
39108         "688"
39109       ],
39110       [
39111         "U.S. Virgin Islands",
39112         "vi",
39113         "1340"
39114       ],
39115       [
39116         "Uganda",
39117         "ug",
39118         "256"
39119       ],
39120       [
39121         "Ukraine (Україна)",
39122         "ua",
39123         "380"
39124       ],
39125       [
39126         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39127         "ae",
39128         "971"
39129       ],
39130       [
39131         "United Kingdom",
39132         "gb",
39133         "44",
39134         0
39135       ],
39136       [
39137         "United States",
39138         "us",
39139         "1",
39140         0
39141       ],
39142       [
39143         "Uruguay",
39144         "uy",
39145         "598"
39146       ],
39147       [
39148         "Uzbekistan (Oʻzbekiston)",
39149         "uz",
39150         "998"
39151       ],
39152       [
39153         "Vanuatu",
39154         "vu",
39155         "678"
39156       ],
39157       [
39158         "Vatican City (Città del Vaticano)",
39159         "va",
39160         "39",
39161         1
39162       ],
39163       [
39164         "Venezuela",
39165         "ve",
39166         "58"
39167       ],
39168       [
39169         "Vietnam (Việt Nam)",
39170         "vn",
39171         "84"
39172       ],
39173       [
39174         "Wallis and Futuna (Wallis-et-Futuna)",
39175         "wf",
39176         "681"
39177       ],
39178       [
39179         "Western Sahara (‫الصحراء الغربية‬‎)",
39180         "eh",
39181         "212",
39182         1
39183       ],
39184       [
39185         "Yemen (‫اليمن‬‎)",
39186         "ye",
39187         "967"
39188       ],
39189       [
39190         "Zambia",
39191         "zm",
39192         "260"
39193       ],
39194       [
39195         "Zimbabwe",
39196         "zw",
39197         "263"
39198       ],
39199       [
39200         "Åland Islands",
39201         "ax",
39202         "358",
39203         1
39204       ]
39205   ];
39206   
39207   return d;
39208 }/**
39209 *    This script refer to:
39210 *    Title: International Telephone Input
39211 *    Author: Jack O'Connor
39212 *    Code version:  v12.1.12
39213 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39214 **/
39215
39216 /**
39217  * @class Roo.bootstrap.PhoneInput
39218  * @extends Roo.bootstrap.TriggerField
39219  * An input with International dial-code selection
39220  
39221  * @cfg {String} defaultDialCode default '+852'
39222  * @cfg {Array} preferedCountries default []
39223   
39224  * @constructor
39225  * Create a new PhoneInput.
39226  * @param {Object} config Configuration options
39227  */
39228
39229 Roo.bootstrap.PhoneInput = function(config) {
39230     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39231 };
39232
39233 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39234         
39235         listWidth: undefined,
39236         
39237         selectedClass: 'active',
39238         
39239         invalidClass : "has-warning",
39240         
39241         validClass: 'has-success',
39242         
39243         allowed: '0123456789',
39244         
39245         /**
39246          * @cfg {String} defaultDialCode The default dial code when initializing the input
39247          */
39248         defaultDialCode: '+852',
39249         
39250         /**
39251          * @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
39252          */
39253         preferedCountries: false,
39254         
39255         getAutoCreate : function()
39256         {
39257             var data = Roo.bootstrap.PhoneInputData();
39258             var align = this.labelAlign || this.parentLabelAlign();
39259             var id = Roo.id();
39260             
39261             this.allCountries = [];
39262             this.dialCodeMapping = [];
39263             
39264             for (var i = 0; i < data.length; i++) {
39265               var c = data[i];
39266               this.allCountries[i] = {
39267                 name: c[0],
39268                 iso2: c[1],
39269                 dialCode: c[2],
39270                 priority: c[3] || 0,
39271                 areaCodes: c[4] || null
39272               };
39273               this.dialCodeMapping[c[2]] = {
39274                   name: c[0],
39275                   iso2: c[1],
39276                   priority: c[3] || 0,
39277                   areaCodes: c[4] || null
39278               };
39279             }
39280             
39281             var cfg = {
39282                 cls: 'form-group',
39283                 cn: []
39284             };
39285             
39286             var input =  {
39287                 tag: 'input',
39288                 id : id,
39289                 cls : 'form-control tel-input',
39290                 autocomplete: 'new-password'
39291             };
39292             
39293             var hiddenInput = {
39294                 tag: 'input',
39295                 type: 'hidden',
39296                 cls: 'hidden-tel-input'
39297             };
39298             
39299             if (this.name) {
39300                 hiddenInput.name = this.name;
39301             }
39302             
39303             if (this.disabled) {
39304                 input.disabled = true;
39305             }
39306             
39307             var flag_container = {
39308                 tag: 'div',
39309                 cls: 'flag-box',
39310                 cn: [
39311                     {
39312                         tag: 'div',
39313                         cls: 'flag'
39314                     },
39315                     {
39316                         tag: 'div',
39317                         cls: 'caret'
39318                     }
39319                 ]
39320             };
39321             
39322             var box = {
39323                 tag: 'div',
39324                 cls: this.hasFeedback ? 'has-feedback' : '',
39325                 cn: [
39326                     hiddenInput,
39327                     input,
39328                     {
39329                         tag: 'input',
39330                         cls: 'dial-code-holder',
39331                         disabled: true
39332                     }
39333                 ]
39334             };
39335             
39336             var container = {
39337                 cls: 'roo-select2-container input-group',
39338                 cn: [
39339                     flag_container,
39340                     box
39341                 ]
39342             };
39343             
39344             if (this.fieldLabel.length) {
39345                 var indicator = {
39346                     tag: 'i',
39347                     tooltip: 'This field is required'
39348                 };
39349                 
39350                 var label = {
39351                     tag: 'label',
39352                     'for':  id,
39353                     cls: 'control-label',
39354                     cn: []
39355                 };
39356                 
39357                 var label_text = {
39358                     tag: 'span',
39359                     html: this.fieldLabel
39360                 };
39361                 
39362                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39363                 label.cn = [
39364                     indicator,
39365                     label_text
39366                 ];
39367                 
39368                 if(this.indicatorpos == 'right') {
39369                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39370                     label.cn = [
39371                         label_text,
39372                         indicator
39373                     ];
39374                 }
39375                 
39376                 if(align == 'left') {
39377                     container = {
39378                         tag: 'div',
39379                         cn: [
39380                             container
39381                         ]
39382                     };
39383                     
39384                     if(this.labelWidth > 12){
39385                         label.style = "width: " + this.labelWidth + 'px';
39386                     }
39387                     if(this.labelWidth < 13 && this.labelmd == 0){
39388                         this.labelmd = this.labelWidth;
39389                     }
39390                     if(this.labellg > 0){
39391                         label.cls += ' col-lg-' + this.labellg;
39392                         input.cls += ' col-lg-' + (12 - this.labellg);
39393                     }
39394                     if(this.labelmd > 0){
39395                         label.cls += ' col-md-' + this.labelmd;
39396                         container.cls += ' col-md-' + (12 - this.labelmd);
39397                     }
39398                     if(this.labelsm > 0){
39399                         label.cls += ' col-sm-' + this.labelsm;
39400                         container.cls += ' col-sm-' + (12 - this.labelsm);
39401                     }
39402                     if(this.labelxs > 0){
39403                         label.cls += ' col-xs-' + this.labelxs;
39404                         container.cls += ' col-xs-' + (12 - this.labelxs);
39405                     }
39406                 }
39407             }
39408             
39409             cfg.cn = [
39410                 label,
39411                 container
39412             ];
39413             
39414             var settings = this;
39415             
39416             ['xs','sm','md','lg'].map(function(size){
39417                 if (settings[size]) {
39418                     cfg.cls += ' col-' + size + '-' + settings[size];
39419                 }
39420             });
39421             
39422             this.store = new Roo.data.Store({
39423                 proxy : new Roo.data.MemoryProxy({}),
39424                 reader : new Roo.data.JsonReader({
39425                     fields : [
39426                         {
39427                             'name' : 'name',
39428                             'type' : 'string'
39429                         },
39430                         {
39431                             'name' : 'iso2',
39432                             'type' : 'string'
39433                         },
39434                         {
39435                             'name' : 'dialCode',
39436                             'type' : 'string'
39437                         },
39438                         {
39439                             'name' : 'priority',
39440                             'type' : 'string'
39441                         },
39442                         {
39443                             'name' : 'areaCodes',
39444                             'type' : 'string'
39445                         }
39446                     ]
39447                 })
39448             });
39449             
39450             if(!this.preferedCountries) {
39451                 this.preferedCountries = [
39452                     'hk',
39453                     'gb',
39454                     'us'
39455                 ];
39456             }
39457             
39458             var p = this.preferedCountries.reverse();
39459             
39460             if(p) {
39461                 for (var i = 0; i < p.length; i++) {
39462                     for (var j = 0; j < this.allCountries.length; j++) {
39463                         if(this.allCountries[j].iso2 == p[i]) {
39464                             var t = this.allCountries[j];
39465                             this.allCountries.splice(j,1);
39466                             this.allCountries.unshift(t);
39467                         }
39468                     } 
39469                 }
39470             }
39471             
39472             this.store.proxy.data = {
39473                 success: true,
39474                 data: this.allCountries
39475             };
39476             
39477             return cfg;
39478         },
39479         
39480         initEvents : function()
39481         {
39482             this.createList();
39483             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39484             
39485             this.indicator = this.indicatorEl();
39486             this.flag = this.flagEl();
39487             this.dialCodeHolder = this.dialCodeHolderEl();
39488             
39489             this.trigger = this.el.select('div.flag-box',true).first();
39490             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39491             
39492             var _this = this;
39493             
39494             (function(){
39495                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39496                 _this.list.setWidth(lw);
39497             }).defer(100);
39498             
39499             this.list.on('mouseover', this.onViewOver, this);
39500             this.list.on('mousemove', this.onViewMove, this);
39501             this.inputEl().on("keyup", this.onKeyUp, this);
39502             
39503             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39504
39505             this.view = new Roo.View(this.list, this.tpl, {
39506                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39507             });
39508             
39509             this.view.on('click', this.onViewClick, this);
39510             this.setValue(this.defaultDialCode);
39511         },
39512         
39513         onTriggerClick : function(e)
39514         {
39515             Roo.log('trigger click');
39516             if(this.disabled){
39517                 return;
39518             }
39519             
39520             if(this.isExpanded()){
39521                 this.collapse();
39522                 this.hasFocus = false;
39523             }else {
39524                 this.store.load({});
39525                 this.hasFocus = true;
39526                 this.expand();
39527             }
39528         },
39529         
39530         isExpanded : function()
39531         {
39532             return this.list.isVisible();
39533         },
39534         
39535         collapse : function()
39536         {
39537             if(!this.isExpanded()){
39538                 return;
39539             }
39540             this.list.hide();
39541             Roo.get(document).un('mousedown', this.collapseIf, this);
39542             Roo.get(document).un('mousewheel', this.collapseIf, this);
39543             this.fireEvent('collapse', this);
39544             this.validate();
39545         },
39546         
39547         expand : function()
39548         {
39549             Roo.log('expand');
39550
39551             if(this.isExpanded() || !this.hasFocus){
39552                 return;
39553             }
39554             
39555             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39556             this.list.setWidth(lw);
39557             
39558             this.list.show();
39559             this.restrictHeight();
39560             
39561             Roo.get(document).on('mousedown', this.collapseIf, this);
39562             Roo.get(document).on('mousewheel', this.collapseIf, this);
39563             
39564             this.fireEvent('expand', this);
39565         },
39566         
39567         restrictHeight : function()
39568         {
39569             this.list.alignTo(this.inputEl(), this.listAlign);
39570             this.list.alignTo(this.inputEl(), this.listAlign);
39571         },
39572         
39573         onViewOver : function(e, t)
39574         {
39575             if(this.inKeyMode){
39576                 return;
39577             }
39578             var item = this.view.findItemFromChild(t);
39579             
39580             if(item){
39581                 var index = this.view.indexOf(item);
39582                 this.select(index, false);
39583             }
39584         },
39585
39586         // private
39587         onViewClick : function(view, doFocus, el, e)
39588         {
39589             var index = this.view.getSelectedIndexes()[0];
39590             
39591             var r = this.store.getAt(index);
39592             
39593             if(r){
39594                 this.onSelect(r, index);
39595             }
39596             if(doFocus !== false && !this.blockFocus){
39597                 this.inputEl().focus();
39598             }
39599         },
39600         
39601         onViewMove : function(e, t)
39602         {
39603             this.inKeyMode = false;
39604         },
39605         
39606         select : function(index, scrollIntoView)
39607         {
39608             this.selectedIndex = index;
39609             this.view.select(index);
39610             if(scrollIntoView !== false){
39611                 var el = this.view.getNode(index);
39612                 if(el){
39613                     this.list.scrollChildIntoView(el, false);
39614                 }
39615             }
39616         },
39617         
39618         createList : function()
39619         {
39620             this.list = Roo.get(document.body).createChild({
39621                 tag: 'ul',
39622                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39623                 style: 'display:none'
39624             });
39625             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39626         },
39627         
39628         collapseIf : function(e)
39629         {
39630             var in_combo  = e.within(this.el);
39631             var in_list =  e.within(this.list);
39632             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39633             
39634             if (in_combo || in_list || is_list) {
39635                 return;
39636             }
39637             this.collapse();
39638         },
39639         
39640         onSelect : function(record, index)
39641         {
39642             if(this.fireEvent('beforeselect', this, record, index) !== false){
39643                 
39644                 this.setFlagClass(record.data.iso2);
39645                 this.setDialCode(record.data.dialCode);
39646                 this.hasFocus = false;
39647                 this.collapse();
39648                 this.fireEvent('select', this, record, index);
39649             }
39650         },
39651         
39652         flagEl : function()
39653         {
39654             var flag = this.el.select('div.flag',true).first();
39655             if(!flag){
39656                 return false;
39657             }
39658             return flag;
39659         },
39660         
39661         dialCodeHolderEl : function()
39662         {
39663             var d = this.el.select('input.dial-code-holder',true).first();
39664             if(!d){
39665                 return false;
39666             }
39667             return d;
39668         },
39669         
39670         setDialCode : function(v)
39671         {
39672             this.dialCodeHolder.dom.value = '+'+v;
39673         },
39674         
39675         setFlagClass : function(n)
39676         {
39677             this.flag.dom.className = 'flag '+n;
39678         },
39679         
39680         getValue : function()
39681         {
39682             var v = this.inputEl().getValue();
39683             if(this.dialCodeHolder) {
39684                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39685             }
39686             return v;
39687         },
39688         
39689         setValue : function(v)
39690         {
39691             var d = this.getDialCode(v);
39692             
39693             //invalid dial code
39694             if(v.length == 0 || !d || d.length == 0) {
39695                 if(this.rendered){
39696                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39697                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39698                 }
39699                 return;
39700             }
39701             
39702             //valid dial code
39703             this.setFlagClass(this.dialCodeMapping[d].iso2);
39704             this.setDialCode(d);
39705             this.inputEl().dom.value = v.replace('+'+d,'');
39706             this.hiddenEl().dom.value = this.getValue();
39707             
39708             this.validate();
39709         },
39710         
39711         getDialCode : function(v = '')
39712         {
39713             if (v.length == 0) {
39714                 return this.dialCodeHolder.dom.value;
39715             }
39716             
39717             var dialCode = "";
39718             if (v.charAt(0) != "+") {
39719                 return false;
39720             }
39721             var numericChars = "";
39722             for (var i = 1; i < v.length; i++) {
39723               var c = v.charAt(i);
39724               if (!isNaN(c)) {
39725                 numericChars += c;
39726                 if (this.dialCodeMapping[numericChars]) {
39727                   dialCode = v.substr(1, i);
39728                 }
39729                 if (numericChars.length == 4) {
39730                   break;
39731                 }
39732               }
39733             }
39734             return dialCode;
39735         },
39736         
39737         reset : function()
39738         {
39739             this.setValue(this.defaultDialCode);
39740             this.validate();
39741         },
39742         
39743         hiddenEl : function()
39744         {
39745             return this.el.select('input.hidden-tel-input',true).first();
39746         },
39747         
39748         onKeyUp : function(e){
39749             
39750             var k = e.getKey();
39751             var c = e.getCharCode();
39752             
39753             if(
39754                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39755                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39756             ){
39757                 e.stopEvent();
39758             }
39759             
39760             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39761             //     return;
39762             // }
39763             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39764                 e.stopEvent();
39765             }
39766             
39767             this.setValue(this.getValue());
39768         }
39769         
39770 });
39771 /**
39772  * @class Roo.bootstrap.MoneyField
39773  * @extends Roo.bootstrap.ComboBox
39774  * Bootstrap MoneyField class
39775  * 
39776  * @constructor
39777  * Create a new MoneyField.
39778  * @param {Object} config Configuration options
39779  */
39780
39781 Roo.bootstrap.MoneyField = function(config) {
39782     
39783     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39784     
39785 };
39786
39787 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39788     
39789     /**
39790      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39791      */
39792     allowDecimals : true,
39793     /**
39794      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39795      */
39796     decimalSeparator : ".",
39797     /**
39798      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39799      */
39800     decimalPrecision : 2,
39801     /**
39802      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39803      */
39804     allowNegative : true,
39805     /**
39806      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39807      */
39808     minValue : Number.NEGATIVE_INFINITY,
39809     /**
39810      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39811      */
39812     maxValue : Number.MAX_VALUE,
39813     /**
39814      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39815      */
39816     minText : "The minimum value for this field is {0}",
39817     /**
39818      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39819      */
39820     maxText : "The maximum value for this field is {0}",
39821     /**
39822      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39823      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39824      */
39825     nanText : "{0} is not a valid number",
39826     /**
39827      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39828      */
39829     castInt : true,
39830     
39831     inputlg : 9,
39832     inputmd : 9,
39833     inputsm : 9,
39834     inputxs : 6,
39835     
39836     store : false,
39837     
39838     getAutoCreate : function()
39839     {
39840         var align = this.labelAlign || this.parentLabelAlign();
39841         
39842         var id = Roo.id();
39843
39844         var cfg = {
39845             cls: 'form-group',
39846             cn: []
39847         };
39848
39849         var input =  {
39850             tag: 'input',
39851             id : id,
39852             cls : 'form-control roo-money-amount-input',
39853             autocomplete: 'new-password'
39854         };
39855         
39856         if (this.name) {
39857             input.name = this.name;
39858         }
39859
39860         if (this.disabled) {
39861             input.disabled = true;
39862         }
39863
39864         var clg = 12 - this.inputlg;
39865         var cmd = 12 - this.inputmd;
39866         var csm = 12 - this.inputsm;
39867         var cxs = 12 - this.inputxs;
39868         
39869         var container = {
39870             tag : 'div',
39871             cls : 'row roo-money-field',
39872             cn : [
39873                 {
39874                     tag : 'div',
39875                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
39876                     cn : [
39877                         {
39878                             tag : 'div',
39879                             cls: 'roo-select2-container input-group',
39880                             cn: [
39881                                 {
39882                                     tag : 'input',
39883                                     cls : 'form-control roo-money-currency-input',
39884                                     autocomplete: 'new-password'
39885                                 },
39886                                 {
39887                                     tag :'span',
39888                                     cls : 'input-group-addon',
39889                                     cn : [
39890                                         {
39891                                             tag: 'span',
39892                                             cls: 'caret'
39893                                         }
39894                                     ]
39895                                 }
39896                             ]
39897                         }
39898                     ]
39899                 },
39900                 {
39901                     tag : 'div',
39902                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
39903                     cn : [
39904                         {
39905                             tag: 'div',
39906                             cls: this.hasFeedback ? 'has-feedback' : '',
39907                             cn: [
39908                                 input
39909                             ]
39910                         }
39911                     ]
39912                 }
39913             ]
39914             
39915         };
39916         
39917         if (this.fieldLabel.length) {
39918             var indicator = {
39919                 tag: 'i',
39920                 tooltip: 'This field is required'
39921             };
39922
39923             var label = {
39924                 tag: 'label',
39925                 'for':  id,
39926                 cls: 'control-label',
39927                 cn: []
39928             };
39929
39930             var label_text = {
39931                 tag: 'span',
39932                 html: this.fieldLabel
39933             };
39934
39935             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39936             label.cn = [
39937                 indicator,
39938                 label_text
39939             ];
39940
39941             if(this.indicatorpos == 'right') {
39942                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39943                 label.cn = [
39944                     label_text,
39945                     indicator
39946                 ];
39947             }
39948
39949             if(align == 'left') {
39950                 container = {
39951                     tag: 'div',
39952                     cn: [
39953                         container
39954                     ]
39955                 };
39956
39957                 if(this.labelWidth > 12){
39958                     label.style = "width: " + this.labelWidth + 'px';
39959                 }
39960                 if(this.labelWidth < 13 && this.labelmd == 0){
39961                     this.labelmd = this.labelWidth;
39962                 }
39963                 if(this.labellg > 0){
39964                     label.cls += ' col-lg-' + this.labellg;
39965                     input.cls += ' col-lg-' + (12 - this.labellg);
39966                 }
39967                 if(this.labelmd > 0){
39968                     label.cls += ' col-md-' + this.labelmd;
39969                     container.cls += ' col-md-' + (12 - this.labelmd);
39970                 }
39971                 if(this.labelsm > 0){
39972                     label.cls += ' col-sm-' + this.labelsm;
39973                     container.cls += ' col-sm-' + (12 - this.labelsm);
39974                 }
39975                 if(this.labelxs > 0){
39976                     label.cls += ' col-xs-' + this.labelxs;
39977                     container.cls += ' col-xs-' + (12 - this.labelxs);
39978                 }
39979             }
39980         }
39981
39982         cfg.cn = [
39983             label,
39984             container
39985         ];
39986
39987         var settings = this;
39988
39989         ['xs','sm','md','lg'].map(function(size){
39990             if (settings[size]) {
39991                 cfg.cls += ' col-' + size + '-' + settings[size];
39992             }
39993         });
39994         
39995         return cfg;
39996         
39997     },
39998     
39999     initEvents : function()
40000     {
40001         this.indicator = this.indicatorEl();
40002         
40003         this.initCurrencyEvent();
40004         
40005         this.initNumberEvent();
40006         
40007     },
40008     
40009     initCurrencyEvent : function()
40010     {
40011         if (!this.store) {
40012             throw "can not find store for combo";
40013         }
40014         
40015         this.store = Roo.factory(this.store, Roo.data);
40016         this.store.parent = this;
40017         
40018         this.createList();
40019         
40020         this.triggerEl = this.el.select('.input-group-addon', true).first();
40021         
40022         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40023         
40024         this.currencyEl = this.el.select('.roo-money-currency-input', true).first();
40025         
40026         this.amountEl = this.el.select('.roo-money-amount-input', true).first();
40027         
40028         var _this = this;
40029         
40030         (function(){
40031             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40032             _this.list.setWidth(lw);
40033         }).defer(100);
40034         
40035         this.list.on('mouseover', this.onViewOver, this);
40036         this.list.on('mousemove', this.onViewMove, this);
40037         this.list.on('scroll', this.onViewScroll, this);
40038         
40039         if(!this.tpl){
40040             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40041         }
40042         
40043         this.view = new Roo.View(this.list, this.tpl, {
40044             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40045         });
40046         
40047         this.view.on('click', this.onViewClick, this);
40048         
40049         this.store.on('beforeload', this.onBeforeLoad, this);
40050         this.store.on('load', this.onLoad, this);
40051         this.store.on('loadexception', this.onLoadException, this);
40052         
40053         this.keyNav = new Roo.KeyNav(this.currencyEl, {
40054             "up" : function(e){
40055                 this.inKeyMode = true;
40056                 this.selectPrev();
40057             },
40058
40059             "down" : function(e){
40060                 if(!this.isExpanded()){
40061                     this.onTriggerClick();
40062                 }else{
40063                     this.inKeyMode = true;
40064                     this.selectNext();
40065                 }
40066             },
40067
40068             "enter" : function(e){
40069                 this.collapse();
40070                 
40071                 if(this.fireEvent("specialkey", this, e)){
40072                     this.onViewClick(false);
40073                 }
40074                 
40075                 return true;
40076             },
40077
40078             "esc" : function(e){
40079                 this.collapse();
40080             },
40081
40082             "tab" : function(e){
40083                 this.collapse();
40084                 
40085                 if(this.fireEvent("specialkey", this, e)){
40086                     this.onViewClick(false);
40087                 }
40088                 
40089                 return true;
40090             },
40091
40092             scope : this,
40093
40094             doRelay : function(foo, bar, hname){
40095                 if(hname == 'down' || this.scope.isExpanded()){
40096                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40097                 }
40098                 return true;
40099             },
40100
40101             forceKeyDown: true
40102         });
40103         
40104     },
40105     
40106     initNumberEvent : function(e)
40107     {
40108         this.inputEl().on("keydown" , this.fireKey,  this);
40109         this.inputEl().on("focus", this.onFocus,  this);
40110         this.inputEl().on("blur", this.onBlur,  this);
40111         
40112         this.inputEl().relayEvent('keyup', this);
40113         
40114         if(this.indicator){
40115             this.indicator.addClass('invisible');
40116         }
40117  
40118         this.originalValue = this.getValue();
40119         
40120         if(this.validationEvent == 'keyup'){
40121             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40122             this.inputEl().on('keyup', this.filterValidation, this);
40123         }
40124         else if(this.validationEvent !== false){
40125             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40126         }
40127         
40128         if(this.selectOnFocus){
40129             this.on("focus", this.preFocus, this);
40130             
40131         }
40132         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40133             this.inputEl().on("keypress", this.filterKeys, this);
40134         } else {
40135             this.inputEl().relayEvent('keypress', this);
40136         }
40137         
40138         var allowed = "0123456789";
40139         
40140         if(this.allowDecimals){
40141             allowed += this.decimalSeparator;
40142         }
40143         
40144         if(this.allowNegative){
40145             allowed += "-";
40146         }
40147         
40148         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40149         
40150         var keyPress = function(e){
40151             
40152             var k = e.getKey();
40153             
40154             var c = e.getCharCode();
40155             
40156             if(
40157                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40158                     allowed.indexOf(String.fromCharCode(c)) === -1
40159             ){
40160                 e.stopEvent();
40161                 return;
40162             }
40163             
40164             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40165                 return;
40166             }
40167             
40168             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40169                 e.stopEvent();
40170             }
40171         };
40172         
40173         this.inputEl().on("keypress", keyPress, this);
40174         
40175     },
40176     
40177     onTriggerClick : function(e)
40178     {   
40179         if(this.disabled){
40180             return;
40181         }
40182         
40183         this.page = 0;
40184         this.loadNext = false;
40185         
40186         if(this.isExpanded()){
40187             this.collapse();
40188             return;
40189         }
40190         
40191         this.hasFocus = true;
40192         
40193         if(this.triggerAction == 'all') {
40194             this.doQuery(this.allQuery, true);
40195             return;
40196         }
40197         
40198         this.doQuery(this.getRawValue());
40199     },
40200     
40201     getCurrency : function()
40202     {   
40203         var v = this.currencyEl.getValue();
40204         
40205         return v;
40206     },
40207     
40208     restrictHeight : function()
40209     {
40210         this.list.alignTo(this.currencyEl, this.listAlign);
40211         this.list.alignTo(this.currencyEl, this.listAlign);
40212     },
40213     
40214     onViewClick : function(view, doFocus, el, e)
40215     {
40216         var index = this.view.getSelectedIndexes()[0];
40217         
40218         var r = this.store.getAt(index);
40219         
40220         if(r){
40221             this.onSelect(r, index);
40222         }
40223     },
40224     
40225     onSelect : function(record, index){
40226         
40227         if(this.fireEvent('beforeselect', this, record, index) !== false){
40228         
40229             this.setFromCurrencyData(index > -1 ? record.data : false);
40230             
40231             this.collapse();
40232             
40233             this.fireEvent('select', this, record, index);
40234         }
40235     },
40236     
40237     setFromCurrencyData : function(o)
40238     {
40239         var currency = '';
40240         
40241         this.lastCurrency = o;
40242         
40243         if (this.currencyName) {
40244             currency = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40245         } else {
40246             Roo.log('no  currencyName value set for '+ (this.name ? this.name : this.id));
40247         }
40248         
40249         this.lastSelectionText = currency;
40250         
40251         this.setCurrency(currency);
40252     },
40253     
40254     setFromData : function(o)
40255     {
40256         this.setFromCurrencyData(o);
40257         
40258         var value = '';
40259         
40260         if (this.name) {
40261             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40262         } else {
40263             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40264         }
40265         
40266         this.setValue(value);
40267         
40268     },
40269     
40270     setCurrency : function(v)
40271     {   
40272         this.currencyValue = v;
40273         
40274         if(this.rendered){
40275             this.currencyEl.dom.value = (v === null || v === undefined ? '' : v);
40276             this.validate();
40277         }
40278     },
40279     
40280     setValue : function(v)
40281     {
40282         v = this.fixPrecision(v);
40283         
40284         v = String(v).replace(".", this.decimalSeparator);
40285         
40286         this.value = v;
40287         
40288         if(this.rendered){
40289             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40290             this.validate();
40291         }
40292     },
40293     
40294     getRawValue : function()
40295     {
40296         var v = this.inputEl().getValue();
40297         
40298         return v;
40299     },
40300     
40301     getValue : function()
40302     {
40303         return this.fixPrecision(this.parseValue(this.getRawValue()));
40304     },
40305     
40306     parseValue : function(value)
40307     {
40308         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40309         return isNaN(value) ? '' : value;
40310     },
40311     
40312     fixPrecision : function(value)
40313     {
40314         var nan = isNaN(value);
40315         
40316         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40317             return nan ? '' : value;
40318         }
40319         
40320         return parseFloat(value).toFixed(this.decimalPrecision);
40321     },
40322     
40323     decimalPrecisionFcn : function(v)
40324     {
40325         return Math.floor(v);
40326     },
40327     
40328     validateValue : function(value)
40329     {
40330         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40331             return false;
40332         }
40333         
40334         var num = this.parseValue(value);
40335         
40336         if(isNaN(num)){
40337             this.markInvalid(String.format(this.nanText, value));
40338             return false;
40339         }
40340         
40341         if(num < this.minValue){
40342             this.markInvalid(String.format(this.minText, this.minValue));
40343             return false;
40344         }
40345         
40346         if(num > this.maxValue){
40347             this.markInvalid(String.format(this.maxText, this.maxValue));
40348             return false;
40349         }
40350         
40351         return true;
40352     },
40353     
40354     validate : function()
40355     {
40356         if(this.disabled){
40357             this.markValid();
40358             return true;
40359         }
40360         
40361         var currency = this.getCurrency();
40362         
40363         if(this.validateValue(this.getRawValue()) && currency.length){
40364             this.markValid();
40365             return true;
40366         }
40367         
40368         this.markInvalid();
40369         return false;
40370     },
40371     
40372     getName: function()
40373     {
40374         return this.name;
40375     },
40376     
40377     beforeBlur : function()
40378     {
40379         if(!this.castInt){
40380             return;
40381         }
40382         
40383         var v = this.parseValue(this.getRawValue());
40384         
40385         if(v){
40386             this.setValue(v);
40387         }
40388     },
40389     
40390     onBlur : function()
40391     {
40392         this.beforeBlur();
40393         
40394         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40395             //this.el.removeClass(this.focusClass);
40396         }
40397         
40398         this.hasFocus = false;
40399         
40400         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40401             this.validate();
40402         }
40403         
40404         var v = this.getValue();
40405         
40406         if(String(v) !== String(this.startValue)){
40407             this.fireEvent('change', this, v, this.startValue);
40408         }
40409         
40410         this.fireEvent("blur", this);
40411     },
40412     
40413     inputEl : function()
40414     {
40415         return this.amountEl ? this.amountEl : this.el.select('.roo-money-amount-input', true).first();
40416     }
40417     
40418 });