roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  *
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  *
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @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)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.addEvents({
538         // raw events
539         /**
540          * @event click
541          * When a butotn is pressed
542          * @param {Roo.bootstrap.Button} this
543          * @param {Roo.EventObject} e
544          */
545         "click" : true,
546          /**
547          * @event toggle
548          * After the button has been toggles
549          * @param {Roo.EventObject} e
550          * @param {boolean} pressed (also available as button.pressed)
551          */
552         "toggle" : true
553     });
554 };
555
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
557     html: false,
558     active: false,
559     weight: '',
560     size: '',
561     tag: 'button',
562     href: '',
563     disabled: false,
564     isClose: false,
565     glyphicon: '',
566     badge: '',
567     theme: 'default',
568     inverse: false,
569     
570     toggle: false,
571     ontext: 'ON',
572     offtext: 'OFF',
573     defaulton: true,
574     preventDefault: true,
575     removeClass: false,
576     name: false,
577     target: false,
578     
579     
580     pressed : null,
581      
582     
583     getAutoCreate : function(){
584         
585         var cfg = {
586             tag : 'button',
587             cls : 'roo-button',
588             html: ''
589         };
590         
591         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
593             this.tag = 'button';
594         } else {
595             cfg.tag = this.tag;
596         }
597         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
598         
599         if (this.toggle == true) {
600             cfg={
601                 tag: 'div',
602                 cls: 'slider-frame roo-button',
603                 cn: [
604                     {
605                         tag: 'span',
606                         'data-on-text':'ON',
607                         'data-off-text':'OFF',
608                         cls: 'slider-button',
609                         html: this.offtext
610                     }
611                 ]
612             };
613             
614             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615                 cfg.cls += ' '+this.weight;
616             }
617             
618             return cfg;
619         }
620         
621         if (this.isClose) {
622             cfg.cls += ' close';
623             
624             cfg["aria-hidden"] = true;
625             
626             cfg.html = "&times;";
627             
628             return cfg;
629         }
630         
631          
632         if (this.theme==='default') {
633             cfg.cls = 'btn roo-button';
634             
635             //if (this.parentType != 'Navbar') {
636             this.weight = this.weight.length ?  this.weight : 'default';
637             //}
638             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
639                 
640                 cfg.cls += ' btn-' + this.weight;
641             }
642         } else if (this.theme==='glow') {
643             
644             cfg.tag = 'a';
645             cfg.cls = 'btn-glow roo-button';
646             
647             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
648                 
649                 cfg.cls += ' ' + this.weight;
650             }
651         }
652    
653         
654         if (this.inverse) {
655             this.cls += ' inverse';
656         }
657         
658         
659         if (this.active) {
660             cfg.cls += ' active';
661         }
662         
663         if (this.disabled) {
664             cfg.disabled = 'disabled';
665         }
666         
667         if (this.items) {
668             Roo.log('changing to ul' );
669             cfg.tag = 'ul';
670             this.glyphicon = 'caret';
671         }
672         
673         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
674          
675         //gsRoo.log(this.parentType);
676         if (this.parentType === 'Navbar' && !this.parent().bar) {
677             Roo.log('changing to li?');
678             
679             cfg.tag = 'li';
680             
681             cfg.cls = '';
682             cfg.cn =  [{
683                 tag : 'a',
684                 cls : 'roo-button',
685                 html : this.html,
686                 href : this.href || '#'
687             }];
688             if (this.menu) {
689                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
690                 cfg.cls += ' dropdown';
691             }   
692             
693             delete cfg.html;
694             
695         }
696         
697        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
698         
699         if (this.glyphicon) {
700             cfg.html = ' ' + cfg.html;
701             
702             cfg.cn = [
703                 {
704                     tag: 'span',
705                     cls: 'glyphicon glyphicon-' + this.glyphicon
706                 }
707             ];
708         }
709         
710         if (this.badge) {
711             cfg.html += ' ';
712             
713             cfg.tag = 'a';
714             
715 //            cfg.cls='btn roo-button';
716             
717             cfg.href=this.href;
718             
719             var value = cfg.html;
720             
721             if(this.glyphicon){
722                 value = {
723                             tag: 'span',
724                             cls: 'glyphicon glyphicon-' + this.glyphicon,
725                             html: this.html
726                         };
727                 
728             }
729             
730             cfg.cn = [
731                 value,
732                 {
733                     tag: 'span',
734                     cls: 'badge',
735                     html: this.badge
736                 }
737             ];
738             
739             cfg.html='';
740         }
741         
742         if (this.menu) {
743             cfg.cls += ' dropdown';
744             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745         }
746         
747         if (cfg.tag !== 'a' && this.href !== '') {
748             throw "Tag must be a to set href.";
749         } else if (this.href.length > 0) {
750             cfg.href = this.href;
751         }
752         
753         if(this.removeClass){
754             cfg.cls = '';
755         }
756         
757         if(this.target){
758             cfg.target = this.target;
759         }
760         
761         return cfg;
762     },
763     initEvents: function() {
764        // Roo.log('init events?');
765 //        Roo.log(this.el.dom);
766         // add the menu...
767         
768         if (typeof (this.menu) != 'undefined') {
769             this.menu.parentType = this.xtype;
770             this.menu.triggerEl = this.el;
771             this.addxtype(Roo.apply({}, this.menu));
772         }
773
774
775        if (this.el.hasClass('roo-button')) {
776             this.el.on('click', this.onClick, this);
777        } else {
778             this.el.select('.roo-button').on('click', this.onClick, this);
779        }
780        
781        if(this.removeClass){
782            this.el.on('click', this.onClick, this);
783        }
784        
785        this.el.enableDisplayMode();
786         
787     },
788     onClick : function(e)
789     {
790         if (this.disabled) {
791             return;
792         }
793         
794         
795         Roo.log('button on click ');
796         if(this.preventDefault){
797             e.preventDefault();
798         }
799         if (this.pressed === true || this.pressed === false) {
800             this.pressed = !this.pressed;
801             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802             this.fireEvent('toggle', this, e, this.pressed);
803         }
804         
805         
806         this.fireEvent('click', this, e);
807     },
808     
809     /**
810      * Enables this button
811      */
812     enable : function()
813     {
814         this.disabled = false;
815         this.el.removeClass('disabled');
816     },
817     
818     /**
819      * Disable this button
820      */
821     disable : function()
822     {
823         this.disabled = true;
824         this.el.addClass('disabled');
825     },
826      /**
827      * sets the active state on/off, 
828      * @param {Boolean} state (optional) Force a particular state
829      */
830     setActive : function(v) {
831         
832         this.el[v ? 'addClass' : 'removeClass']('active');
833     },
834      /**
835      * toggles the current active state 
836      */
837     toggleActive : function()
838     {
839        var active = this.el.hasClass('active');
840        this.setActive(!active);
841        
842         
843     },
844     setText : function(str)
845     {
846         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
847     },
848     getText : function()
849     {
850         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
851     },
852     hide: function() {
853        
854      
855         this.el.hide();   
856     },
857     show: function() {
858        
859         this.el.show();   
860     }
861     
862     
863 });
864
865  /*
866  * - LGPL
867  *
868  * column
869  * 
870  */
871
872 /**
873  * @class Roo.bootstrap.Column
874  * @extends Roo.bootstrap.Component
875  * Bootstrap Column class
876  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884  *
885  * 
886  * @cfg {Boolean} hidden (true|false) hide the element
887  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888  * @cfg {String} fa (ban|check|...) font awesome icon
889  * @cfg {Number} fasize (1|2|....) font awsome size
890
891  * @cfg {String} icon (info-sign|check|...) glyphicon name
892
893  * @cfg {String} html content of column.
894  * 
895  * @constructor
896  * Create a new Column
897  * @param {Object} config The config object
898  */
899
900 Roo.bootstrap.Column = function(config){
901     Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 };
903
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
905     
906     xs: false,
907     sm: false,
908     md: false,
909     lg: false,
910     xsoff: false,
911     smoff: false,
912     mdoff: false,
913     lgoff: false,
914     html: '',
915     offset: 0,
916     alert: false,
917     fa: false,
918     icon : false,
919     hidden : false,
920     fasize : 1,
921     
922     getAutoCreate : function(){
923         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
924         
925         cfg = {
926             tag: 'div',
927             cls: 'column'
928         };
929         
930         var settings=this;
931         ['xs','sm','md','lg'].map(function(size){
932             //Roo.log( size + ':' + settings[size]);
933             
934             if (settings[size+'off'] !== false) {
935                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936             }
937             
938             if (settings[size] === false) {
939                 return;
940             }
941             
942             if (!settings[size]) { // 0 = hidden
943                 cfg.cls += ' hidden-' + size;
944                 return;
945             }
946             cfg.cls += ' col-' + size + '-' + settings[size];
947             
948         });
949         
950         if (this.hidden) {
951             cfg.cls += ' hidden';
952         }
953         
954         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955             cfg.cls +=' alert alert-' + this.alert;
956         }
957         
958         
959         if (this.html.length) {
960             cfg.html = this.html;
961         }
962         if (this.fa) {
963             var fasize = '';
964             if (this.fasize > 1) {
965                 fasize = ' fa-' + this.fasize + 'x';
966             }
967             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
968             
969             
970         }
971         if (this.icon) {
972             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
973         }
974         
975         return cfg;
976     }
977    
978 });
979
980  
981
982  /*
983  * - LGPL
984  *
985  * page container.
986  * 
987  */
988
989
990 /**
991  * @class Roo.bootstrap.Container
992  * @extends Roo.bootstrap.Component
993  * Bootstrap Container class
994  * @cfg {Boolean} jumbotron is it a jumbotron element
995  * @cfg {String} html content of element
996  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
998  * @cfg {String} header content of header (for panel)
999  * @cfg {String} footer content of footer (for panel)
1000  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001  * @cfg {String} tag (header|aside|section) type of HTML tag.
1002  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003  * @cfg {String} fa font awesome icon
1004  * @cfg {String} icon (info-sign|check|...) glyphicon name
1005  * @cfg {Boolean} hidden (true|false) hide the element
1006  * @cfg {Boolean} expandable (true|false) default false
1007  * @cfg {Boolean} expanded (true|false) default true
1008  * @cfg {String} rheader contet on the right of header
1009  * @cfg {Boolean} clickable (true|false) default false
1010
1011  *     
1012  * @constructor
1013  * Create a new Container
1014  * @param {Object} config The config object
1015  */
1016
1017 Roo.bootstrap.Container = function(config){
1018     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1019     
1020     this.addEvents({
1021         // raw events
1022          /**
1023          * @event expand
1024          * After the panel has been expand
1025          * 
1026          * @param {Roo.bootstrap.Container} this
1027          */
1028         "expand" : true,
1029         /**
1030          * @event collapse
1031          * After the panel has been collapsed
1032          * 
1033          * @param {Roo.bootstrap.Container} this
1034          */
1035         "collapse" : true,
1036         /**
1037          * @event click
1038          * When a element is chick
1039          * @param {Roo.bootstrap.Container} this
1040          * @param {Roo.EventObject} e
1041          */
1042         "click" : true
1043     });
1044 };
1045
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1047     
1048     jumbotron : false,
1049     well: '',
1050     panel : '',
1051     header: '',
1052     footer : '',
1053     sticky: '',
1054     tag : false,
1055     alert : false,
1056     fa: false,
1057     icon : false,
1058     expandable : false,
1059     rheader : '',
1060     expanded : true,
1061     clickable: false,
1062   
1063      
1064     getChildContainer : function() {
1065         
1066         if(!this.el){
1067             return false;
1068         }
1069         
1070         if (this.panel.length) {
1071             return this.el.select('.panel-body',true).first();
1072         }
1073         
1074         return this.el;
1075     },
1076     
1077     
1078     getAutoCreate : function(){
1079         
1080         var cfg = {
1081             tag : this.tag || 'div',
1082             html : '',
1083             cls : ''
1084         };
1085         if (this.jumbotron) {
1086             cfg.cls = 'jumbotron';
1087         }
1088         
1089         
1090         
1091         // - this is applied by the parent..
1092         //if (this.cls) {
1093         //    cfg.cls = this.cls + '';
1094         //}
1095         
1096         if (this.sticky.length) {
1097             
1098             var bd = Roo.get(document.body);
1099             if (!bd.hasClass('bootstrap-sticky')) {
1100                 bd.addClass('bootstrap-sticky');
1101                 Roo.select('html',true).setStyle('height', '100%');
1102             }
1103              
1104             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1105         }
1106         
1107         
1108         if (this.well.length) {
1109             switch (this.well) {
1110                 case 'lg':
1111                 case 'sm':
1112                     cfg.cls +=' well well-' +this.well;
1113                     break;
1114                 default:
1115                     cfg.cls +=' well';
1116                     break;
1117             }
1118         }
1119         
1120         if (this.hidden) {
1121             cfg.cls += ' hidden';
1122         }
1123         
1124         
1125         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126             cfg.cls +=' alert alert-' + this.alert;
1127         }
1128         
1129         var body = cfg;
1130         
1131         if (this.panel.length) {
1132             cfg.cls += ' panel panel-' + this.panel;
1133             cfg.cn = [];
1134             if (this.header.length) {
1135                 
1136                 var h = [];
1137                 
1138                 if(this.expandable){
1139                     
1140                     cfg.cls = cfg.cls + ' expandable';
1141                     
1142                     h.push({
1143                         tag: 'i',
1144                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1145                     });
1146                     
1147                 }
1148                 
1149                 h.push(
1150                     {
1151                         tag: 'span',
1152                         cls : 'panel-title',
1153                         html : (this.expandable ? '&nbsp;' : '') + this.header
1154                     },
1155                     {
1156                         tag: 'span',
1157                         cls: 'panel-header-right',
1158                         html: this.rheader
1159                     }
1160                 );
1161                 
1162                 cfg.cn.push({
1163                     cls : 'panel-heading',
1164                     style : this.expandable ? 'cursor: pointer' : '',
1165                     cn : h
1166                 });
1167                 
1168             }
1169             
1170             body = false;
1171             cfg.cn.push({
1172                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1173                 html : this.html
1174             });
1175             
1176             
1177             if (this.footer.length) {
1178                 cfg.cn.push({
1179                     cls : 'panel-footer',
1180                     html : this.footer
1181                     
1182                 });
1183             }
1184             
1185         }
1186         
1187         if (body) {
1188             body.html = this.html || cfg.html;
1189             // prefix with the icons..
1190             if (this.fa) {
1191                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192             }
1193             if (this.icon) {
1194                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1195             }
1196             
1197             
1198         }
1199         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200             cfg.cls =  'container';
1201         }
1202         
1203         return cfg;
1204     },
1205     
1206     initEvents: function() 
1207     {
1208         if(this.expandable){
1209             var headerEl = this.headerEl();
1210         
1211             if(headerEl){
1212                 headerEl.on('click', this.onToggleClick, this);
1213             }
1214         }
1215         
1216         if(this.clickable){
1217             this.el.on('click', this.onClick, this);
1218         }
1219         
1220     },
1221     
1222     onToggleClick : function()
1223     {
1224         var headerEl = this.headerEl();
1225         
1226         if(!headerEl){
1227             return;
1228         }
1229         
1230         if(this.expanded){
1231             this.collapse();
1232             return;
1233         }
1234         
1235         this.expand();
1236     },
1237     
1238     expand : function()
1239     {
1240         if(this.fireEvent('expand', this)) {
1241             
1242             this.expanded = true;
1243             
1244             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1245             
1246             this.el.select('.panel-body',true).first().removeClass('hide');
1247             
1248             var toggleEl = this.toggleEl();
1249
1250             if(!toggleEl){
1251                 return;
1252             }
1253
1254             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1255         }
1256         
1257     },
1258     
1259     collapse : function()
1260     {
1261         if(this.fireEvent('collapse', this)) {
1262             
1263             this.expanded = false;
1264             
1265             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266             this.el.select('.panel-body',true).first().addClass('hide');
1267         
1268             var toggleEl = this.toggleEl();
1269
1270             if(!toggleEl){
1271                 return;
1272             }
1273
1274             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1275         }
1276     },
1277     
1278     toggleEl : function()
1279     {
1280         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1281             return;
1282         }
1283         
1284         return this.el.select('.panel-heading .fa',true).first();
1285     },
1286     
1287     headerEl : function()
1288     {
1289         if(!this.el || !this.panel.length || !this.header.length){
1290             return;
1291         }
1292         
1293         return this.el.select('.panel-heading',true).first()
1294     },
1295     
1296     titleEl : function()
1297     {
1298         if(!this.el || !this.panel.length || !this.header.length){
1299             return;
1300         }
1301         
1302         return this.el.select('.panel-title',true).first();
1303     },
1304     
1305     setTitle : function(v)
1306     {
1307         var titleEl = this.titleEl();
1308         
1309         if(!titleEl){
1310             return;
1311         }
1312         
1313         titleEl.dom.innerHTML = v;
1314     },
1315     
1316     getTitle : function()
1317     {
1318         
1319         var titleEl = this.titleEl();
1320         
1321         if(!titleEl){
1322             return '';
1323         }
1324         
1325         return titleEl.dom.innerHTML;
1326     },
1327     
1328     setRightTitle : function(v)
1329     {
1330         var t = this.el.select('.panel-header-right',true).first();
1331         
1332         if(!t){
1333             return;
1334         }
1335         
1336         t.dom.innerHTML = v;
1337     },
1338     
1339     onClick : function(e)
1340     {
1341         e.preventDefault();
1342         
1343         this.fireEvent('click', this, e);
1344     }
1345    
1346 });
1347
1348  /*
1349  * - LGPL
1350  *
1351  * image
1352  * 
1353  */
1354
1355
1356 /**
1357  * @class Roo.bootstrap.Img
1358  * @extends Roo.bootstrap.Component
1359  * Bootstrap Img class
1360  * @cfg {Boolean} imgResponsive false | true
1361  * @cfg {String} border rounded | circle | thumbnail
1362  * @cfg {String} src image source
1363  * @cfg {String} alt image alternative text
1364  * @cfg {String} href a tag href
1365  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366  * @cfg {String} xsUrl xs image source
1367  * @cfg {String} smUrl sm image source
1368  * @cfg {String} mdUrl md image source
1369  * @cfg {String} lgUrl lg image source
1370  * 
1371  * @constructor
1372  * Create a new Input
1373  * @param {Object} config The config object
1374  */
1375
1376 Roo.bootstrap.Img = function(config){
1377     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1378     
1379     this.addEvents({
1380         // img events
1381         /**
1382          * @event click
1383          * The img click event for the img.
1384          * @param {Roo.EventObject} e
1385          */
1386         "click" : true
1387     });
1388 };
1389
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1391     
1392     imgResponsive: true,
1393     border: '',
1394     src: 'about:blank',
1395     href: false,
1396     target: false,
1397     xsUrl: '',
1398     smUrl: '',
1399     mdUrl: '',
1400     lgUrl: '',
1401
1402     getAutoCreate : function()
1403     {   
1404         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405             return this.createSingleImg();
1406         }
1407         
1408         var cfg = {
1409             tag: 'div',
1410             cls: 'roo-image-responsive-group',
1411             cn: []
1412         };
1413         var _this = this;
1414         
1415         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1416             
1417             if(!_this[size + 'Url']){
1418                 return;
1419             }
1420             
1421             var img = {
1422                 tag: 'img',
1423                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424                 html: _this.html || cfg.html,
1425                 src: _this[size + 'Url']
1426             };
1427             
1428             img.cls += ' roo-image-responsive-' + size;
1429             
1430             var s = ['xs', 'sm', 'md', 'lg'];
1431             
1432             s.splice(s.indexOf(size), 1);
1433             
1434             Roo.each(s, function(ss){
1435                 img.cls += ' hidden-' + ss;
1436             });
1437             
1438             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439                 cfg.cls += ' img-' + _this.border;
1440             }
1441             
1442             if(_this.alt){
1443                 cfg.alt = _this.alt;
1444             }
1445             
1446             if(_this.href){
1447                 var a = {
1448                     tag: 'a',
1449                     href: _this.href,
1450                     cn: [
1451                         img
1452                     ]
1453                 };
1454
1455                 if(this.target){
1456                     a.target = _this.target;
1457                 }
1458             }
1459             
1460             cfg.cn.push((_this.href) ? a : img);
1461             
1462         });
1463         
1464         return cfg;
1465     },
1466     
1467     createSingleImg : function()
1468     {
1469         var cfg = {
1470             tag: 'img',
1471             cls: (this.imgResponsive) ? 'img-responsive' : '',
1472             html : null,
1473             src : 'about:blank'  // just incase src get's set to undefined?!?
1474         };
1475         
1476         cfg.html = this.html || cfg.html;
1477         
1478         cfg.src = this.src || cfg.src;
1479         
1480         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481             cfg.cls += ' img-' + this.border;
1482         }
1483         
1484         if(this.alt){
1485             cfg.alt = this.alt;
1486         }
1487         
1488         if(this.href){
1489             var a = {
1490                 tag: 'a',
1491                 href: this.href,
1492                 cn: [
1493                     cfg
1494                 ]
1495             };
1496             
1497             if(this.target){
1498                 a.target = this.target;
1499             }
1500             
1501         }
1502         
1503         return (this.href) ? a : cfg;
1504     },
1505     
1506     initEvents: function() 
1507     {
1508         if(!this.href){
1509             this.el.on('click', this.onClick, this);
1510         }
1511         
1512     },
1513     
1514     onClick : function(e)
1515     {
1516         Roo.log('img onclick');
1517         this.fireEvent('click', this, e);
1518     },
1519     /**
1520      * Sets the url of the image - used to update it
1521      * @param {String} url the url of the image
1522      */
1523     
1524     setSrc : function(url)
1525     {
1526         this.src =  url;
1527         
1528         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1529             this.el.dom.src =  url;
1530             return;
1531         }
1532         
1533         this.el.select('img', true).first().dom.src =  url;
1534     }
1535     
1536     
1537    
1538 });
1539
1540  /*
1541  * - LGPL
1542  *
1543  * image
1544  * 
1545  */
1546
1547
1548 /**
1549  * @class Roo.bootstrap.Link
1550  * @extends Roo.bootstrap.Component
1551  * Bootstrap Link Class
1552  * @cfg {String} alt image alternative text
1553  * @cfg {String} href a tag href
1554  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1555  * @cfg {String} html the content of the link.
1556  * @cfg {String} anchor name for the anchor link
1557  * @cfg {String} fa - favicon
1558
1559  * @cfg {Boolean} preventDefault (true | false) default false
1560
1561  * 
1562  * @constructor
1563  * Create a new Input
1564  * @param {Object} config The config object
1565  */
1566
1567 Roo.bootstrap.Link = function(config){
1568     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1569     
1570     this.addEvents({
1571         // img events
1572         /**
1573          * @event click
1574          * The img click event for the img.
1575          * @param {Roo.EventObject} e
1576          */
1577         "click" : true
1578     });
1579 };
1580
1581 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1582     
1583     href: false,
1584     target: false,
1585     preventDefault: false,
1586     anchor : false,
1587     alt : false,
1588     fa: false,
1589
1590
1591     getAutoCreate : function()
1592     {
1593         var html = this.html || '';
1594         
1595         if (this.fa !== false) {
1596             html = '<i class="fa fa-' + this.fa + '"></i>';
1597         }
1598         var cfg = {
1599             tag: 'a'
1600         };
1601         // anchor's do not require html/href...
1602         if (this.anchor === false) {
1603             cfg.html = html;
1604             cfg.href = this.href || '#';
1605         } else {
1606             cfg.name = this.anchor;
1607             if (this.html !== false || this.fa !== false) {
1608                 cfg.html = html;
1609             }
1610             if (this.href !== false) {
1611                 cfg.href = this.href;
1612             }
1613         }
1614         
1615         if(this.alt !== false){
1616             cfg.alt = this.alt;
1617         }
1618         
1619         
1620         if(this.target !== false) {
1621             cfg.target = this.target;
1622         }
1623         
1624         return cfg;
1625     },
1626     
1627     initEvents: function() {
1628         
1629         if(!this.href || this.preventDefault){
1630             this.el.on('click', this.onClick, this);
1631         }
1632     },
1633     
1634     onClick : function(e)
1635     {
1636         if(this.preventDefault){
1637             e.preventDefault();
1638         }
1639         //Roo.log('img onclick');
1640         this.fireEvent('click', this, e);
1641     }
1642    
1643 });
1644
1645  /*
1646  * - LGPL
1647  *
1648  * header
1649  * 
1650  */
1651
1652 /**
1653  * @class Roo.bootstrap.Header
1654  * @extends Roo.bootstrap.Component
1655  * Bootstrap Header class
1656  * @cfg {String} html content of header
1657  * @cfg {Number} level (1|2|3|4|5|6) default 1
1658  * 
1659  * @constructor
1660  * Create a new Header
1661  * @param {Object} config The config object
1662  */
1663
1664
1665 Roo.bootstrap.Header  = function(config){
1666     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1667 };
1668
1669 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1670     
1671     //href : false,
1672     html : false,
1673     level : 1,
1674     
1675     
1676     
1677     getAutoCreate : function(){
1678         
1679         
1680         
1681         var cfg = {
1682             tag: 'h' + (1 *this.level),
1683             html: this.html || ''
1684         } ;
1685         
1686         return cfg;
1687     }
1688    
1689 });
1690
1691  
1692
1693  /*
1694  * Based on:
1695  * Ext JS Library 1.1.1
1696  * Copyright(c) 2006-2007, Ext JS, LLC.
1697  *
1698  * Originally Released Under LGPL - original licence link has changed is not relivant.
1699  *
1700  * Fork - LGPL
1701  * <script type="text/javascript">
1702  */
1703  
1704 /**
1705  * @class Roo.bootstrap.MenuMgr
1706  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1707  * @singleton
1708  */
1709 Roo.bootstrap.MenuMgr = function(){
1710    var menus, active, groups = {}, attached = false, lastShow = new Date();
1711
1712    // private - called when first menu is created
1713    function init(){
1714        menus = {};
1715        active = new Roo.util.MixedCollection();
1716        Roo.get(document).addKeyListener(27, function(){
1717            if(active.length > 0){
1718                hideAll();
1719            }
1720        });
1721    }
1722
1723    // private
1724    function hideAll(){
1725        if(active && active.length > 0){
1726            var c = active.clone();
1727            c.each(function(m){
1728                m.hide();
1729            });
1730        }
1731    }
1732
1733    // private
1734    function onHide(m){
1735        active.remove(m);
1736        if(active.length < 1){
1737            Roo.get(document).un("mouseup", onMouseDown);
1738             
1739            attached = false;
1740        }
1741    }
1742
1743    // private
1744    function onShow(m){
1745        var last = active.last();
1746        lastShow = new Date();
1747        active.add(m);
1748        if(!attached){
1749           Roo.get(document).on("mouseup", onMouseDown);
1750            
1751            attached = true;
1752        }
1753        if(m.parentMenu){
1754           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1755           m.parentMenu.activeChild = m;
1756        }else if(last && last.isVisible()){
1757           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1758        }
1759    }
1760
1761    // private
1762    function onBeforeHide(m){
1763        if(m.activeChild){
1764            m.activeChild.hide();
1765        }
1766        if(m.autoHideTimer){
1767            clearTimeout(m.autoHideTimer);
1768            delete m.autoHideTimer;
1769        }
1770    }
1771
1772    // private
1773    function onBeforeShow(m){
1774        var pm = m.parentMenu;
1775        if(!pm && !m.allowOtherMenus){
1776            hideAll();
1777        }else if(pm && pm.activeChild && active != m){
1778            pm.activeChild.hide();
1779        }
1780    }
1781
1782    // private this should really trigger on mouseup..
1783    function onMouseDown(e){
1784         Roo.log("on Mouse Up");
1785         
1786         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1787             Roo.log("MenuManager hideAll");
1788             hideAll();
1789             e.stopEvent();
1790         }
1791         
1792         
1793    }
1794
1795    // private
1796    function onBeforeCheck(mi, state){
1797        if(state){
1798            var g = groups[mi.group];
1799            for(var i = 0, l = g.length; i < l; i++){
1800                if(g[i] != mi){
1801                    g[i].setChecked(false);
1802                }
1803            }
1804        }
1805    }
1806
1807    return {
1808
1809        /**
1810         * Hides all menus that are currently visible
1811         */
1812        hideAll : function(){
1813             hideAll();  
1814        },
1815
1816        // private
1817        register : function(menu){
1818            if(!menus){
1819                init();
1820            }
1821            menus[menu.id] = menu;
1822            menu.on("beforehide", onBeforeHide);
1823            menu.on("hide", onHide);
1824            menu.on("beforeshow", onBeforeShow);
1825            menu.on("show", onShow);
1826            var g = menu.group;
1827            if(g && menu.events["checkchange"]){
1828                if(!groups[g]){
1829                    groups[g] = [];
1830                }
1831                groups[g].push(menu);
1832                menu.on("checkchange", onCheck);
1833            }
1834        },
1835
1836         /**
1837          * Returns a {@link Roo.menu.Menu} object
1838          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1839          * be used to generate and return a new Menu instance.
1840          */
1841        get : function(menu){
1842            if(typeof menu == "string"){ // menu id
1843                return menus[menu];
1844            }else if(menu.events){  // menu instance
1845                return menu;
1846            }
1847            /*else if(typeof menu.length == 'number'){ // array of menu items?
1848                return new Roo.bootstrap.Menu({items:menu});
1849            }else{ // otherwise, must be a config
1850                return new Roo.bootstrap.Menu(menu);
1851            }
1852            */
1853            return false;
1854        },
1855
1856        // private
1857        unregister : function(menu){
1858            delete menus[menu.id];
1859            menu.un("beforehide", onBeforeHide);
1860            menu.un("hide", onHide);
1861            menu.un("beforeshow", onBeforeShow);
1862            menu.un("show", onShow);
1863            var g = menu.group;
1864            if(g && menu.events["checkchange"]){
1865                groups[g].remove(menu);
1866                menu.un("checkchange", onCheck);
1867            }
1868        },
1869
1870        // private
1871        registerCheckable : function(menuItem){
1872            var g = menuItem.group;
1873            if(g){
1874                if(!groups[g]){
1875                    groups[g] = [];
1876                }
1877                groups[g].push(menuItem);
1878                menuItem.on("beforecheckchange", onBeforeCheck);
1879            }
1880        },
1881
1882        // private
1883        unregisterCheckable : function(menuItem){
1884            var g = menuItem.group;
1885            if(g){
1886                groups[g].remove(menuItem);
1887                menuItem.un("beforecheckchange", onBeforeCheck);
1888            }
1889        }
1890    };
1891 }();/*
1892  * - LGPL
1893  *
1894  * menu
1895  * 
1896  */
1897
1898 /**
1899  * @class Roo.bootstrap.Menu
1900  * @extends Roo.bootstrap.Component
1901  * Bootstrap Menu class - container for MenuItems
1902  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1903  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1904  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1905  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1906  * 
1907  * @constructor
1908  * Create a new Menu
1909  * @param {Object} config The config object
1910  */
1911
1912
1913 Roo.bootstrap.Menu = function(config){
1914     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1915     if (this.registerMenu && this.type != 'treeview')  {
1916         Roo.bootstrap.MenuMgr.register(this);
1917     }
1918     this.addEvents({
1919         /**
1920          * @event beforeshow
1921          * Fires before this menu is displayed
1922          * @param {Roo.menu.Menu} this
1923          */
1924         beforeshow : true,
1925         /**
1926          * @event beforehide
1927          * Fires before this menu is hidden
1928          * @param {Roo.menu.Menu} this
1929          */
1930         beforehide : true,
1931         /**
1932          * @event show
1933          * Fires after this menu is displayed
1934          * @param {Roo.menu.Menu} this
1935          */
1936         show : true,
1937         /**
1938          * @event hide
1939          * Fires after this menu is hidden
1940          * @param {Roo.menu.Menu} this
1941          */
1942         hide : true,
1943         /**
1944          * @event click
1945          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1946          * @param {Roo.menu.Menu} this
1947          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948          * @param {Roo.EventObject} e
1949          */
1950         click : true,
1951         /**
1952          * @event mouseover
1953          * Fires when the mouse is hovering over this menu
1954          * @param {Roo.menu.Menu} this
1955          * @param {Roo.EventObject} e
1956          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1957          */
1958         mouseover : true,
1959         /**
1960          * @event mouseout
1961          * Fires when the mouse exits this menu
1962          * @param {Roo.menu.Menu} this
1963          * @param {Roo.EventObject} e
1964          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1965          */
1966         mouseout : true,
1967         /**
1968          * @event itemclick
1969          * Fires when a menu item contained in this menu is clicked
1970          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1971          * @param {Roo.EventObject} e
1972          */
1973         itemclick: true
1974     });
1975     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1976 };
1977
1978 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1979     
1980    /// html : false,
1981     //align : '',
1982     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1983     type: false,
1984     /**
1985      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1986      */
1987     registerMenu : true,
1988     
1989     menuItems :false, // stores the menu items..
1990     
1991     hidden:true,
1992         
1993     parentMenu : false,
1994     
1995     stopEvent : true,
1996     
1997     isLink : false,
1998     
1999     getChildContainer : function() {
2000         return this.el;  
2001     },
2002     
2003     getAutoCreate : function(){
2004          
2005         //if (['right'].indexOf(this.align)!==-1) {
2006         //    cfg.cn[1].cls += ' pull-right'
2007         //}
2008         
2009         
2010         var cfg = {
2011             tag : 'ul',
2012             cls : 'dropdown-menu' ,
2013             style : 'z-index:1000'
2014             
2015         };
2016         
2017         if (this.type === 'submenu') {
2018             cfg.cls = 'submenu active';
2019         }
2020         if (this.type === 'treeview') {
2021             cfg.cls = 'treeview-menu';
2022         }
2023         
2024         return cfg;
2025     },
2026     initEvents : function() {
2027         
2028        // Roo.log("ADD event");
2029        // Roo.log(this.triggerEl.dom);
2030         
2031         this.triggerEl.on('click', this.onTriggerClick, this);
2032         
2033         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2034         
2035         this.triggerEl.addClass('dropdown-toggle');
2036         
2037         if (Roo.isTouch) {
2038             this.el.on('touchstart'  , this.onTouch, this);
2039         }
2040         this.el.on('click' , this.onClick, this);
2041
2042         this.el.on("mouseover", this.onMouseOver, this);
2043         this.el.on("mouseout", this.onMouseOut, this);
2044         
2045     },
2046     
2047     findTargetItem : function(e)
2048     {
2049         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2050         if(!t){
2051             return false;
2052         }
2053         //Roo.log(t);         Roo.log(t.id);
2054         if(t && t.id){
2055             //Roo.log(this.menuitems);
2056             return this.menuitems.get(t.id);
2057             
2058             //return this.items.get(t.menuItemId);
2059         }
2060         
2061         return false;
2062     },
2063     
2064     onTouch : function(e) 
2065     {
2066         Roo.log("menu.onTouch");
2067         //e.stopEvent(); this make the user popdown broken
2068         this.onClick(e);
2069     },
2070     
2071     onClick : function(e)
2072     {
2073         Roo.log("menu.onClick");
2074         
2075         var t = this.findTargetItem(e);
2076         if(!t || t.isContainer){
2077             return;
2078         }
2079         Roo.log(e);
2080         /*
2081         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2082             if(t == this.activeItem && t.shouldDeactivate(e)){
2083                 this.activeItem.deactivate();
2084                 delete this.activeItem;
2085                 return;
2086             }
2087             if(t.canActivate){
2088                 this.setActiveItem(t, true);
2089             }
2090             return;
2091             
2092             
2093         }
2094         */
2095        
2096         Roo.log('pass click event');
2097         
2098         t.onClick(e);
2099         
2100         this.fireEvent("click", this, t, e);
2101         
2102         var _this = this;
2103         
2104         (function() { _this.hide(); }).defer(100);
2105     },
2106     
2107     onMouseOver : function(e){
2108         var t  = this.findTargetItem(e);
2109         //Roo.log(t);
2110         //if(t){
2111         //    if(t.canActivate && !t.disabled){
2112         //        this.setActiveItem(t, true);
2113         //    }
2114         //}
2115         
2116         this.fireEvent("mouseover", this, e, t);
2117     },
2118     isVisible : function(){
2119         return !this.hidden;
2120     },
2121      onMouseOut : function(e){
2122         var t  = this.findTargetItem(e);
2123         
2124         //if(t ){
2125         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2126         //        this.activeItem.deactivate();
2127         //        delete this.activeItem;
2128         //    }
2129         //}
2130         this.fireEvent("mouseout", this, e, t);
2131     },
2132     
2133     
2134     /**
2135      * Displays this menu relative to another element
2136      * @param {String/HTMLElement/Roo.Element} element The element to align to
2137      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2138      * the element (defaults to this.defaultAlign)
2139      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2140      */
2141     show : function(el, pos, parentMenu){
2142         this.parentMenu = parentMenu;
2143         if(!this.el){
2144             this.render();
2145         }
2146         this.fireEvent("beforeshow", this);
2147         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2148     },
2149      /**
2150      * Displays this menu at a specific xy position
2151      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2152      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2153      */
2154     showAt : function(xy, parentMenu, /* private: */_e){
2155         this.parentMenu = parentMenu;
2156         if(!this.el){
2157             this.render();
2158         }
2159         if(_e !== false){
2160             this.fireEvent("beforeshow", this);
2161             //xy = this.el.adjustForConstraints(xy);
2162         }
2163         
2164         //this.el.show();
2165         this.hideMenuItems();
2166         this.hidden = false;
2167         this.triggerEl.addClass('open');
2168         
2169         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2170             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2171         }
2172         
2173         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2174             this.el.setXY(xy);
2175         }
2176         
2177         this.focus();
2178         this.fireEvent("show", this);
2179     },
2180     
2181     focus : function(){
2182         return;
2183         if(!this.hidden){
2184             this.doFocus.defer(50, this);
2185         }
2186     },
2187
2188     doFocus : function(){
2189         if(!this.hidden){
2190             this.focusEl.focus();
2191         }
2192     },
2193
2194     /**
2195      * Hides this menu and optionally all parent menus
2196      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2197      */
2198     hide : function(deep)
2199     {
2200         
2201         this.hideMenuItems();
2202         if(this.el && this.isVisible()){
2203             this.fireEvent("beforehide", this);
2204             if(this.activeItem){
2205                 this.activeItem.deactivate();
2206                 this.activeItem = null;
2207             }
2208             this.triggerEl.removeClass('open');;
2209             this.hidden = true;
2210             this.fireEvent("hide", this);
2211         }
2212         if(deep === true && this.parentMenu){
2213             this.parentMenu.hide(true);
2214         }
2215     },
2216     
2217     onTriggerClick : function(e)
2218     {
2219         Roo.log('trigger click');
2220         
2221         var target = e.getTarget();
2222         
2223         Roo.log(target.nodeName.toLowerCase());
2224         
2225         if(target.nodeName.toLowerCase() === 'i'){
2226             e.preventDefault();
2227         }
2228         
2229     },
2230     
2231     onTriggerPress  : function(e)
2232     {
2233         Roo.log('trigger press');
2234         //Roo.log(e.getTarget());
2235        // Roo.log(this.triggerEl.dom);
2236        
2237         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2238         var pel = Roo.get(e.getTarget());
2239         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2240             Roo.log('is treeview or dropdown?');
2241             return;
2242         }
2243         
2244         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2245             return;
2246         }
2247         
2248         if (this.isVisible()) {
2249             Roo.log('hide');
2250             this.hide();
2251         } else {
2252             Roo.log('show');
2253             this.show(this.triggerEl, false, false);
2254         }
2255         
2256         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2257             e.stopEvent();
2258         }
2259         
2260     },
2261        
2262     
2263     hideMenuItems : function()
2264     {
2265         Roo.log("hide Menu Items");
2266         if (!this.el) { 
2267             return;
2268         }
2269         //$(backdrop).remove()
2270         this.el.select('.open',true).each(function(aa) {
2271             
2272             aa.removeClass('open');
2273           //var parent = getParent($(this))
2274           //var relatedTarget = { relatedTarget: this }
2275           
2276            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2277           //if (e.isDefaultPrevented()) return
2278            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2279         });
2280     },
2281     addxtypeChild : function (tree, cntr) {
2282         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2283           
2284         this.menuitems.add(comp);
2285         return comp;
2286
2287     },
2288     getEl : function()
2289     {
2290         Roo.log(this.el);
2291         return this.el;
2292     }
2293 });
2294
2295  
2296  /*
2297  * - LGPL
2298  *
2299  * menu item
2300  * 
2301  */
2302
2303
2304 /**
2305  * @class Roo.bootstrap.MenuItem
2306  * @extends Roo.bootstrap.Component
2307  * Bootstrap MenuItem class
2308  * @cfg {String} html the menu label
2309  * @cfg {String} href the link
2310  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2311  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2312  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2313  * @cfg {String} fa favicon to show on left of menu item.
2314  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2315  * 
2316  * 
2317  * @constructor
2318  * Create a new MenuItem
2319  * @param {Object} config The config object
2320  */
2321
2322
2323 Roo.bootstrap.MenuItem = function(config){
2324     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2325     this.addEvents({
2326         // raw events
2327         /**
2328          * @event click
2329          * The raw click event for the entire grid.
2330          * @param {Roo.bootstrap.MenuItem} this
2331          * @param {Roo.EventObject} e
2332          */
2333         "click" : true
2334     });
2335 };
2336
2337 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2338     
2339     href : false,
2340     html : false,
2341     preventDefault: false,
2342     isContainer : false,
2343     active : false,
2344     fa: false,
2345     
2346     getAutoCreate : function(){
2347         
2348         if(this.isContainer){
2349             return {
2350                 tag: 'li',
2351                 cls: 'dropdown-menu-item'
2352             };
2353         }
2354         var ctag = {
2355             tag: 'span',
2356             html: 'Link'
2357         };
2358         
2359         var anc = {
2360             tag : 'a',
2361             href : '#',
2362             cn : [  ]
2363         };
2364         
2365         if (this.fa !== false) {
2366             anc.cn.push({
2367                 tag : 'i',
2368                 cls : 'fa fa-' + this.fa
2369             });
2370         }
2371         
2372         anc.cn.push(ctag);
2373         
2374         
2375         var cfg= {
2376             tag: 'li',
2377             cls: 'dropdown-menu-item',
2378             cn: [ anc ]
2379         };
2380         if (this.parent().type == 'treeview') {
2381             cfg.cls = 'treeview-menu';
2382         }
2383         if (this.active) {
2384             cfg.cls += ' active';
2385         }
2386         
2387         
2388         
2389         anc.href = this.href || cfg.cn[0].href ;
2390         ctag.html = this.html || cfg.cn[0].html ;
2391         return cfg;
2392     },
2393     
2394     initEvents: function()
2395     {
2396         if (this.parent().type == 'treeview') {
2397             this.el.select('a').on('click', this.onClick, this);
2398         }
2399         if (this.menu) {
2400             this.menu.parentType = this.xtype;
2401             this.menu.triggerEl = this.el;
2402             this.menu = this.addxtype(Roo.apply({}, this.menu));
2403         }
2404         
2405     },
2406     onClick : function(e)
2407     {
2408         Roo.log('item on click ');
2409         
2410         if(this.preventDefault){
2411             e.preventDefault();
2412         }
2413         //this.parent().hideMenuItems();
2414         
2415         this.fireEvent('click', this, e);
2416     },
2417     getEl : function()
2418     {
2419         return this.el;
2420     } 
2421 });
2422
2423  
2424
2425  /*
2426  * - LGPL
2427  *
2428  * menu separator
2429  * 
2430  */
2431
2432
2433 /**
2434  * @class Roo.bootstrap.MenuSeparator
2435  * @extends Roo.bootstrap.Component
2436  * Bootstrap MenuSeparator class
2437  * 
2438  * @constructor
2439  * Create a new MenuItem
2440  * @param {Object} config The config object
2441  */
2442
2443
2444 Roo.bootstrap.MenuSeparator = function(config){
2445     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2446 };
2447
2448 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2449     
2450     getAutoCreate : function(){
2451         var cfg = {
2452             cls: 'divider',
2453             tag : 'li'
2454         };
2455         
2456         return cfg;
2457     }
2458    
2459 });
2460
2461  
2462
2463  
2464 /*
2465 * Licence: LGPL
2466 */
2467
2468 /**
2469  * @class Roo.bootstrap.Modal
2470  * @extends Roo.bootstrap.Component
2471  * Bootstrap Modal class
2472  * @cfg {String} title Title of dialog
2473  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2474  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2475  * @cfg {Boolean} specificTitle default false
2476  * @cfg {Array} buttons Array of buttons or standard button set..
2477  * @cfg {String} buttonPosition (left|right|center) default right
2478  * @cfg {Boolean} animate default true
2479  * @cfg {Boolean} allow_close default true
2480  * @cfg {Boolean} fitwindow default false
2481  * @cfg {String} size (sm|lg) default empty
2482  *
2483  *
2484  * @constructor
2485  * Create a new Modal Dialog
2486  * @param {Object} config The config object
2487  */
2488
2489 Roo.bootstrap.Modal = function(config){
2490     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event btnclick
2495          * The raw btnclick event for the button
2496          * @param {Roo.EventObject} e
2497          */
2498         "btnclick" : true
2499     });
2500     this.buttons = this.buttons || [];
2501
2502     if (this.tmpl) {
2503         this.tmpl = Roo.factory(this.tmpl);
2504     }
2505
2506 };
2507
2508 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2509
2510     title : 'test dialog',
2511
2512     buttons : false,
2513
2514     // set on load...
2515
2516     html: false,
2517
2518     tmp: false,
2519
2520     specificTitle: false,
2521
2522     buttonPosition: 'right',
2523
2524     allow_close : true,
2525
2526     animate : true,
2527
2528     fitwindow: false,
2529
2530
2531      // private
2532     dialogEl: false,
2533     bodyEl:  false,
2534     footerEl:  false,
2535     titleEl:  false,
2536     closeEl:  false,
2537
2538     size: '',
2539
2540
2541     onRender : function(ct, position)
2542     {
2543         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2544
2545         if(!this.el){
2546             var cfg = Roo.apply({},  this.getAutoCreate());
2547             cfg.id = Roo.id();
2548             //if(!cfg.name){
2549             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2550             //}
2551             //if (!cfg.name.length) {
2552             //    delete cfg.name;
2553            // }
2554             if (this.cls) {
2555                 cfg.cls += ' ' + this.cls;
2556             }
2557             if (this.style) {
2558                 cfg.style = this.style;
2559             }
2560             this.el = Roo.get(document.body).createChild(cfg, position);
2561         }
2562         //var type = this.el.dom.type;
2563
2564
2565         if(this.tabIndex !== undefined){
2566             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2567         }
2568
2569         this.dialogEl = this.el.select('.modal-dialog',true).first();
2570         this.bodyEl = this.el.select('.modal-body',true).first();
2571         this.closeEl = this.el.select('.modal-header .close', true).first();
2572         this.headerEl = this.el.select('.modal-header',true).first();
2573         this.titleEl = this.el.select('.modal-title',true).first();
2574         this.footerEl = this.el.select('.modal-footer',true).first();
2575
2576         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2577         this.maskEl.enableDisplayMode("block");
2578         this.maskEl.hide();
2579         //this.el.addClass("x-dlg-modal");
2580
2581         if (this.buttons.length) {
2582             Roo.each(this.buttons, function(bb) {
2583                 var b = Roo.apply({}, bb);
2584                 b.xns = b.xns || Roo.bootstrap;
2585                 b.xtype = b.xtype || 'Button';
2586                 if (typeof(b.listeners) == 'undefined') {
2587                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2588                 }
2589
2590                 var btn = Roo.factory(b);
2591
2592                 btn.render(this.el.select('.modal-footer div').first());
2593
2594             },this);
2595         }
2596         // render the children.
2597         var nitems = [];
2598
2599         if(typeof(this.items) != 'undefined'){
2600             var items = this.items;
2601             delete this.items;
2602
2603             for(var i =0;i < items.length;i++) {
2604                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2605             }
2606         }
2607
2608         this.items = nitems;
2609
2610         // where are these used - they used to be body/close/footer
2611
2612
2613         this.initEvents();
2614         //this.el.addClass([this.fieldClass, this.cls]);
2615
2616     },
2617
2618     getAutoCreate : function(){
2619
2620
2621         var bdy = {
2622                 cls : 'modal-body',
2623                 html : this.html || ''
2624         };
2625
2626         var title = {
2627             tag: 'h4',
2628             cls : 'modal-title',
2629             html : this.title
2630         };
2631
2632         if(this.specificTitle){
2633             title = this.title;
2634
2635         };
2636
2637         var header = [];
2638         if (this.allow_close) {
2639             header.push({
2640                 tag: 'button',
2641                 cls : 'close',
2642                 html : '&times'
2643             });
2644         }
2645
2646         header.push(title);
2647
2648         var size = '';
2649
2650         if(this.size.length){
2651             size = 'modal-' + this.size;
2652         }
2653
2654         var modal = {
2655             cls: "modal",
2656             style : 'display: none',
2657             cn : [
2658                 {
2659                     cls: "modal-dialog " + size,
2660                     cn : [
2661                         {
2662                             cls : "modal-content",
2663                             cn : [
2664                                 {
2665                                     cls : 'modal-header',
2666                                     cn : header
2667                                 },
2668                                 bdy,
2669                                 {
2670                                     cls : 'modal-footer',
2671                                     cn : [
2672                                         {
2673                                             tag: 'div',
2674                                             cls: 'btn-' + this.buttonPosition
2675                                         }
2676                                     ]
2677
2678                                 }
2679
2680
2681                             ]
2682
2683                         }
2684                     ]
2685
2686                 }
2687             ]
2688         };
2689
2690         if(this.animate){
2691             modal.cls += ' fade';
2692         }
2693
2694         return modal;
2695
2696     },
2697     getChildContainer : function() {
2698
2699          return this.bodyEl;
2700
2701     },
2702     getButtonContainer : function() {
2703          return this.el.select('.modal-footer div',true).first();
2704
2705     },
2706     initEvents : function()
2707     {
2708         if (this.allow_close) {
2709             this.closeEl.on('click', this.hide, this);
2710         }
2711         Roo.EventManager.onWindowResize(this.resize, this, true);
2712
2713
2714     },
2715
2716     resize : function()
2717     {
2718         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2719         if (this.fitwindow) {
2720             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2721             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2722             this.setSize(w,h);
2723         }
2724     },
2725
2726     setSize : function(w,h)
2727     {
2728         if (!w && !h) {
2729             return;
2730         }
2731         this.resizeTo(w,h);
2732     },
2733
2734     show : function() {
2735
2736         if (!this.rendered) {
2737             this.render();
2738         }
2739
2740         this.el.setStyle('display', 'block');
2741
2742         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2743             var _this = this;
2744             (function(){
2745                 this.el.addClass('in');
2746             }).defer(50, this);
2747         }else{
2748             this.el.addClass('in');
2749
2750         }
2751
2752         // not sure how we can show data in here..
2753         //if (this.tmpl) {
2754         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2755         //}
2756
2757         Roo.get(document.body).addClass("x-body-masked");
2758         
2759         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2760         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2761         this.maskEl.show();
2762         
2763         this.resize();
2764         
2765         this.fireEvent('show', this);
2766
2767         // set zindex here - otherwise it appears to be ignored...
2768         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2769
2770         (function () {
2771             this.items.forEach( function(e) {
2772                 e.layout ? e.layout() : false;
2773
2774             });
2775         }).defer(100,this);
2776
2777     },
2778     hide : function()
2779     {
2780         if(this.fireEvent("beforehide", this) !== false){
2781             this.maskEl.hide();
2782             Roo.get(document.body).removeClass("x-body-masked");
2783             this.el.removeClass('in');
2784             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2785
2786             if(this.animate){ // why
2787                 var _this = this;
2788                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2789             }else{
2790                 this.el.setStyle('display', 'none');
2791             }
2792             this.fireEvent('hide', this);
2793         }
2794     },
2795
2796     addButton : function(str, cb)
2797     {
2798
2799
2800         var b = Roo.apply({}, { html : str } );
2801         b.xns = b.xns || Roo.bootstrap;
2802         b.xtype = b.xtype || 'Button';
2803         if (typeof(b.listeners) == 'undefined') {
2804             b.listeners = { click : cb.createDelegate(this)  };
2805         }
2806
2807         var btn = Roo.factory(b);
2808
2809         btn.render(this.el.select('.modal-footer div').first());
2810
2811         return btn;
2812
2813     },
2814
2815     setDefaultButton : function(btn)
2816     {
2817         //this.el.select('.modal-footer').()
2818     },
2819     diff : false,
2820
2821     resizeTo: function(w,h)
2822     {
2823         // skip.. ?? why??
2824
2825         this.dialogEl.setWidth(w);
2826         if (this.diff === false) {
2827             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2828         }
2829
2830         this.bodyEl.setHeight(h-this.diff);
2831
2832
2833     },
2834     setContentSize  : function(w, h)
2835     {
2836
2837     },
2838     onButtonClick: function(btn,e)
2839     {
2840         //Roo.log([a,b,c]);
2841         this.fireEvent('btnclick', btn.name, e);
2842     },
2843      /**
2844      * Set the title of the Dialog
2845      * @param {String} str new Title
2846      */
2847     setTitle: function(str) {
2848         this.titleEl.dom.innerHTML = str;
2849     },
2850     /**
2851      * Set the body of the Dialog
2852      * @param {String} str new Title
2853      */
2854     setBody: function(str) {
2855         this.bodyEl.dom.innerHTML = str;
2856     },
2857     /**
2858      * Set the body of the Dialog using the template
2859      * @param {Obj} data - apply this data to the template and replace the body contents.
2860      */
2861     applyBody: function(obj)
2862     {
2863         if (!this.tmpl) {
2864             Roo.log("Error - using apply Body without a template");
2865             //code
2866         }
2867         this.tmpl.overwrite(this.bodyEl, obj);
2868     }
2869
2870 });
2871
2872
2873 Roo.apply(Roo.bootstrap.Modal,  {
2874     /**
2875          * Button config that displays a single OK button
2876          * @type Object
2877          */
2878         OK :  [{
2879             name : 'ok',
2880             weight : 'primary',
2881             html : 'OK'
2882         }],
2883         /**
2884          * Button config that displays Yes and No buttons
2885          * @type Object
2886          */
2887         YESNO : [
2888             {
2889                 name  : 'no',
2890                 html : 'No'
2891             },
2892             {
2893                 name  :'yes',
2894                 weight : 'primary',
2895                 html : 'Yes'
2896             }
2897         ],
2898
2899         /**
2900          * Button config that displays OK and Cancel buttons
2901          * @type Object
2902          */
2903         OKCANCEL : [
2904             {
2905                name : 'cancel',
2906                 html : 'Cancel'
2907             },
2908             {
2909                 name : 'ok',
2910                 weight : 'primary',
2911                 html : 'OK'
2912             }
2913         ],
2914         /**
2915          * Button config that displays Yes, No and Cancel buttons
2916          * @type Object
2917          */
2918         YESNOCANCEL : [
2919             {
2920                 name : 'yes',
2921                 weight : 'primary',
2922                 html : 'Yes'
2923             },
2924             {
2925                 name : 'no',
2926                 html : 'No'
2927             },
2928             {
2929                 name : 'cancel',
2930                 html : 'Cancel'
2931             }
2932         ],
2933         
2934         zIndex : 10001
2935 });
2936 /*
2937  * - LGPL
2938  *
2939  * messagebox - can be used as a replace
2940  * 
2941  */
2942 /**
2943  * @class Roo.MessageBox
2944  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2945  * Example usage:
2946  *<pre><code>
2947 // Basic alert:
2948 Roo.Msg.alert('Status', 'Changes saved successfully.');
2949
2950 // Prompt for user data:
2951 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2952     if (btn == 'ok'){
2953         // process text value...
2954     }
2955 });
2956
2957 // Show a dialog using config options:
2958 Roo.Msg.show({
2959    title:'Save Changes?',
2960    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2961    buttons: Roo.Msg.YESNOCANCEL,
2962    fn: processResult,
2963    animEl: 'elId'
2964 });
2965 </code></pre>
2966  * @singleton
2967  */
2968 Roo.bootstrap.MessageBox = function(){
2969     var dlg, opt, mask, waitTimer;
2970     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2971     var buttons, activeTextEl, bwidth;
2972
2973     
2974     // private
2975     var handleButton = function(button){
2976         dlg.hide();
2977         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2978     };
2979
2980     // private
2981     var handleHide = function(){
2982         if(opt && opt.cls){
2983             dlg.el.removeClass(opt.cls);
2984         }
2985         //if(waitTimer){
2986         //    Roo.TaskMgr.stop(waitTimer);
2987         //    waitTimer = null;
2988         //}
2989     };
2990
2991     // private
2992     var updateButtons = function(b){
2993         var width = 0;
2994         if(!b){
2995             buttons["ok"].hide();
2996             buttons["cancel"].hide();
2997             buttons["yes"].hide();
2998             buttons["no"].hide();
2999             //dlg.footer.dom.style.display = 'none';
3000             return width;
3001         }
3002         dlg.footerEl.dom.style.display = '';
3003         for(var k in buttons){
3004             if(typeof buttons[k] != "function"){
3005                 if(b[k]){
3006                     buttons[k].show();
3007                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3008                     width += buttons[k].el.getWidth()+15;
3009                 }else{
3010                     buttons[k].hide();
3011                 }
3012             }
3013         }
3014         return width;
3015     };
3016
3017     // private
3018     var handleEsc = function(d, k, e){
3019         if(opt && opt.closable !== false){
3020             dlg.hide();
3021         }
3022         if(e){
3023             e.stopEvent();
3024         }
3025     };
3026
3027     return {
3028         /**
3029          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3030          * @return {Roo.BasicDialog} The BasicDialog element
3031          */
3032         getDialog : function(){
3033            if(!dlg){
3034                 dlg = new Roo.bootstrap.Modal( {
3035                     //draggable: true,
3036                     //resizable:false,
3037                     //constraintoviewport:false,
3038                     //fixedcenter:true,
3039                     //collapsible : false,
3040                     //shim:true,
3041                     //modal: true,
3042                 //    width: 'auto',
3043                   //  height:100,
3044                     //buttonAlign:"center",
3045                     closeClick : function(){
3046                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3047                             handleButton("no");
3048                         }else{
3049                             handleButton("cancel");
3050                         }
3051                     }
3052                 });
3053                 dlg.render();
3054                 dlg.on("hide", handleHide);
3055                 mask = dlg.mask;
3056                 //dlg.addKeyListener(27, handleEsc);
3057                 buttons = {};
3058                 this.buttons = buttons;
3059                 var bt = this.buttonText;
3060                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3061                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3062                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3063                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3064                 //Roo.log(buttons);
3065                 bodyEl = dlg.bodyEl.createChild({
3066
3067                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3068                         '<textarea class="roo-mb-textarea"></textarea>' +
3069                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3070                 });
3071                 msgEl = bodyEl.dom.firstChild;
3072                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3073                 textboxEl.enableDisplayMode();
3074                 textboxEl.addKeyListener([10,13], function(){
3075                     if(dlg.isVisible() && opt && opt.buttons){
3076                         if(opt.buttons.ok){
3077                             handleButton("ok");
3078                         }else if(opt.buttons.yes){
3079                             handleButton("yes");
3080                         }
3081                     }
3082                 });
3083                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3084                 textareaEl.enableDisplayMode();
3085                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3086                 progressEl.enableDisplayMode();
3087                 
3088                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3089                 //var pf = progressEl.dom.firstChild;
3090                 //if (pf) {
3091                     //pp = Roo.get(pf.firstChild);
3092                     //pp.setHeight(pf.offsetHeight);
3093                 //}
3094                 
3095             }
3096             return dlg;
3097         },
3098
3099         /**
3100          * Updates the message box body text
3101          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3102          * the XHTML-compliant non-breaking space character '&amp;#160;')
3103          * @return {Roo.MessageBox} This message box
3104          */
3105         updateText : function(text)
3106         {
3107             if(!dlg.isVisible() && !opt.width){
3108                 dlg.dialogEl.setWidth(this.maxWidth);
3109                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3110             }
3111             msgEl.innerHTML = text || '&#160;';
3112       
3113             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3114             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3115             var w = Math.max(
3116                     Math.min(opt.width || cw , this.maxWidth), 
3117                     Math.max(opt.minWidth || this.minWidth, bwidth)
3118             );
3119             if(opt.prompt){
3120                 activeTextEl.setWidth(w);
3121             }
3122             if(dlg.isVisible()){
3123                 dlg.fixedcenter = false;
3124             }
3125             // to big, make it scroll. = But as usual stupid IE does not support
3126             // !important..
3127             
3128             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3129                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3130                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3131             } else {
3132                 bodyEl.dom.style.height = '';
3133                 bodyEl.dom.style.overflowY = '';
3134             }
3135             if (cw > w) {
3136                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3137             } else {
3138                 bodyEl.dom.style.overflowX = '';
3139             }
3140             
3141             dlg.setContentSize(w, bodyEl.getHeight());
3142             if(dlg.isVisible()){
3143                 dlg.fixedcenter = true;
3144             }
3145             return this;
3146         },
3147
3148         /**
3149          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3150          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3151          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3152          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3153          * @return {Roo.MessageBox} This message box
3154          */
3155         updateProgress : function(value, text){
3156             if(text){
3157                 this.updateText(text);
3158             }
3159             if (pp) { // weird bug on my firefox - for some reason this is not defined
3160                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3161             }
3162             return this;
3163         },        
3164
3165         /**
3166          * Returns true if the message box is currently displayed
3167          * @return {Boolean} True if the message box is visible, else false
3168          */
3169         isVisible : function(){
3170             return dlg && dlg.isVisible();  
3171         },
3172
3173         /**
3174          * Hides the message box if it is displayed
3175          */
3176         hide : function(){
3177             if(this.isVisible()){
3178                 dlg.hide();
3179             }  
3180         },
3181
3182         /**
3183          * Displays a new message box, or reinitializes an existing message box, based on the config options
3184          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3185          * The following config object properties are supported:
3186          * <pre>
3187 Property    Type             Description
3188 ----------  ---------------  ------------------------------------------------------------------------------------
3189 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3190                                    closes (defaults to undefined)
3191 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3192                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3193 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3194                                    progress and wait dialogs will ignore this property and always hide the
3195                                    close button as they can only be closed programmatically.
3196 cls               String           A custom CSS class to apply to the message box element
3197 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3198                                    displayed (defaults to 75)
3199 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3200                                    function will be btn (the name of the button that was clicked, if applicable,
3201                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3202                                    Progress and wait dialogs will ignore this option since they do not respond to
3203                                    user actions and can only be closed programmatically, so any required function
3204                                    should be called by the same code after it closes the dialog.
3205 icon              String           A CSS class that provides a background image to be used as an icon for
3206                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3207 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3208 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3209 modal             Boolean          False to allow user interaction with the page while the message box is
3210                                    displayed (defaults to true)
3211 msg               String           A string that will replace the existing message box body text (defaults
3212                                    to the XHTML-compliant non-breaking space character '&#160;')
3213 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3214 progress          Boolean          True to display a progress bar (defaults to false)
3215 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3216 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3217 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3218 title             String           The title text
3219 value             String           The string value to set into the active textbox element if displayed
3220 wait              Boolean          True to display a progress bar (defaults to false)
3221 width             Number           The width of the dialog in pixels
3222 </pre>
3223          *
3224          * Example usage:
3225          * <pre><code>
3226 Roo.Msg.show({
3227    title: 'Address',
3228    msg: 'Please enter your address:',
3229    width: 300,
3230    buttons: Roo.MessageBox.OKCANCEL,
3231    multiline: true,
3232    fn: saveAddress,
3233    animEl: 'addAddressBtn'
3234 });
3235 </code></pre>
3236          * @param {Object} config Configuration options
3237          * @return {Roo.MessageBox} This message box
3238          */
3239         show : function(options)
3240         {
3241             
3242             // this causes nightmares if you show one dialog after another
3243             // especially on callbacks..
3244              
3245             if(this.isVisible()){
3246                 
3247                 this.hide();
3248                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3249                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3250                 Roo.log("New Dialog Message:" +  options.msg )
3251                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3252                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3253                 
3254             }
3255             var d = this.getDialog();
3256             opt = options;
3257             d.setTitle(opt.title || "&#160;");
3258             d.closeEl.setDisplayed(opt.closable !== false);
3259             activeTextEl = textboxEl;
3260             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3261             if(opt.prompt){
3262                 if(opt.multiline){
3263                     textboxEl.hide();
3264                     textareaEl.show();
3265                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3266                         opt.multiline : this.defaultTextHeight);
3267                     activeTextEl = textareaEl;
3268                 }else{
3269                     textboxEl.show();
3270                     textareaEl.hide();
3271                 }
3272             }else{
3273                 textboxEl.hide();
3274                 textareaEl.hide();
3275             }
3276             progressEl.setDisplayed(opt.progress === true);
3277             this.updateProgress(0);
3278             activeTextEl.dom.value = opt.value || "";
3279             if(opt.prompt){
3280                 dlg.setDefaultButton(activeTextEl);
3281             }else{
3282                 var bs = opt.buttons;
3283                 var db = null;
3284                 if(bs && bs.ok){
3285                     db = buttons["ok"];
3286                 }else if(bs && bs.yes){
3287                     db = buttons["yes"];
3288                 }
3289                 dlg.setDefaultButton(db);
3290             }
3291             bwidth = updateButtons(opt.buttons);
3292             this.updateText(opt.msg);
3293             if(opt.cls){
3294                 d.el.addClass(opt.cls);
3295             }
3296             d.proxyDrag = opt.proxyDrag === true;
3297             d.modal = opt.modal !== false;
3298             d.mask = opt.modal !== false ? mask : false;
3299             if(!d.isVisible()){
3300                 // force it to the end of the z-index stack so it gets a cursor in FF
3301                 document.body.appendChild(dlg.el.dom);
3302                 d.animateTarget = null;
3303                 d.show(options.animEl);
3304             }
3305             return this;
3306         },
3307
3308         /**
3309          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3310          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3311          * and closing the message box when the process is complete.
3312          * @param {String} title The title bar text
3313          * @param {String} msg The message box body text
3314          * @return {Roo.MessageBox} This message box
3315          */
3316         progress : function(title, msg){
3317             this.show({
3318                 title : title,
3319                 msg : msg,
3320                 buttons: false,
3321                 progress:true,
3322                 closable:false,
3323                 minWidth: this.minProgressWidth,
3324                 modal : true
3325             });
3326             return this;
3327         },
3328
3329         /**
3330          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3331          * If a callback function is passed it will be called after the user clicks the button, and the
3332          * id of the button that was clicked will be passed as the only parameter to the callback
3333          * (could also be the top-right close button).
3334          * @param {String} title The title bar text
3335          * @param {String} msg The message box body text
3336          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3337          * @param {Object} scope (optional) The scope of the callback function
3338          * @return {Roo.MessageBox} This message box
3339          */
3340         alert : function(title, msg, fn, scope)
3341         {
3342             this.show({
3343                 title : title,
3344                 msg : msg,
3345                 buttons: this.OK,
3346                 fn: fn,
3347                 closable : false,
3348                 scope : scope,
3349                 modal : true
3350             });
3351             return this;
3352         },
3353
3354         /**
3355          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3356          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3357          * You are responsible for closing the message box when the process is complete.
3358          * @param {String} msg The message box body text
3359          * @param {String} title (optional) The title bar text
3360          * @return {Roo.MessageBox} This message box
3361          */
3362         wait : function(msg, title){
3363             this.show({
3364                 title : title,
3365                 msg : msg,
3366                 buttons: false,
3367                 closable:false,
3368                 progress:true,
3369                 modal:true,
3370                 width:300,
3371                 wait:true
3372             });
3373             waitTimer = Roo.TaskMgr.start({
3374                 run: function(i){
3375                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3376                 },
3377                 interval: 1000
3378             });
3379             return this;
3380         },
3381
3382         /**
3383          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3384          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3385          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3386          * @param {String} title The title bar text
3387          * @param {String} msg The message box body text
3388          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3389          * @param {Object} scope (optional) The scope of the callback function
3390          * @return {Roo.MessageBox} This message box
3391          */
3392         confirm : function(title, msg, fn, scope){
3393             this.show({
3394                 title : title,
3395                 msg : msg,
3396                 buttons: this.YESNO,
3397                 fn: fn,
3398                 scope : scope,
3399                 modal : true
3400             });
3401             return this;
3402         },
3403
3404         /**
3405          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3406          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3407          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3408          * (could also be the top-right close button) and the text that was entered will be passed as the two
3409          * parameters to the callback.
3410          * @param {String} title The title bar text
3411          * @param {String} msg The message box body text
3412          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3413          * @param {Object} scope (optional) The scope of the callback function
3414          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3415          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3416          * @return {Roo.MessageBox} This message box
3417          */
3418         prompt : function(title, msg, fn, scope, multiline){
3419             this.show({
3420                 title : title,
3421                 msg : msg,
3422                 buttons: this.OKCANCEL,
3423                 fn: fn,
3424                 minWidth:250,
3425                 scope : scope,
3426                 prompt:true,
3427                 multiline: multiline,
3428                 modal : true
3429             });
3430             return this;
3431         },
3432
3433         /**
3434          * Button config that displays a single OK button
3435          * @type Object
3436          */
3437         OK : {ok:true},
3438         /**
3439          * Button config that displays Yes and No buttons
3440          * @type Object
3441          */
3442         YESNO : {yes:true, no:true},
3443         /**
3444          * Button config that displays OK and Cancel buttons
3445          * @type Object
3446          */
3447         OKCANCEL : {ok:true, cancel:true},
3448         /**
3449          * Button config that displays Yes, No and Cancel buttons
3450          * @type Object
3451          */
3452         YESNOCANCEL : {yes:true, no:true, cancel:true},
3453
3454         /**
3455          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3456          * @type Number
3457          */
3458         defaultTextHeight : 75,
3459         /**
3460          * The maximum width in pixels of the message box (defaults to 600)
3461          * @type Number
3462          */
3463         maxWidth : 600,
3464         /**
3465          * The minimum width in pixels of the message box (defaults to 100)
3466          * @type Number
3467          */
3468         minWidth : 100,
3469         /**
3470          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3471          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3472          * @type Number
3473          */
3474         minProgressWidth : 250,
3475         /**
3476          * An object containing the default button text strings that can be overriden for localized language support.
3477          * Supported properties are: ok, cancel, yes and no.
3478          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3479          * @type Object
3480          */
3481         buttonText : {
3482             ok : "OK",
3483             cancel : "Cancel",
3484             yes : "Yes",
3485             no : "No"
3486         }
3487     };
3488 }();
3489
3490 /**
3491  * Shorthand for {@link Roo.MessageBox}
3492  */
3493 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3494 Roo.Msg = Roo.Msg || Roo.MessageBox;
3495 /*
3496  * - LGPL
3497  *
3498  * navbar
3499  * 
3500  */
3501
3502 /**
3503  * @class Roo.bootstrap.Navbar
3504  * @extends Roo.bootstrap.Component
3505  * Bootstrap Navbar class
3506
3507  * @constructor
3508  * Create a new Navbar
3509  * @param {Object} config The config object
3510  */
3511
3512
3513 Roo.bootstrap.Navbar = function(config){
3514     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3515     this.addEvents({
3516         // raw events
3517         /**
3518          * @event beforetoggle
3519          * Fire before toggle the menu
3520          * @param {Roo.EventObject} e
3521          */
3522         "beforetoggle" : true
3523     });
3524 };
3525
3526 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3527     
3528     
3529    
3530     // private
3531     navItems : false,
3532     loadMask : false,
3533     
3534     
3535     getAutoCreate : function(){
3536         
3537         
3538         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3539         
3540     },
3541     
3542     initEvents :function ()
3543     {
3544         //Roo.log(this.el.select('.navbar-toggle',true));
3545         this.el.select('.navbar-toggle',true).on('click', function() {
3546             if(this.fireEvent('beforetoggle', this) !== false){
3547                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3548             }
3549             
3550         }, this);
3551         
3552         var mark = {
3553             tag: "div",
3554             cls:"x-dlg-mask"
3555         };
3556         
3557         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3558         
3559         var size = this.el.getSize();
3560         this.maskEl.setSize(size.width, size.height);
3561         this.maskEl.enableDisplayMode("block");
3562         this.maskEl.hide();
3563         
3564         if(this.loadMask){
3565             this.maskEl.show();
3566         }
3567     },
3568     
3569     
3570     getChildContainer : function()
3571     {
3572         if (this.el.select('.collapse').getCount()) {
3573             return this.el.select('.collapse',true).first();
3574         }
3575         
3576         return this.el;
3577     },
3578     
3579     mask : function()
3580     {
3581         this.maskEl.show();
3582     },
3583     
3584     unmask : function()
3585     {
3586         this.maskEl.hide();
3587     } 
3588     
3589     
3590     
3591     
3592 });
3593
3594
3595
3596  
3597
3598  /*
3599  * - LGPL
3600  *
3601  * navbar
3602  * 
3603  */
3604
3605 /**
3606  * @class Roo.bootstrap.NavSimplebar
3607  * @extends Roo.bootstrap.Navbar
3608  * Bootstrap Sidebar class
3609  *
3610  * @cfg {Boolean} inverse is inverted color
3611  * 
3612  * @cfg {String} type (nav | pills | tabs)
3613  * @cfg {Boolean} arrangement stacked | justified
3614  * @cfg {String} align (left | right) alignment
3615  * 
3616  * @cfg {Boolean} main (true|false) main nav bar? default false
3617  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3618  * 
3619  * @cfg {String} tag (header|footer|nav|div) default is nav 
3620
3621  * 
3622  * 
3623  * 
3624  * @constructor
3625  * Create a new Sidebar
3626  * @param {Object} config The config object
3627  */
3628
3629
3630 Roo.bootstrap.NavSimplebar = function(config){
3631     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3632 };
3633
3634 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3635     
3636     inverse: false,
3637     
3638     type: false,
3639     arrangement: '',
3640     align : false,
3641     
3642     
3643     
3644     main : false,
3645     
3646     
3647     tag : false,
3648     
3649     
3650     getAutoCreate : function(){
3651         
3652         
3653         var cfg = {
3654             tag : this.tag || 'div',
3655             cls : 'navbar'
3656         };
3657           
3658         
3659         cfg.cn = [
3660             {
3661                 cls: 'nav',
3662                 tag : 'ul'
3663             }
3664         ];
3665         
3666          
3667         this.type = this.type || 'nav';
3668         if (['tabs','pills'].indexOf(this.type)!==-1) {
3669             cfg.cn[0].cls += ' nav-' + this.type
3670         
3671         
3672         } else {
3673             if (this.type!=='nav') {
3674                 Roo.log('nav type must be nav/tabs/pills')
3675             }
3676             cfg.cn[0].cls += ' navbar-nav'
3677         }
3678         
3679         
3680         
3681         
3682         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3683             cfg.cn[0].cls += ' nav-' + this.arrangement;
3684         }
3685         
3686         
3687         if (this.align === 'right') {
3688             cfg.cn[0].cls += ' navbar-right';
3689         }
3690         
3691         if (this.inverse) {
3692             cfg.cls += ' navbar-inverse';
3693             
3694         }
3695         
3696         
3697         return cfg;
3698     
3699         
3700     }
3701     
3702     
3703     
3704 });
3705
3706
3707
3708  
3709
3710  
3711        /*
3712  * - LGPL
3713  *
3714  * navbar
3715  * 
3716  */
3717
3718 /**
3719  * @class Roo.bootstrap.NavHeaderbar
3720  * @extends Roo.bootstrap.NavSimplebar
3721  * Bootstrap Sidebar class
3722  *
3723  * @cfg {String} brand what is brand
3724  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3725  * @cfg {String} brand_href href of the brand
3726  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3727  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3728  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3729  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3730  * 
3731  * @constructor
3732  * Create a new Sidebar
3733  * @param {Object} config The config object
3734  */
3735
3736
3737 Roo.bootstrap.NavHeaderbar = function(config){
3738     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3739       
3740 };
3741
3742 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3743     
3744     position: '',
3745     brand: '',
3746     brand_href: false,
3747     srButton : true,
3748     autohide : false,
3749     desktopCenter : false,
3750    
3751     
3752     getAutoCreate : function(){
3753         
3754         var   cfg = {
3755             tag: this.nav || 'nav',
3756             cls: 'navbar',
3757             role: 'navigation',
3758             cn: []
3759         };
3760         
3761         var cn = cfg.cn;
3762         if (this.desktopCenter) {
3763             cn.push({cls : 'container', cn : []});
3764             cn = cn[0].cn;
3765         }
3766         
3767         if(this.srButton){
3768             cn.push({
3769                 tag: 'div',
3770                 cls: 'navbar-header',
3771                 cn: [
3772                     {
3773                         tag: 'button',
3774                         type: 'button',
3775                         cls: 'navbar-toggle',
3776                         'data-toggle': 'collapse',
3777                         cn: [
3778                             {
3779                                 tag: 'span',
3780                                 cls: 'sr-only',
3781                                 html: 'Toggle navigation'
3782                             },
3783                             {
3784                                 tag: 'span',
3785                                 cls: 'icon-bar'
3786                             },
3787                             {
3788                                 tag: 'span',
3789                                 cls: 'icon-bar'
3790                             },
3791                             {
3792                                 tag: 'span',
3793                                 cls: 'icon-bar'
3794                             }
3795                         ]
3796                     }
3797                 ]
3798             });
3799         }
3800         
3801         cn.push({
3802             tag: 'div',
3803             cls: 'collapse navbar-collapse',
3804             cn : []
3805         });
3806         
3807         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3808         
3809         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3810             cfg.cls += ' navbar-' + this.position;
3811             
3812             // tag can override this..
3813             
3814             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3815         }
3816         
3817         if (this.brand !== '') {
3818             cn[0].cn.push({
3819                 tag: 'a',
3820                 href: this.brand_href ? this.brand_href : '#',
3821                 cls: 'navbar-brand',
3822                 cn: [
3823                 this.brand
3824                 ]
3825             });
3826         }
3827         
3828         if(this.main){
3829             cfg.cls += ' main-nav';
3830         }
3831         
3832         
3833         return cfg;
3834
3835         
3836     },
3837     getHeaderChildContainer : function()
3838     {
3839         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3840             return this.el.select('.navbar-header',true).first();
3841         }
3842         
3843         return this.getChildContainer();
3844     },
3845     
3846     
3847     initEvents : function()
3848     {
3849         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3850         
3851         if (this.autohide) {
3852             
3853             var prevScroll = 0;
3854             var ft = this.el;
3855             
3856             Roo.get(document).on('scroll',function(e) {
3857                 var ns = Roo.get(document).getScroll().top;
3858                 var os = prevScroll;
3859                 prevScroll = ns;
3860                 
3861                 if(ns > os){
3862                     ft.removeClass('slideDown');
3863                     ft.addClass('slideUp');
3864                     return;
3865                 }
3866                 ft.removeClass('slideUp');
3867                 ft.addClass('slideDown');
3868                  
3869               
3870           },this);
3871         }
3872     }    
3873     
3874 });
3875
3876
3877
3878  
3879
3880  /*
3881  * - LGPL
3882  *
3883  * navbar
3884  * 
3885  */
3886
3887 /**
3888  * @class Roo.bootstrap.NavSidebar
3889  * @extends Roo.bootstrap.Navbar
3890  * Bootstrap Sidebar class
3891  * 
3892  * @constructor
3893  * Create a new Sidebar
3894  * @param {Object} config The config object
3895  */
3896
3897
3898 Roo.bootstrap.NavSidebar = function(config){
3899     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3900 };
3901
3902 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3903     
3904     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3905     
3906     getAutoCreate : function(){
3907         
3908         
3909         return  {
3910             tag: 'div',
3911             cls: 'sidebar sidebar-nav'
3912         };
3913     
3914         
3915     }
3916     
3917     
3918     
3919 });
3920
3921
3922
3923  
3924
3925  /*
3926  * - LGPL
3927  *
3928  * nav group
3929  * 
3930  */
3931
3932 /**
3933  * @class Roo.bootstrap.NavGroup
3934  * @extends Roo.bootstrap.Component
3935  * Bootstrap NavGroup class
3936  * @cfg {String} align (left|right)
3937  * @cfg {Boolean} inverse
3938  * @cfg {String} type (nav|pills|tab) default nav
3939  * @cfg {String} navId - reference Id for navbar.
3940
3941  * 
3942  * @constructor
3943  * Create a new nav group
3944  * @param {Object} config The config object
3945  */
3946
3947 Roo.bootstrap.NavGroup = function(config){
3948     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3949     this.navItems = [];
3950    
3951     Roo.bootstrap.NavGroup.register(this);
3952      this.addEvents({
3953         /**
3954              * @event changed
3955              * Fires when the active item changes
3956              * @param {Roo.bootstrap.NavGroup} this
3957              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3958              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3959          */
3960         'changed': true
3961      });
3962     
3963 };
3964
3965 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3966     
3967     align: '',
3968     inverse: false,
3969     form: false,
3970     type: 'nav',
3971     navId : '',
3972     // private
3973     
3974     navItems : false, 
3975     
3976     getAutoCreate : function()
3977     {
3978         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3979         
3980         cfg = {
3981             tag : 'ul',
3982             cls: 'nav' 
3983         };
3984         
3985         if (['tabs','pills'].indexOf(this.type)!==-1) {
3986             cfg.cls += ' nav-' + this.type
3987         } else {
3988             if (this.type!=='nav') {
3989                 Roo.log('nav type must be nav/tabs/pills')
3990             }
3991             cfg.cls += ' navbar-nav'
3992         }
3993         
3994         if (this.parent().sidebar) {
3995             cfg = {
3996                 tag: 'ul',
3997                 cls: 'dashboard-menu sidebar-menu'
3998             };
3999             
4000             return cfg;
4001         }
4002         
4003         if (this.form === true) {
4004             cfg = {
4005                 tag: 'form',
4006                 cls: 'navbar-form'
4007             };
4008             
4009             if (this.align === 'right') {
4010                 cfg.cls += ' navbar-right';
4011             } else {
4012                 cfg.cls += ' navbar-left';
4013             }
4014         }
4015         
4016         if (this.align === 'right') {
4017             cfg.cls += ' navbar-right';
4018         }
4019         
4020         if (this.inverse) {
4021             cfg.cls += ' navbar-inverse';
4022             
4023         }
4024         
4025         
4026         return cfg;
4027     },
4028     /**
4029     * sets the active Navigation item
4030     * @param {Roo.bootstrap.NavItem} the new current navitem
4031     */
4032     setActiveItem : function(item)
4033     {
4034         var prev = false;
4035         Roo.each(this.navItems, function(v){
4036             if (v == item) {
4037                 return ;
4038             }
4039             if (v.isActive()) {
4040                 v.setActive(false, true);
4041                 prev = v;
4042                 
4043             }
4044             
4045         });
4046
4047         item.setActive(true, true);
4048         this.fireEvent('changed', this, item, prev);
4049         
4050         
4051     },
4052     /**
4053     * gets the active Navigation item
4054     * @return {Roo.bootstrap.NavItem} the current navitem
4055     */
4056     getActive : function()
4057     {
4058         
4059         var prev = false;
4060         Roo.each(this.navItems, function(v){
4061             
4062             if (v.isActive()) {
4063                 prev = v;
4064                 
4065             }
4066             
4067         });
4068         return prev;
4069     },
4070     
4071     indexOfNav : function()
4072     {
4073         
4074         var prev = false;
4075         Roo.each(this.navItems, function(v,i){
4076             
4077             if (v.isActive()) {
4078                 prev = i;
4079                 
4080             }
4081             
4082         });
4083         return prev;
4084     },
4085     /**
4086     * adds a Navigation item
4087     * @param {Roo.bootstrap.NavItem} the navitem to add
4088     */
4089     addItem : function(cfg)
4090     {
4091         var cn = new Roo.bootstrap.NavItem(cfg);
4092         this.register(cn);
4093         cn.parentId = this.id;
4094         cn.onRender(this.el, null);
4095         return cn;
4096     },
4097     /**
4098     * register a Navigation item
4099     * @param {Roo.bootstrap.NavItem} the navitem to add
4100     */
4101     register : function(item)
4102     {
4103         this.navItems.push( item);
4104         item.navId = this.navId;
4105     
4106     },
4107     
4108     /**
4109     * clear all the Navigation item
4110     */
4111    
4112     clearAll : function()
4113     {
4114         this.navItems = [];
4115         this.el.dom.innerHTML = '';
4116     },
4117     
4118     getNavItem: function(tabId)
4119     {
4120         var ret = false;
4121         Roo.each(this.navItems, function(e) {
4122             if (e.tabId == tabId) {
4123                ret =  e;
4124                return false;
4125             }
4126             return true;
4127             
4128         });
4129         return ret;
4130     },
4131     
4132     setActiveNext : function()
4133     {
4134         var i = this.indexOfNav(this.getActive());
4135         if (i > this.navItems.length) {
4136             return;
4137         }
4138         this.setActiveItem(this.navItems[i+1]);
4139     },
4140     setActivePrev : function()
4141     {
4142         var i = this.indexOfNav(this.getActive());
4143         if (i  < 1) {
4144             return;
4145         }
4146         this.setActiveItem(this.navItems[i-1]);
4147     },
4148     clearWasActive : function(except) {
4149         Roo.each(this.navItems, function(e) {
4150             if (e.tabId != except.tabId && e.was_active) {
4151                e.was_active = false;
4152                return false;
4153             }
4154             return true;
4155             
4156         });
4157     },
4158     getWasActive : function ()
4159     {
4160         var r = false;
4161         Roo.each(this.navItems, function(e) {
4162             if (e.was_active) {
4163                r = e;
4164                return false;
4165             }
4166             return true;
4167             
4168         });
4169         return r;
4170     }
4171     
4172     
4173 });
4174
4175  
4176 Roo.apply(Roo.bootstrap.NavGroup, {
4177     
4178     groups: {},
4179      /**
4180     * register a Navigation Group
4181     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4182     */
4183     register : function(navgrp)
4184     {
4185         this.groups[navgrp.navId] = navgrp;
4186         
4187     },
4188     /**
4189     * fetch a Navigation Group based on the navigation ID
4190     * @param {string} the navgroup to add
4191     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4192     */
4193     get: function(navId) {
4194         if (typeof(this.groups[navId]) == 'undefined') {
4195             return false;
4196             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4197         }
4198         return this.groups[navId] ;
4199     }
4200     
4201     
4202     
4203 });
4204
4205  /*
4206  * - LGPL
4207  *
4208  * row
4209  * 
4210  */
4211
4212 /**
4213  * @class Roo.bootstrap.NavItem
4214  * @extends Roo.bootstrap.Component
4215  * Bootstrap Navbar.NavItem class
4216  * @cfg {String} href  link to
4217  * @cfg {String} html content of button
4218  * @cfg {String} badge text inside badge
4219  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4220  * @cfg {String} glyphicon name of glyphicon
4221  * @cfg {String} icon name of font awesome icon
4222  * @cfg {Boolean} active Is item active
4223  * @cfg {Boolean} disabled Is item disabled
4224  
4225  * @cfg {Boolean} preventDefault (true | false) default false
4226  * @cfg {String} tabId the tab that this item activates.
4227  * @cfg {String} tagtype (a|span) render as a href or span?
4228  * @cfg {Boolean} animateRef (true|false) link to element default false  
4229   
4230  * @constructor
4231  * Create a new Navbar Item
4232  * @param {Object} config The config object
4233  */
4234 Roo.bootstrap.NavItem = function(config){
4235     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4236     this.addEvents({
4237         // raw events
4238         /**
4239          * @event click
4240          * The raw click event for the entire grid.
4241          * @param {Roo.EventObject} e
4242          */
4243         "click" : true,
4244          /**
4245             * @event changed
4246             * Fires when the active item active state changes
4247             * @param {Roo.bootstrap.NavItem} this
4248             * @param {boolean} state the new state
4249              
4250          */
4251         'changed': true,
4252         /**
4253             * @event scrollto
4254             * Fires when scroll to element
4255             * @param {Roo.bootstrap.NavItem} this
4256             * @param {Object} options
4257             * @param {Roo.EventObject} e
4258              
4259          */
4260         'scrollto': true
4261     });
4262    
4263 };
4264
4265 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4266     
4267     href: false,
4268     html: '',
4269     badge: '',
4270     icon: false,
4271     glyphicon: false,
4272     active: false,
4273     preventDefault : false,
4274     tabId : false,
4275     tagtype : 'a',
4276     disabled : false,
4277     animateRef : false,
4278     was_active : false,
4279     
4280     getAutoCreate : function(){
4281          
4282         var cfg = {
4283             tag: 'li',
4284             cls: 'nav-item'
4285             
4286         };
4287         
4288         if (this.active) {
4289             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4290         }
4291         if (this.disabled) {
4292             cfg.cls += ' disabled';
4293         }
4294         
4295         if (this.href || this.html || this.glyphicon || this.icon) {
4296             cfg.cn = [
4297                 {
4298                     tag: this.tagtype,
4299                     href : this.href || "#",
4300                     html: this.html || ''
4301                 }
4302             ];
4303             
4304             if (this.icon) {
4305                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4306             }
4307
4308             if(this.glyphicon) {
4309                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4310             }
4311             
4312             if (this.menu) {
4313                 
4314                 cfg.cn[0].html += " <span class='caret'></span>";
4315              
4316             }
4317             
4318             if (this.badge !== '') {
4319                  
4320                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4321             }
4322         }
4323         
4324         
4325         
4326         return cfg;
4327     },
4328     initEvents: function() 
4329     {
4330         if (typeof (this.menu) != 'undefined') {
4331             this.menu.parentType = this.xtype;
4332             this.menu.triggerEl = this.el;
4333             this.menu = this.addxtype(Roo.apply({}, this.menu));
4334         }
4335         
4336         this.el.select('a',true).on('click', this.onClick, this);
4337         
4338         if(this.tagtype == 'span'){
4339             this.el.select('span',true).on('click', this.onClick, this);
4340         }
4341        
4342         // at this point parent should be available..
4343         this.parent().register(this);
4344     },
4345     
4346     onClick : function(e)
4347     {
4348         if (e.getTarget('.dropdown-menu-item')) {
4349             // did you click on a menu itemm.... - then don't trigger onclick..
4350             return;
4351         }
4352         
4353         if(
4354                 this.preventDefault || 
4355                 this.href == '#' 
4356         ){
4357             Roo.log("NavItem - prevent Default?");
4358             e.preventDefault();
4359         }
4360         
4361         if (this.disabled) {
4362             return;
4363         }
4364         
4365         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4366         if (tg && tg.transition) {
4367             Roo.log("waiting for the transitionend");
4368             return;
4369         }
4370         
4371         
4372         
4373         //Roo.log("fire event clicked");
4374         if(this.fireEvent('click', this, e) === false){
4375             return;
4376         };
4377         
4378         if(this.tagtype == 'span'){
4379             return;
4380         }
4381         
4382         //Roo.log(this.href);
4383         var ael = this.el.select('a',true).first();
4384         //Roo.log(ael);
4385         
4386         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4387             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4388             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4389                 return; // ignore... - it's a 'hash' to another page.
4390             }
4391             Roo.log("NavItem - prevent Default?");
4392             e.preventDefault();
4393             this.scrollToElement(e);
4394         }
4395         
4396         
4397         var p =  this.parent();
4398    
4399         if (['tabs','pills'].indexOf(p.type)!==-1) {
4400             if (typeof(p.setActiveItem) !== 'undefined') {
4401                 p.setActiveItem(this);
4402             }
4403         }
4404         
4405         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4406         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4407             // remove the collapsed menu expand...
4408             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4409         }
4410     },
4411     
4412     isActive: function () {
4413         return this.active
4414     },
4415     setActive : function(state, fire, is_was_active)
4416     {
4417         if (this.active && !state && this.navId) {
4418             this.was_active = true;
4419             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4420             if (nv) {
4421                 nv.clearWasActive(this);
4422             }
4423             
4424         }
4425         this.active = state;
4426         
4427         if (!state ) {
4428             this.el.removeClass('active');
4429         } else if (!this.el.hasClass('active')) {
4430             this.el.addClass('active');
4431         }
4432         if (fire) {
4433             this.fireEvent('changed', this, state);
4434         }
4435         
4436         // show a panel if it's registered and related..
4437         
4438         if (!this.navId || !this.tabId || !state || is_was_active) {
4439             return;
4440         }
4441         
4442         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4443         if (!tg) {
4444             return;
4445         }
4446         var pan = tg.getPanelByName(this.tabId);
4447         if (!pan) {
4448             return;
4449         }
4450         // if we can not flip to new panel - go back to old nav highlight..
4451         if (false == tg.showPanel(pan)) {
4452             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4453             if (nv) {
4454                 var onav = nv.getWasActive();
4455                 if (onav) {
4456                     onav.setActive(true, false, true);
4457                 }
4458             }
4459             
4460         }
4461         
4462         
4463         
4464     },
4465      // this should not be here...
4466     setDisabled : function(state)
4467     {
4468         this.disabled = state;
4469         if (!state ) {
4470             this.el.removeClass('disabled');
4471         } else if (!this.el.hasClass('disabled')) {
4472             this.el.addClass('disabled');
4473         }
4474         
4475     },
4476     
4477     /**
4478      * Fetch the element to display the tooltip on.
4479      * @return {Roo.Element} defaults to this.el
4480      */
4481     tooltipEl : function()
4482     {
4483         return this.el.select('' + this.tagtype + '', true).first();
4484     },
4485     
4486     scrollToElement : function(e)
4487     {
4488         var c = document.body;
4489         
4490         /*
4491          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4492          */
4493         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4494             c = document.documentElement;
4495         }
4496         
4497         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4498         
4499         if(!target){
4500             return;
4501         }
4502
4503         var o = target.calcOffsetsTo(c);
4504         
4505         var options = {
4506             target : target,
4507             value : o[1]
4508         };
4509         
4510         this.fireEvent('scrollto', this, options, e);
4511         
4512         Roo.get(c).scrollTo('top', options.value, true);
4513         
4514         return;
4515     }
4516 });
4517  
4518
4519  /*
4520  * - LGPL
4521  *
4522  * sidebar item
4523  *
4524  *  li
4525  *    <span> icon </span>
4526  *    <span> text </span>
4527  *    <span>badge </span>
4528  */
4529
4530 /**
4531  * @class Roo.bootstrap.NavSidebarItem
4532  * @extends Roo.bootstrap.NavItem
4533  * Bootstrap Navbar.NavSidebarItem class
4534  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4535  * {bool} open is the menu open
4536  * @constructor
4537  * Create a new Navbar Button
4538  * @param {Object} config The config object
4539  */
4540 Roo.bootstrap.NavSidebarItem = function(config){
4541     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4542     this.addEvents({
4543         // raw events
4544         /**
4545          * @event click
4546          * The raw click event for the entire grid.
4547          * @param {Roo.EventObject} e
4548          */
4549         "click" : true,
4550          /**
4551             * @event changed
4552             * Fires when the active item active state changes
4553             * @param {Roo.bootstrap.NavSidebarItem} this
4554             * @param {boolean} state the new state
4555              
4556          */
4557         'changed': true
4558     });
4559    
4560 };
4561
4562 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4563     
4564     badgeWeight : 'default',
4565     
4566     open: false,
4567     
4568     getAutoCreate : function(){
4569         
4570         
4571         var a = {
4572                 tag: 'a',
4573                 href : this.href || '#',
4574                 cls: '',
4575                 html : '',
4576                 cn : []
4577         };
4578         var cfg = {
4579             tag: 'li',
4580             cls: '',
4581             cn: [ a ]
4582         };
4583         var span = {
4584             tag: 'span',
4585             html : this.html || ''
4586         };
4587         
4588         
4589         if (this.active) {
4590             cfg.cls += ' active';
4591         }
4592         
4593         if (this.disabled) {
4594             cfg.cls += ' disabled';
4595         }
4596         if (this.open) {
4597             cfg.cls += ' open x-open';
4598         }
4599         // left icon..
4600         if (this.glyphicon || this.icon) {
4601             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4602             a.cn.push({ tag : 'i', cls : c }) ;
4603         }
4604         // html..
4605         a.cn.push(span);
4606         // then badge..
4607         if (this.badge !== '') {
4608             
4609             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4610         }
4611         // fi
4612         if (this.menu) {
4613             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4614             a.cls += 'dropdown-toggle treeview' ;
4615         }
4616         
4617         return cfg;
4618          
4619            
4620     },
4621     
4622     initEvents : function()
4623     { 
4624         if (typeof (this.menu) != 'undefined') {
4625             this.menu.parentType = this.xtype;
4626             this.menu.triggerEl = this.el;
4627             this.menu = this.addxtype(Roo.apply({}, this.menu));
4628         }
4629         
4630         this.el.on('click', this.onClick, this);
4631        
4632     
4633         if(this.badge !== ''){
4634  
4635             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4636         }
4637         
4638     },
4639     
4640     onClick : function(e)
4641     {
4642         if(this.disabled){
4643             e.preventDefault();
4644             return;
4645         }
4646         
4647         if(this.preventDefault){
4648             e.preventDefault();
4649         }
4650         
4651         this.fireEvent('click', this);
4652     },
4653     
4654     disable : function()
4655     {
4656         this.setDisabled(true);
4657     },
4658     
4659     enable : function()
4660     {
4661         this.setDisabled(false);
4662     },
4663     
4664     setDisabled : function(state)
4665     {
4666         if(this.disabled == state){
4667             return;
4668         }
4669         
4670         this.disabled = state;
4671         
4672         if (state) {
4673             this.el.addClass('disabled');
4674             return;
4675         }
4676         
4677         this.el.removeClass('disabled');
4678         
4679         return;
4680     },
4681     
4682     setActive : function(state)
4683     {
4684         if(this.active == state){
4685             return;
4686         }
4687         
4688         this.active = state;
4689         
4690         if (state) {
4691             this.el.addClass('active');
4692             return;
4693         }
4694         
4695         this.el.removeClass('active');
4696         
4697         return;
4698     },
4699     
4700     isActive: function () 
4701     {
4702         return this.active;
4703     },
4704     
4705     setBadge : function(str)
4706     {
4707         if(!this.badgeEl){
4708             return;
4709         }
4710         
4711         this.badgeEl.dom.innerHTML = str;
4712     }
4713     
4714    
4715      
4716  
4717 });
4718  
4719
4720  /*
4721  * - LGPL
4722  *
4723  * row
4724  * 
4725  */
4726
4727 /**
4728  * @class Roo.bootstrap.Row
4729  * @extends Roo.bootstrap.Component
4730  * Bootstrap Row class (contains columns...)
4731  * 
4732  * @constructor
4733  * Create a new Row
4734  * @param {Object} config The config object
4735  */
4736
4737 Roo.bootstrap.Row = function(config){
4738     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4739 };
4740
4741 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4742     
4743     getAutoCreate : function(){
4744        return {
4745             cls: 'row clearfix'
4746        };
4747     }
4748     
4749     
4750 });
4751
4752  
4753
4754  /*
4755  * - LGPL
4756  *
4757  * element
4758  * 
4759  */
4760
4761 /**
4762  * @class Roo.bootstrap.Element
4763  * @extends Roo.bootstrap.Component
4764  * Bootstrap Element class
4765  * @cfg {String} html contents of the element
4766  * @cfg {String} tag tag of the element
4767  * @cfg {String} cls class of the element
4768  * @cfg {Boolean} preventDefault (true|false) default false
4769  * @cfg {Boolean} clickable (true|false) default false
4770  * 
4771  * @constructor
4772  * Create a new Element
4773  * @param {Object} config The config object
4774  */
4775
4776 Roo.bootstrap.Element = function(config){
4777     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4778     
4779     this.addEvents({
4780         // raw events
4781         /**
4782          * @event click
4783          * When a element is chick
4784          * @param {Roo.bootstrap.Element} this
4785          * @param {Roo.EventObject} e
4786          */
4787         "click" : true
4788     });
4789 };
4790
4791 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4792     
4793     tag: 'div',
4794     cls: '',
4795     html: '',
4796     preventDefault: false, 
4797     clickable: false,
4798     
4799     getAutoCreate : function(){
4800         
4801         var cfg = {
4802             tag: this.tag,
4803             cls: this.cls,
4804             html: this.html
4805         };
4806         
4807         return cfg;
4808     },
4809     
4810     initEvents: function() 
4811     {
4812         Roo.bootstrap.Element.superclass.initEvents.call(this);
4813         
4814         if(this.clickable){
4815             this.el.on('click', this.onClick, this);
4816         }
4817         
4818     },
4819     
4820     onClick : function(e)
4821     {
4822         if(this.preventDefault){
4823             e.preventDefault();
4824         }
4825         
4826         this.fireEvent('click', this, e);
4827     },
4828     
4829     getValue : function()
4830     {
4831         return this.el.dom.innerHTML;
4832     },
4833     
4834     setValue : function(value)
4835     {
4836         this.el.dom.innerHTML = value;
4837     }
4838    
4839 });
4840
4841  
4842
4843  /*
4844  * - LGPL
4845  *
4846  * pagination
4847  * 
4848  */
4849
4850 /**
4851  * @class Roo.bootstrap.Pagination
4852  * @extends Roo.bootstrap.Component
4853  * Bootstrap Pagination class
4854  * @cfg {String} size xs | sm | md | lg
4855  * @cfg {Boolean} inverse false | true
4856  * 
4857  * @constructor
4858  * Create a new Pagination
4859  * @param {Object} config The config object
4860  */
4861
4862 Roo.bootstrap.Pagination = function(config){
4863     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4864 };
4865
4866 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4867     
4868     cls: false,
4869     size: false,
4870     inverse: false,
4871     
4872     getAutoCreate : function(){
4873         var cfg = {
4874             tag: 'ul',
4875                 cls: 'pagination'
4876         };
4877         if (this.inverse) {
4878             cfg.cls += ' inverse';
4879         }
4880         if (this.html) {
4881             cfg.html=this.html;
4882         }
4883         if (this.cls) {
4884             cfg.cls += " " + this.cls;
4885         }
4886         return cfg;
4887     }
4888    
4889 });
4890
4891  
4892
4893  /*
4894  * - LGPL
4895  *
4896  * Pagination item
4897  * 
4898  */
4899
4900
4901 /**
4902  * @class Roo.bootstrap.PaginationItem
4903  * @extends Roo.bootstrap.Component
4904  * Bootstrap PaginationItem class
4905  * @cfg {String} html text
4906  * @cfg {String} href the link
4907  * @cfg {Boolean} preventDefault (true | false) default true
4908  * @cfg {Boolean} active (true | false) default false
4909  * @cfg {Boolean} disabled default false
4910  * 
4911  * 
4912  * @constructor
4913  * Create a new PaginationItem
4914  * @param {Object} config The config object
4915  */
4916
4917
4918 Roo.bootstrap.PaginationItem = function(config){
4919     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4920     this.addEvents({
4921         // raw events
4922         /**
4923          * @event click
4924          * The raw click event for the entire grid.
4925          * @param {Roo.EventObject} e
4926          */
4927         "click" : true
4928     });
4929 };
4930
4931 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4932     
4933     href : false,
4934     html : false,
4935     preventDefault: true,
4936     active : false,
4937     cls : false,
4938     disabled: false,
4939     
4940     getAutoCreate : function(){
4941         var cfg= {
4942             tag: 'li',
4943             cn: [
4944                 {
4945                     tag : 'a',
4946                     href : this.href ? this.href : '#',
4947                     html : this.html ? this.html : ''
4948                 }
4949             ]
4950         };
4951         
4952         if(this.cls){
4953             cfg.cls = this.cls;
4954         }
4955         
4956         if(this.disabled){
4957             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4958         }
4959         
4960         if(this.active){
4961             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4962         }
4963         
4964         return cfg;
4965     },
4966     
4967     initEvents: function() {
4968         
4969         this.el.on('click', this.onClick, this);
4970         
4971     },
4972     onClick : function(e)
4973     {
4974         Roo.log('PaginationItem on click ');
4975         if(this.preventDefault){
4976             e.preventDefault();
4977         }
4978         
4979         if(this.disabled){
4980             return;
4981         }
4982         
4983         this.fireEvent('click', this, e);
4984     }
4985    
4986 });
4987
4988  
4989
4990  /*
4991  * - LGPL
4992  *
4993  * slider
4994  * 
4995  */
4996
4997
4998 /**
4999  * @class Roo.bootstrap.Slider
5000  * @extends Roo.bootstrap.Component
5001  * Bootstrap Slider class
5002  *    
5003  * @constructor
5004  * Create a new Slider
5005  * @param {Object} config The config object
5006  */
5007
5008 Roo.bootstrap.Slider = function(config){
5009     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5010 };
5011
5012 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5013     
5014     getAutoCreate : function(){
5015         
5016         var cfg = {
5017             tag: 'div',
5018             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5019             cn: [
5020                 {
5021                     tag: 'a',
5022                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5023                 }
5024             ]
5025         };
5026         
5027         return cfg;
5028     }
5029    
5030 });
5031
5032  /*
5033  * Based on:
5034  * Ext JS Library 1.1.1
5035  * Copyright(c) 2006-2007, Ext JS, LLC.
5036  *
5037  * Originally Released Under LGPL - original licence link has changed is not relivant.
5038  *
5039  * Fork - LGPL
5040  * <script type="text/javascript">
5041  */
5042  
5043
5044 /**
5045  * @class Roo.grid.ColumnModel
5046  * @extends Roo.util.Observable
5047  * This is the default implementation of a ColumnModel used by the Grid. It defines
5048  * the columns in the grid.
5049  * <br>Usage:<br>
5050  <pre><code>
5051  var colModel = new Roo.grid.ColumnModel([
5052         {header: "Ticker", width: 60, sortable: true, locked: true},
5053         {header: "Company Name", width: 150, sortable: true},
5054         {header: "Market Cap.", width: 100, sortable: true},
5055         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5056         {header: "Employees", width: 100, sortable: true, resizable: false}
5057  ]);
5058  </code></pre>
5059  * <p>
5060  
5061  * The config options listed for this class are options which may appear in each
5062  * individual column definition.
5063  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5064  * @constructor
5065  * @param {Object} config An Array of column config objects. See this class's
5066  * config objects for details.
5067 */
5068 Roo.grid.ColumnModel = function(config){
5069         /**
5070      * The config passed into the constructor
5071      */
5072     this.config = config;
5073     this.lookup = {};
5074
5075     // if no id, create one
5076     // if the column does not have a dataIndex mapping,
5077     // map it to the order it is in the config
5078     for(var i = 0, len = config.length; i < len; i++){
5079         var c = config[i];
5080         if(typeof c.dataIndex == "undefined"){
5081             c.dataIndex = i;
5082         }
5083         if(typeof c.renderer == "string"){
5084             c.renderer = Roo.util.Format[c.renderer];
5085         }
5086         if(typeof c.id == "undefined"){
5087             c.id = Roo.id();
5088         }
5089         if(c.editor && c.editor.xtype){
5090             c.editor  = Roo.factory(c.editor, Roo.grid);
5091         }
5092         if(c.editor && c.editor.isFormField){
5093             c.editor = new Roo.grid.GridEditor(c.editor);
5094         }
5095         this.lookup[c.id] = c;
5096     }
5097
5098     /**
5099      * The width of columns which have no width specified (defaults to 100)
5100      * @type Number
5101      */
5102     this.defaultWidth = 100;
5103
5104     /**
5105      * Default sortable of columns which have no sortable specified (defaults to false)
5106      * @type Boolean
5107      */
5108     this.defaultSortable = false;
5109
5110     this.addEvents({
5111         /**
5112              * @event widthchange
5113              * Fires when the width of a column changes.
5114              * @param {ColumnModel} this
5115              * @param {Number} columnIndex The column index
5116              * @param {Number} newWidth The new width
5117              */
5118             "widthchange": true,
5119         /**
5120              * @event headerchange
5121              * Fires when the text of a header changes.
5122              * @param {ColumnModel} this
5123              * @param {Number} columnIndex The column index
5124              * @param {Number} newText The new header text
5125              */
5126             "headerchange": true,
5127         /**
5128              * @event hiddenchange
5129              * Fires when a column is hidden or "unhidden".
5130              * @param {ColumnModel} this
5131              * @param {Number} columnIndex The column index
5132              * @param {Boolean} hidden true if hidden, false otherwise
5133              */
5134             "hiddenchange": true,
5135             /**
5136          * @event columnmoved
5137          * Fires when a column is moved.
5138          * @param {ColumnModel} this
5139          * @param {Number} oldIndex
5140          * @param {Number} newIndex
5141          */
5142         "columnmoved" : true,
5143         /**
5144          * @event columlockchange
5145          * Fires when a column's locked state is changed
5146          * @param {ColumnModel} this
5147          * @param {Number} colIndex
5148          * @param {Boolean} locked true if locked
5149          */
5150         "columnlockchange" : true
5151     });
5152     Roo.grid.ColumnModel.superclass.constructor.call(this);
5153 };
5154 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5155     /**
5156      * @cfg {String} header The header text to display in the Grid view.
5157      */
5158     /**
5159      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5160      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5161      * specified, the column's index is used as an index into the Record's data Array.
5162      */
5163     /**
5164      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5165      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5166      */
5167     /**
5168      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5169      * Defaults to the value of the {@link #defaultSortable} property.
5170      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5171      */
5172     /**
5173      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5174      */
5175     /**
5176      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5177      */
5178     /**
5179      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5180      */
5181     /**
5182      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5183      */
5184     /**
5185      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5186      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5187      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5188      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5189      */
5190        /**
5191      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5192      */
5193     /**
5194      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5195      */
5196     /**
5197      * @cfg {String} cursor (Optional)
5198      */
5199     /**
5200      * @cfg {String} tooltip (Optional)
5201      */
5202     /**
5203      * @cfg {Number} xs (Optional)
5204      */
5205     /**
5206      * @cfg {Number} sm (Optional)
5207      */
5208     /**
5209      * @cfg {Number} md (Optional)
5210      */
5211     /**
5212      * @cfg {Number} lg (Optional)
5213      */
5214     /**
5215      * Returns the id of the column at the specified index.
5216      * @param {Number} index The column index
5217      * @return {String} the id
5218      */
5219     getColumnId : function(index){
5220         return this.config[index].id;
5221     },
5222
5223     /**
5224      * Returns the column for a specified id.
5225      * @param {String} id The column id
5226      * @return {Object} the column
5227      */
5228     getColumnById : function(id){
5229         return this.lookup[id];
5230     },
5231
5232     
5233     /**
5234      * Returns the column for a specified dataIndex.
5235      * @param {String} dataIndex The column dataIndex
5236      * @return {Object|Boolean} the column or false if not found
5237      */
5238     getColumnByDataIndex: function(dataIndex){
5239         var index = this.findColumnIndex(dataIndex);
5240         return index > -1 ? this.config[index] : false;
5241     },
5242     
5243     /**
5244      * Returns the index for a specified column id.
5245      * @param {String} id The column id
5246      * @return {Number} the index, or -1 if not found
5247      */
5248     getIndexById : function(id){
5249         for(var i = 0, len = this.config.length; i < len; i++){
5250             if(this.config[i].id == id){
5251                 return i;
5252             }
5253         }
5254         return -1;
5255     },
5256     
5257     /**
5258      * Returns the index for a specified column dataIndex.
5259      * @param {String} dataIndex The column dataIndex
5260      * @return {Number} the index, or -1 if not found
5261      */
5262     
5263     findColumnIndex : function(dataIndex){
5264         for(var i = 0, len = this.config.length; i < len; i++){
5265             if(this.config[i].dataIndex == dataIndex){
5266                 return i;
5267             }
5268         }
5269         return -1;
5270     },
5271     
5272     
5273     moveColumn : function(oldIndex, newIndex){
5274         var c = this.config[oldIndex];
5275         this.config.splice(oldIndex, 1);
5276         this.config.splice(newIndex, 0, c);
5277         this.dataMap = null;
5278         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5279     },
5280
5281     isLocked : function(colIndex){
5282         return this.config[colIndex].locked === true;
5283     },
5284
5285     setLocked : function(colIndex, value, suppressEvent){
5286         if(this.isLocked(colIndex) == value){
5287             return;
5288         }
5289         this.config[colIndex].locked = value;
5290         if(!suppressEvent){
5291             this.fireEvent("columnlockchange", this, colIndex, value);
5292         }
5293     },
5294
5295     getTotalLockedWidth : function(){
5296         var totalWidth = 0;
5297         for(var i = 0; i < this.config.length; i++){
5298             if(this.isLocked(i) && !this.isHidden(i)){
5299                 this.totalWidth += this.getColumnWidth(i);
5300             }
5301         }
5302         return totalWidth;
5303     },
5304
5305     getLockedCount : function(){
5306         for(var i = 0, len = this.config.length; i < len; i++){
5307             if(!this.isLocked(i)){
5308                 return i;
5309             }
5310         }
5311         
5312         return this.config.length;
5313     },
5314
5315     /**
5316      * Returns the number of columns.
5317      * @return {Number}
5318      */
5319     getColumnCount : function(visibleOnly){
5320         if(visibleOnly === true){
5321             var c = 0;
5322             for(var i = 0, len = this.config.length; i < len; i++){
5323                 if(!this.isHidden(i)){
5324                     c++;
5325                 }
5326             }
5327             return c;
5328         }
5329         return this.config.length;
5330     },
5331
5332     /**
5333      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5334      * @param {Function} fn
5335      * @param {Object} scope (optional)
5336      * @return {Array} result
5337      */
5338     getColumnsBy : function(fn, scope){
5339         var r = [];
5340         for(var i = 0, len = this.config.length; i < len; i++){
5341             var c = this.config[i];
5342             if(fn.call(scope||this, c, i) === true){
5343                 r[r.length] = c;
5344             }
5345         }
5346         return r;
5347     },
5348
5349     /**
5350      * Returns true if the specified column is sortable.
5351      * @param {Number} col The column index
5352      * @return {Boolean}
5353      */
5354     isSortable : function(col){
5355         if(typeof this.config[col].sortable == "undefined"){
5356             return this.defaultSortable;
5357         }
5358         return this.config[col].sortable;
5359     },
5360
5361     /**
5362      * Returns the rendering (formatting) function defined for the column.
5363      * @param {Number} col The column index.
5364      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5365      */
5366     getRenderer : function(col){
5367         if(!this.config[col].renderer){
5368             return Roo.grid.ColumnModel.defaultRenderer;
5369         }
5370         return this.config[col].renderer;
5371     },
5372
5373     /**
5374      * Sets the rendering (formatting) function for a column.
5375      * @param {Number} col The column index
5376      * @param {Function} fn The function to use to process the cell's raw data
5377      * to return HTML markup for the grid view. The render function is called with
5378      * the following parameters:<ul>
5379      * <li>Data value.</li>
5380      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5381      * <li>css A CSS style string to apply to the table cell.</li>
5382      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5383      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5384      * <li>Row index</li>
5385      * <li>Column index</li>
5386      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5387      */
5388     setRenderer : function(col, fn){
5389         this.config[col].renderer = fn;
5390     },
5391
5392     /**
5393      * Returns the width for the specified column.
5394      * @param {Number} col The column index
5395      * @return {Number}
5396      */
5397     getColumnWidth : function(col){
5398         return this.config[col].width * 1 || this.defaultWidth;
5399     },
5400
5401     /**
5402      * Sets the width for a column.
5403      * @param {Number} col The column index
5404      * @param {Number} width The new width
5405      */
5406     setColumnWidth : function(col, width, suppressEvent){
5407         this.config[col].width = width;
5408         this.totalWidth = null;
5409         if(!suppressEvent){
5410              this.fireEvent("widthchange", this, col, width);
5411         }
5412     },
5413
5414     /**
5415      * Returns the total width of all columns.
5416      * @param {Boolean} includeHidden True to include hidden column widths
5417      * @return {Number}
5418      */
5419     getTotalWidth : function(includeHidden){
5420         if(!this.totalWidth){
5421             this.totalWidth = 0;
5422             for(var i = 0, len = this.config.length; i < len; i++){
5423                 if(includeHidden || !this.isHidden(i)){
5424                     this.totalWidth += this.getColumnWidth(i);
5425                 }
5426             }
5427         }
5428         return this.totalWidth;
5429     },
5430
5431     /**
5432      * Returns the header for the specified column.
5433      * @param {Number} col The column index
5434      * @return {String}
5435      */
5436     getColumnHeader : function(col){
5437         return this.config[col].header;
5438     },
5439
5440     /**
5441      * Sets the header for a column.
5442      * @param {Number} col The column index
5443      * @param {String} header The new header
5444      */
5445     setColumnHeader : function(col, header){
5446         this.config[col].header = header;
5447         this.fireEvent("headerchange", this, col, header);
5448     },
5449
5450     /**
5451      * Returns the tooltip for the specified column.
5452      * @param {Number} col The column index
5453      * @return {String}
5454      */
5455     getColumnTooltip : function(col){
5456             return this.config[col].tooltip;
5457     },
5458     /**
5459      * Sets the tooltip for a column.
5460      * @param {Number} col The column index
5461      * @param {String} tooltip The new tooltip
5462      */
5463     setColumnTooltip : function(col, tooltip){
5464             this.config[col].tooltip = tooltip;
5465     },
5466
5467     /**
5468      * Returns the dataIndex for the specified column.
5469      * @param {Number} col The column index
5470      * @return {Number}
5471      */
5472     getDataIndex : function(col){
5473         return this.config[col].dataIndex;
5474     },
5475
5476     /**
5477      * Sets the dataIndex for a column.
5478      * @param {Number} col The column index
5479      * @param {Number} dataIndex The new dataIndex
5480      */
5481     setDataIndex : function(col, dataIndex){
5482         this.config[col].dataIndex = dataIndex;
5483     },
5484
5485     
5486     
5487     /**
5488      * Returns true if the cell is editable.
5489      * @param {Number} colIndex The column index
5490      * @param {Number} rowIndex The row index - this is nto actually used..?
5491      * @return {Boolean}
5492      */
5493     isCellEditable : function(colIndex, rowIndex){
5494         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5495     },
5496
5497     /**
5498      * Returns the editor defined for the cell/column.
5499      * return false or null to disable editing.
5500      * @param {Number} colIndex The column index
5501      * @param {Number} rowIndex The row index
5502      * @return {Object}
5503      */
5504     getCellEditor : function(colIndex, rowIndex){
5505         return this.config[colIndex].editor;
5506     },
5507
5508     /**
5509      * Sets if a column is editable.
5510      * @param {Number} col The column index
5511      * @param {Boolean} editable True if the column is editable
5512      */
5513     setEditable : function(col, editable){
5514         this.config[col].editable = editable;
5515     },
5516
5517
5518     /**
5519      * Returns true if the column is hidden.
5520      * @param {Number} colIndex The column index
5521      * @return {Boolean}
5522      */
5523     isHidden : function(colIndex){
5524         return this.config[colIndex].hidden;
5525     },
5526
5527
5528     /**
5529      * Returns true if the column width cannot be changed
5530      */
5531     isFixed : function(colIndex){
5532         return this.config[colIndex].fixed;
5533     },
5534
5535     /**
5536      * Returns true if the column can be resized
5537      * @return {Boolean}
5538      */
5539     isResizable : function(colIndex){
5540         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5541     },
5542     /**
5543      * Sets if a column is hidden.
5544      * @param {Number} colIndex The column index
5545      * @param {Boolean} hidden True if the column is hidden
5546      */
5547     setHidden : function(colIndex, hidden){
5548         this.config[colIndex].hidden = hidden;
5549         this.totalWidth = null;
5550         this.fireEvent("hiddenchange", this, colIndex, hidden);
5551     },
5552
5553     /**
5554      * Sets the editor for a column.
5555      * @param {Number} col The column index
5556      * @param {Object} editor The editor object
5557      */
5558     setEditor : function(col, editor){
5559         this.config[col].editor = editor;
5560     }
5561 });
5562
5563 Roo.grid.ColumnModel.defaultRenderer = function(value)
5564 {
5565     if(typeof value == "object") {
5566         return value;
5567     }
5568         if(typeof value == "string" && value.length < 1){
5569             return "&#160;";
5570         }
5571     
5572         return String.format("{0}", value);
5573 };
5574
5575 // Alias for backwards compatibility
5576 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5577 /*
5578  * Based on:
5579  * Ext JS Library 1.1.1
5580  * Copyright(c) 2006-2007, Ext JS, LLC.
5581  *
5582  * Originally Released Under LGPL - original licence link has changed is not relivant.
5583  *
5584  * Fork - LGPL
5585  * <script type="text/javascript">
5586  */
5587  
5588 /**
5589  * @class Roo.LoadMask
5590  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5591  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5592  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5593  * element's UpdateManager load indicator and will be destroyed after the initial load.
5594  * @constructor
5595  * Create a new LoadMask
5596  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5597  * @param {Object} config The config object
5598  */
5599 Roo.LoadMask = function(el, config){
5600     this.el = Roo.get(el);
5601     Roo.apply(this, config);
5602     if(this.store){
5603         this.store.on('beforeload', this.onBeforeLoad, this);
5604         this.store.on('load', this.onLoad, this);
5605         this.store.on('loadexception', this.onLoadException, this);
5606         this.removeMask = false;
5607     }else{
5608         var um = this.el.getUpdateManager();
5609         um.showLoadIndicator = false; // disable the default indicator
5610         um.on('beforeupdate', this.onBeforeLoad, this);
5611         um.on('update', this.onLoad, this);
5612         um.on('failure', this.onLoad, this);
5613         this.removeMask = true;
5614     }
5615 };
5616
5617 Roo.LoadMask.prototype = {
5618     /**
5619      * @cfg {Boolean} removeMask
5620      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5621      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5622      */
5623     /**
5624      * @cfg {String} msg
5625      * The text to display in a centered loading message box (defaults to 'Loading...')
5626      */
5627     msg : 'Loading...',
5628     /**
5629      * @cfg {String} msgCls
5630      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5631      */
5632     msgCls : 'x-mask-loading',
5633
5634     /**
5635      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5636      * @type Boolean
5637      */
5638     disabled: false,
5639
5640     /**
5641      * Disables the mask to prevent it from being displayed
5642      */
5643     disable : function(){
5644        this.disabled = true;
5645     },
5646
5647     /**
5648      * Enables the mask so that it can be displayed
5649      */
5650     enable : function(){
5651         this.disabled = false;
5652     },
5653     
5654     onLoadException : function()
5655     {
5656         Roo.log(arguments);
5657         
5658         if (typeof(arguments[3]) != 'undefined') {
5659             Roo.MessageBox.alert("Error loading",arguments[3]);
5660         } 
5661         /*
5662         try {
5663             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5664                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5665             }   
5666         } catch(e) {
5667             
5668         }
5669         */
5670     
5671         
5672         
5673         this.el.unmask(this.removeMask);
5674     },
5675     // private
5676     onLoad : function()
5677     {
5678         this.el.unmask(this.removeMask);
5679     },
5680
5681     // private
5682     onBeforeLoad : function(){
5683         if(!this.disabled){
5684             (function() { this.el.mask(this.msg, this.msgCls) }).defer(50, this);
5685         }
5686     },
5687
5688     // private
5689     destroy : function(){
5690         if(this.store){
5691             this.store.un('beforeload', this.onBeforeLoad, this);
5692             this.store.un('load', this.onLoad, this);
5693             this.store.un('loadexception', this.onLoadException, this);
5694         }else{
5695             var um = this.el.getUpdateManager();
5696             um.un('beforeupdate', this.onBeforeLoad, this);
5697             um.un('update', this.onLoad, this);
5698             um.un('failure', this.onLoad, this);
5699         }
5700     }
5701 };/*
5702  * - LGPL
5703  *
5704  * table
5705  * 
5706  */
5707
5708 /**
5709  * @class Roo.bootstrap.Table
5710  * @extends Roo.bootstrap.Component
5711  * Bootstrap Table class
5712  * @cfg {String} cls table class
5713  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5714  * @cfg {String} bgcolor Specifies the background color for a table
5715  * @cfg {Number} border Specifies whether the table cells should have borders or not
5716  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5717  * @cfg {Number} cellspacing Specifies the space between cells
5718  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5719  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5720  * @cfg {String} sortable Specifies that the table should be sortable
5721  * @cfg {String} summary Specifies a summary of the content of a table
5722  * @cfg {Number} width Specifies the width of a table
5723  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5724  * 
5725  * @cfg {boolean} striped Should the rows be alternative striped
5726  * @cfg {boolean} bordered Add borders to the table
5727  * @cfg {boolean} hover Add hover highlighting
5728  * @cfg {boolean} condensed Format condensed
5729  * @cfg {boolean} responsive Format condensed
5730  * @cfg {Boolean} loadMask (true|false) default false
5731  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5732  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5733  * @cfg {Boolean} rowSelection (true|false) default false
5734  * @cfg {Boolean} cellSelection (true|false) default false
5735  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5736  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5737  
5738  * 
5739  * @constructor
5740  * Create a new Table
5741  * @param {Object} config The config object
5742  */
5743
5744 Roo.bootstrap.Table = function(config){
5745     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5746     
5747   
5748     
5749     // BC...
5750     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5751     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5752     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5753     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5754     
5755     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5756     if (this.sm) {
5757         this.sm.grid = this;
5758         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5759         this.sm = this.selModel;
5760         this.sm.xmodule = this.xmodule || false;
5761     }
5762     
5763     if (this.cm && typeof(this.cm.config) == 'undefined') {
5764         this.colModel = new Roo.grid.ColumnModel(this.cm);
5765         this.cm = this.colModel;
5766         this.cm.xmodule = this.xmodule || false;
5767     }
5768     if (this.store) {
5769         this.store= Roo.factory(this.store, Roo.data);
5770         this.ds = this.store;
5771         this.ds.xmodule = this.xmodule || false;
5772          
5773     }
5774     if (this.footer && this.store) {
5775         this.footer.dataSource = this.ds;
5776         this.footer = Roo.factory(this.footer);
5777     }
5778     
5779     /** @private */
5780     this.addEvents({
5781         /**
5782          * @event cellclick
5783          * Fires when a cell is clicked
5784          * @param {Roo.bootstrap.Table} this
5785          * @param {Roo.Element} el
5786          * @param {Number} rowIndex
5787          * @param {Number} columnIndex
5788          * @param {Roo.EventObject} e
5789          */
5790         "cellclick" : true,
5791         /**
5792          * @event celldblclick
5793          * Fires when a cell is double clicked
5794          * @param {Roo.bootstrap.Table} this
5795          * @param {Roo.Element} el
5796          * @param {Number} rowIndex
5797          * @param {Number} columnIndex
5798          * @param {Roo.EventObject} e
5799          */
5800         "celldblclick" : true,
5801         /**
5802          * @event rowclick
5803          * Fires when a row is clicked
5804          * @param {Roo.bootstrap.Table} this
5805          * @param {Roo.Element} el
5806          * @param {Number} rowIndex
5807          * @param {Roo.EventObject} e
5808          */
5809         "rowclick" : true,
5810         /**
5811          * @event rowdblclick
5812          * Fires when a row is double clicked
5813          * @param {Roo.bootstrap.Table} this
5814          * @param {Roo.Element} el
5815          * @param {Number} rowIndex
5816          * @param {Roo.EventObject} e
5817          */
5818         "rowdblclick" : true,
5819         /**
5820          * @event mouseover
5821          * Fires when a mouseover occur
5822          * @param {Roo.bootstrap.Table} this
5823          * @param {Roo.Element} el
5824          * @param {Number} rowIndex
5825          * @param {Number} columnIndex
5826          * @param {Roo.EventObject} e
5827          */
5828         "mouseover" : true,
5829         /**
5830          * @event mouseout
5831          * Fires when a mouseout occur
5832          * @param {Roo.bootstrap.Table} this
5833          * @param {Roo.Element} el
5834          * @param {Number} rowIndex
5835          * @param {Number} columnIndex
5836          * @param {Roo.EventObject} e
5837          */
5838         "mouseout" : true,
5839         /**
5840          * @event rowclass
5841          * Fires when a row is rendered, so you can change add a style to it.
5842          * @param {Roo.bootstrap.Table} this
5843          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5844          */
5845         'rowclass' : true,
5846           /**
5847          * @event rowsrendered
5848          * Fires when all the  rows have been rendered
5849          * @param {Roo.bootstrap.Table} this
5850          */
5851         'rowsrendered' : true,
5852         /**
5853          * @event contextmenu
5854          * The raw contextmenu event for the entire grid.
5855          * @param {Roo.EventObject} e
5856          */
5857         "contextmenu" : true,
5858         /**
5859          * @event rowcontextmenu
5860          * Fires when a row is right clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Number} rowIndex
5863          * @param {Roo.EventObject} e
5864          */
5865         "rowcontextmenu" : true,
5866         /**
5867          * @event cellcontextmenu
5868          * Fires when a cell is right clicked
5869          * @param {Roo.bootstrap.Table} this
5870          * @param {Number} rowIndex
5871          * @param {Number} cellIndex
5872          * @param {Roo.EventObject} e
5873          */
5874          "cellcontextmenu" : true,
5875          /**
5876          * @event headercontextmenu
5877          * Fires when a header is right clicked
5878          * @param {Roo.bootstrap.Table} this
5879          * @param {Number} columnIndex
5880          * @param {Roo.EventObject} e
5881          */
5882         "headercontextmenu" : true
5883     });
5884 };
5885
5886 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5887     
5888     cls: false,
5889     align: false,
5890     bgcolor: false,
5891     border: false,
5892     cellpadding: false,
5893     cellspacing: false,
5894     frame: false,
5895     rules: false,
5896     sortable: false,
5897     summary: false,
5898     width: false,
5899     striped : false,
5900     scrollBody : false,
5901     bordered: false,
5902     hover:  false,
5903     condensed : false,
5904     responsive : false,
5905     sm : false,
5906     cm : false,
5907     store : false,
5908     loadMask : false,
5909     footerShow : true,
5910     headerShow : true,
5911   
5912     rowSelection : false,
5913     cellSelection : false,
5914     layout : false,
5915     
5916     // Roo.Element - the tbody
5917     mainBody: false,
5918     // Roo.Element - thead element
5919     mainHead: false,
5920     
5921     container: false, // used by gridpanel...
5922     
5923     getAutoCreate : function()
5924     {
5925         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5926         
5927         cfg = {
5928             tag: 'table',
5929             cls : 'table',
5930             cn : []
5931         };
5932         if (this.scrollBody) {
5933             cfg.cls += ' table-body-fixed';
5934         }    
5935         if (this.striped) {
5936             cfg.cls += ' table-striped';
5937         }
5938         
5939         if (this.hover) {
5940             cfg.cls += ' table-hover';
5941         }
5942         if (this.bordered) {
5943             cfg.cls += ' table-bordered';
5944         }
5945         if (this.condensed) {
5946             cfg.cls += ' table-condensed';
5947         }
5948         if (this.responsive) {
5949             cfg.cls += ' table-responsive';
5950         }
5951         
5952         if (this.cls) {
5953             cfg.cls+=  ' ' +this.cls;
5954         }
5955         
5956         // this lot should be simplifed...
5957         
5958         if (this.align) {
5959             cfg.align=this.align;
5960         }
5961         if (this.bgcolor) {
5962             cfg.bgcolor=this.bgcolor;
5963         }
5964         if (this.border) {
5965             cfg.border=this.border;
5966         }
5967         if (this.cellpadding) {
5968             cfg.cellpadding=this.cellpadding;
5969         }
5970         if (this.cellspacing) {
5971             cfg.cellspacing=this.cellspacing;
5972         }
5973         if (this.frame) {
5974             cfg.frame=this.frame;
5975         }
5976         if (this.rules) {
5977             cfg.rules=this.rules;
5978         }
5979         if (this.sortable) {
5980             cfg.sortable=this.sortable;
5981         }
5982         if (this.summary) {
5983             cfg.summary=this.summary;
5984         }
5985         if (this.width) {
5986             cfg.width=this.width;
5987         }
5988         if (this.layout) {
5989             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5990         }
5991         
5992         if(this.store || this.cm){
5993             if(this.headerShow){
5994                 cfg.cn.push(this.renderHeader());
5995             }
5996             
5997             cfg.cn.push(this.renderBody());
5998             
5999             if(this.footerShow){
6000                 cfg.cn.push(this.renderFooter());
6001             }
6002             // where does this come from?
6003             //cfg.cls+=  ' TableGrid';
6004         }
6005         
6006         return { cn : [ cfg ] };
6007     },
6008     
6009     initEvents : function()
6010     {   
6011         if(!this.store || !this.cm){
6012             return;
6013         }
6014         if (this.selModel) {
6015             this.selModel.initEvents();
6016         }
6017         
6018         
6019         //Roo.log('initEvents with ds!!!!');
6020         
6021         this.mainBody = this.el.select('tbody', true).first();
6022         this.mainHead = this.el.select('thead', true).first();
6023         
6024         
6025         
6026         
6027         var _this = this;
6028         
6029         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6030             e.on('click', _this.sort, _this);
6031         });
6032         
6033         this.mainBody.on("click", this.onClick, this);
6034         this.mainBody.on("dblclick", this.onDblClick, this);
6035         
6036         // why is this done????? = it breaks dialogs??
6037         //this.parent().el.setStyle('position', 'relative');
6038         
6039         
6040         if (this.footer) {
6041             this.footer.parentId = this.id;
6042             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6043         } 
6044         
6045         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6046         
6047         this.store.on('load', this.onLoad, this);
6048         this.store.on('beforeload', this.onBeforeLoad, this);
6049         this.store.on('update', this.onUpdate, this);
6050         this.store.on('add', this.onAdd, this);
6051         this.store.on("clear", this.clear, this);
6052         
6053         this.el.on("contextmenu", this.onContextMenu, this);
6054         
6055         this.mainBody.on('scroll', this.onBodyScroll, this);
6056         
6057         
6058     },
6059     
6060     onContextMenu : function(e, t)
6061     {
6062         this.processEvent("contextmenu", e);
6063     },
6064     
6065     processEvent : function(name, e)
6066     {
6067         if (name != 'touchstart' ) {
6068             this.fireEvent(name, e);    
6069         }
6070         
6071         var t = e.getTarget();
6072         
6073         var cell = Roo.get(t);
6074         
6075         if(!cell){
6076             return;
6077         }
6078         
6079         if(cell.findParent('tfoot', false, true)){
6080             return;
6081         }
6082         
6083         if(cell.findParent('thead', false, true)){
6084             
6085             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6086                 cell = Roo.get(t).findParent('th', false, true);
6087                 if (!cell) {
6088                     Roo.log("failed to find th in thead?");
6089                     Roo.log(e.getTarget());
6090                     return;
6091                 }
6092             }
6093             
6094             var cellIndex = cell.dom.cellIndex;
6095             
6096             var ename = name == 'touchstart' ? 'click' : name;
6097             this.fireEvent("header" + ename, this, cellIndex, e);
6098             
6099             return;
6100         }
6101         
6102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6103             cell = Roo.get(t).findParent('td', false, true);
6104             if (!cell) {
6105                 Roo.log("failed to find th in tbody?");
6106                 Roo.log(e.getTarget());
6107                 return;
6108             }
6109         }
6110         
6111         var row = cell.findParent('tr', false, true);
6112         var cellIndex = cell.dom.cellIndex;
6113         var rowIndex = row.dom.rowIndex - 1;
6114         
6115         if(row !== false){
6116             
6117             this.fireEvent("row" + name, this, rowIndex, e);
6118             
6119             if(cell !== false){
6120             
6121                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6122             }
6123         }
6124         
6125     },
6126     
6127     onMouseover : function(e, el)
6128     {
6129         var cell = Roo.get(el);
6130         
6131         if(!cell){
6132             return;
6133         }
6134         
6135         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6136             cell = cell.findParent('td', false, true);
6137         }
6138         
6139         var row = cell.findParent('tr', false, true);
6140         var cellIndex = cell.dom.cellIndex;
6141         var rowIndex = row.dom.rowIndex - 1; // start from 0
6142         
6143         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6144         
6145     },
6146     
6147     onMouseout : function(e, el)
6148     {
6149         var cell = Roo.get(el);
6150         
6151         if(!cell){
6152             return;
6153         }
6154         
6155         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6156             cell = cell.findParent('td', false, true);
6157         }
6158         
6159         var row = cell.findParent('tr', false, true);
6160         var cellIndex = cell.dom.cellIndex;
6161         var rowIndex = row.dom.rowIndex - 1; // start from 0
6162         
6163         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6164         
6165     },
6166     
6167     onClick : function(e, el)
6168     {
6169         var cell = Roo.get(el);
6170         
6171         if(!cell || (!this.cellSelection && !this.rowSelection)){
6172             return;
6173         }
6174         
6175         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6176             cell = cell.findParent('td', false, true);
6177         }
6178         
6179         if(!cell || typeof(cell) == 'undefined'){
6180             return;
6181         }
6182         
6183         var row = cell.findParent('tr', false, true);
6184         
6185         if(!row || typeof(row) == 'undefined'){
6186             return;
6187         }
6188         
6189         var cellIndex = cell.dom.cellIndex;
6190         var rowIndex = this.getRowIndex(row);
6191         
6192         // why??? - should these not be based on SelectionModel?
6193         if(this.cellSelection){
6194             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6195         }
6196         
6197         if(this.rowSelection){
6198             this.fireEvent('rowclick', this, row, rowIndex, e);
6199         }
6200         
6201         
6202     },
6203         
6204     onDblClick : function(e,el)
6205     {
6206         var cell = Roo.get(el);
6207         
6208         if(!cell || (!this.cellSelection && !this.rowSelection)){
6209             return;
6210         }
6211         
6212         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6213             cell = cell.findParent('td', false, true);
6214         }
6215         
6216         if(!cell || typeof(cell) == 'undefined'){
6217             return;
6218         }
6219         
6220         var row = cell.findParent('tr', false, true);
6221         
6222         if(!row || typeof(row) == 'undefined'){
6223             return;
6224         }
6225         
6226         var cellIndex = cell.dom.cellIndex;
6227         var rowIndex = this.getRowIndex(row);
6228         
6229         if(this.cellSelection){
6230             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6231         }
6232         
6233         if(this.rowSelection){
6234             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6235         }
6236     },
6237     
6238     sort : function(e,el)
6239     {
6240         var col = Roo.get(el);
6241         
6242         if(!col.hasClass('sortable')){
6243             return;
6244         }
6245         
6246         var sort = col.attr('sort');
6247         var dir = 'ASC';
6248         
6249         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6250             dir = 'DESC';
6251         }
6252         
6253         this.store.sortInfo = {field : sort, direction : dir};
6254         
6255         if (this.footer) {
6256             Roo.log("calling footer first");
6257             this.footer.onClick('first');
6258         } else {
6259         
6260             this.store.load({ params : { start : 0 } });
6261         }
6262     },
6263     
6264     renderHeader : function()
6265     {
6266         var header = {
6267             tag: 'thead',
6268             cn : []
6269         };
6270         
6271         var cm = this.cm;
6272         this.totalWidth = 0;
6273         
6274         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6275             
6276             var config = cm.config[i];
6277             
6278             var c = {
6279                 tag: 'th',
6280                 style : '',
6281                 html: cm.getColumnHeader(i)
6282             };
6283             
6284             var hh = '';
6285             
6286             if(typeof(config.sortable) != 'undefined' && config.sortable){
6287                 c.cls = 'sortable';
6288                 c.html = '<i class="glyphicon"></i>' + c.html;
6289             }
6290             
6291             if(typeof(config.lgHeader) != 'undefined'){
6292                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6293             }
6294             
6295             if(typeof(config.mdHeader) != 'undefined'){
6296                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6297             }
6298             
6299             if(typeof(config.smHeader) != 'undefined'){
6300                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6301             }
6302             
6303             if(typeof(config.xsHeader) != 'undefined'){
6304                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6305             }
6306             
6307             if(hh.length){
6308                 c.html = hh;
6309             }
6310             
6311             if(typeof(config.tooltip) != 'undefined'){
6312                 c.tooltip = config.tooltip;
6313             }
6314             
6315             if(typeof(config.colspan) != 'undefined'){
6316                 c.colspan = config.colspan;
6317             }
6318             
6319             if(typeof(config.hidden) != 'undefined' && config.hidden){
6320                 c.style += ' display:none;';
6321             }
6322             
6323             if(typeof(config.dataIndex) != 'undefined'){
6324                 c.sort = config.dataIndex;
6325             }
6326             
6327            
6328             
6329             if(typeof(config.align) != 'undefined' && config.align.length){
6330                 c.style += ' text-align:' + config.align + ';';
6331             }
6332             
6333             if(typeof(config.width) != 'undefined'){
6334                 c.style += ' width:' + config.width + 'px;';
6335                 this.totalWidth += config.width;
6336             } else {
6337                 this.totalWidth += 100; // assume minimum of 100 per column?
6338             }
6339             
6340             if(typeof(config.cls) != 'undefined'){
6341                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6342             }
6343             
6344             ['xs','sm','md','lg'].map(function(size){
6345                 
6346                 if(typeof(config[size]) == 'undefined'){
6347                     return;
6348                 }
6349                 
6350                 if (!config[size]) { // 0 = hidden
6351                     c.cls += ' hidden-' + size;
6352                     return;
6353                 }
6354                 
6355                 c.cls += ' col-' + size + '-' + config[size];
6356
6357             });
6358             
6359             header.cn.push(c)
6360         }
6361         
6362         return header;
6363     },
6364     
6365     renderBody : function()
6366     {
6367         var body = {
6368             tag: 'tbody',
6369             cn : [
6370                 {
6371                     tag: 'tr',
6372                     cn : [
6373                         {
6374                             tag : 'td',
6375                             colspan :  this.cm.getColumnCount()
6376                         }
6377                     ]
6378                 }
6379             ]
6380         };
6381         
6382         return body;
6383     },
6384     
6385     renderFooter : function()
6386     {
6387         var footer = {
6388             tag: 'tfoot',
6389             cn : [
6390                 {
6391                     tag: 'tr',
6392                     cn : [
6393                         {
6394                             tag : 'td',
6395                             colspan :  this.cm.getColumnCount()
6396                         }
6397                     ]
6398                 }
6399             ]
6400         };
6401         
6402         return footer;
6403     },
6404     
6405     
6406     
6407     onLoad : function()
6408     {
6409 //        Roo.log('ds onload');
6410         this.clear();
6411         
6412         var _this = this;
6413         var cm = this.cm;
6414         var ds = this.store;
6415         
6416         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6417             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6418             if (_this.store.sortInfo) {
6419                     
6420                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6421                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6422                 }
6423                 
6424                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6425                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6426                 }
6427             }
6428         });
6429         
6430         var tbody =  this.mainBody;
6431               
6432         if(ds.getCount() > 0){
6433             ds.data.each(function(d,rowIndex){
6434                 var row =  this.renderRow(cm, ds, rowIndex);
6435                 
6436                 tbody.createChild(row);
6437                 
6438                 var _this = this;
6439                 
6440                 if(row.cellObjects.length){
6441                     Roo.each(row.cellObjects, function(r){
6442                         _this.renderCellObject(r);
6443                     })
6444                 }
6445                 
6446             }, this);
6447         }
6448         
6449         Roo.each(this.el.select('tbody td', true).elements, function(e){
6450             e.on('mouseover', _this.onMouseover, _this);
6451         });
6452         
6453         Roo.each(this.el.select('tbody td', true).elements, function(e){
6454             e.on('mouseout', _this.onMouseout, _this);
6455         });
6456         this.fireEvent('rowsrendered', this);
6457         //if(this.loadMask){
6458         //    this.maskEl.hide();
6459         //}
6460         
6461         this.autoSize();
6462     },
6463     
6464     
6465     onUpdate : function(ds,record)
6466     {
6467         this.refreshRow(record);
6468         this.autoSize();
6469     },
6470     
6471     onRemove : function(ds, record, index, isUpdate){
6472         if(isUpdate !== true){
6473             this.fireEvent("beforerowremoved", this, index, record);
6474         }
6475         var bt = this.mainBody.dom;
6476         
6477         var rows = this.el.select('tbody > tr', true).elements;
6478         
6479         if(typeof(rows[index]) != 'undefined'){
6480             bt.removeChild(rows[index].dom);
6481         }
6482         
6483 //        if(bt.rows[index]){
6484 //            bt.removeChild(bt.rows[index]);
6485 //        }
6486         
6487         if(isUpdate !== true){
6488             //this.stripeRows(index);
6489             //this.syncRowHeights(index, index);
6490             //this.layout();
6491             this.fireEvent("rowremoved", this, index, record);
6492         }
6493     },
6494     
6495     onAdd : function(ds, records, rowIndex)
6496     {
6497         //Roo.log('on Add called');
6498         // - note this does not handle multiple adding very well..
6499         var bt = this.mainBody.dom;
6500         for (var i =0 ; i < records.length;i++) {
6501             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6502             //Roo.log(records[i]);
6503             //Roo.log(this.store.getAt(rowIndex+i));
6504             this.insertRow(this.store, rowIndex + i, false);
6505             return;
6506         }
6507         
6508     },
6509     
6510     
6511     refreshRow : function(record){
6512         var ds = this.store, index;
6513         if(typeof record == 'number'){
6514             index = record;
6515             record = ds.getAt(index);
6516         }else{
6517             index = ds.indexOf(record);
6518         }
6519         this.insertRow(ds, index, true);
6520         this.autoSize();
6521         this.onRemove(ds, record, index+1, true);
6522         this.autoSize();
6523         //this.syncRowHeights(index, index);
6524         //this.layout();
6525         this.fireEvent("rowupdated", this, index, record);
6526     },
6527     
6528     insertRow : function(dm, rowIndex, isUpdate){
6529         
6530         if(!isUpdate){
6531             this.fireEvent("beforerowsinserted", this, rowIndex);
6532         }
6533             //var s = this.getScrollState();
6534         var row = this.renderRow(this.cm, this.store, rowIndex);
6535         // insert before rowIndex..
6536         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6537         
6538         var _this = this;
6539                 
6540         if(row.cellObjects.length){
6541             Roo.each(row.cellObjects, function(r){
6542                 _this.renderCellObject(r);
6543             })
6544         }
6545             
6546         if(!isUpdate){
6547             this.fireEvent("rowsinserted", this, rowIndex);
6548             //this.syncRowHeights(firstRow, lastRow);
6549             //this.stripeRows(firstRow);
6550             //this.layout();
6551         }
6552         
6553     },
6554     
6555     
6556     getRowDom : function(rowIndex)
6557     {
6558         var rows = this.el.select('tbody > tr', true).elements;
6559         
6560         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6561         
6562     },
6563     // returns the object tree for a tr..
6564   
6565     
6566     renderRow : function(cm, ds, rowIndex) 
6567     {
6568         
6569         var d = ds.getAt(rowIndex);
6570         
6571         var row = {
6572             tag : 'tr',
6573             cn : []
6574         };
6575             
6576         var cellObjects = [];
6577         
6578         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6579             var config = cm.config[i];
6580             
6581             var renderer = cm.getRenderer(i);
6582             var value = '';
6583             var id = false;
6584             
6585             if(typeof(renderer) !== 'undefined'){
6586                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6587             }
6588             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6589             // and are rendered into the cells after the row is rendered - using the id for the element.
6590             
6591             if(typeof(value) === 'object'){
6592                 id = Roo.id();
6593                 cellObjects.push({
6594                     container : id,
6595                     cfg : value 
6596                 })
6597             }
6598             
6599             var rowcfg = {
6600                 record: d,
6601                 rowIndex : rowIndex,
6602                 colIndex : i,
6603                 rowClass : ''
6604             };
6605
6606             this.fireEvent('rowclass', this, rowcfg);
6607             
6608             var td = {
6609                 tag: 'td',
6610                 cls : rowcfg.rowClass,
6611                 style: '',
6612                 html: (typeof(value) === 'object') ? '' : value
6613             };
6614             
6615             if (id) {
6616                 td.id = id;
6617             }
6618             
6619             if(typeof(config.colspan) != 'undefined'){
6620                 td.colspan = config.colspan;
6621             }
6622             
6623             if(typeof(config.hidden) != 'undefined' && config.hidden){
6624                 td.style += ' display:none;';
6625             }
6626             
6627             if(typeof(config.align) != 'undefined' && config.align.length){
6628                 td.style += ' text-align:' + config.align + ';';
6629             }
6630             
6631             if(typeof(config.width) != 'undefined'){
6632                 td.style += ' width:' +  config.width + 'px;';
6633             }
6634             
6635             if(typeof(config.cursor) != 'undefined'){
6636                 td.style += ' cursor:' +  config.cursor + ';';
6637             }
6638             
6639             if(typeof(config.cls) != 'undefined'){
6640                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6641             }
6642             
6643             ['xs','sm','md','lg'].map(function(size){
6644                 
6645                 if(typeof(config[size]) == 'undefined'){
6646                     return;
6647                 }
6648                 
6649                 if (!config[size]) { // 0 = hidden
6650                     td.cls += ' hidden-' + size;
6651                     return;
6652                 }
6653                 
6654                 td.cls += ' col-' + size + '-' + config[size];
6655
6656             });
6657              
6658             row.cn.push(td);
6659            
6660         }
6661         
6662         row.cellObjects = cellObjects;
6663         
6664         return row;
6665           
6666     },
6667     
6668     
6669     
6670     onBeforeLoad : function()
6671     {
6672         //Roo.log('ds onBeforeLoad');
6673         
6674         //this.clear();
6675         
6676         //if(this.loadMask){
6677         //    this.maskEl.show();
6678         //}
6679     },
6680      /**
6681      * Remove all rows
6682      */
6683     clear : function()
6684     {
6685         this.el.select('tbody', true).first().dom.innerHTML = '';
6686     },
6687     /**
6688      * Show or hide a row.
6689      * @param {Number} rowIndex to show or hide
6690      * @param {Boolean} state hide
6691      */
6692     setRowVisibility : function(rowIndex, state)
6693     {
6694         var bt = this.mainBody.dom;
6695         
6696         var rows = this.el.select('tbody > tr', true).elements;
6697         
6698         if(typeof(rows[rowIndex]) == 'undefined'){
6699             return;
6700         }
6701         rows[rowIndex].dom.style.display = state ? '' : 'none';
6702     },
6703     
6704     
6705     getSelectionModel : function(){
6706         if(!this.selModel){
6707             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6708         }
6709         return this.selModel;
6710     },
6711     /*
6712      * Render the Roo.bootstrap object from renderder
6713      */
6714     renderCellObject : function(r)
6715     {
6716         var _this = this;
6717         
6718         var t = r.cfg.render(r.container);
6719         
6720         if(r.cfg.cn){
6721             Roo.each(r.cfg.cn, function(c){
6722                 var child = {
6723                     container: t.getChildContainer(),
6724                     cfg: c
6725                 };
6726                 _this.renderCellObject(child);
6727             })
6728         }
6729     },
6730     
6731     getRowIndex : function(row)
6732     {
6733         var rowIndex = -1;
6734         
6735         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6736             if(el != row){
6737                 return;
6738             }
6739             
6740             rowIndex = index;
6741         });
6742         
6743         return rowIndex;
6744     },
6745      /**
6746      * Returns the grid's underlying element = used by panel.Grid
6747      * @return {Element} The element
6748      */
6749     getGridEl : function(){
6750         return this.el;
6751     },
6752      /**
6753      * Forces a resize - used by panel.Grid
6754      * @return {Element} The element
6755      */
6756     autoSize : function()
6757     {
6758         //var ctr = Roo.get(this.container.dom.parentElement);
6759         var ctr = Roo.get(this.el.dom);
6760         
6761         var thd = this.getGridEl().select('thead',true).first();
6762         var tbd = this.getGridEl().select('tbody', true).first();
6763         var tfd = this.getGridEl().select('tfoot', true).first();
6764         
6765         var cw = ctr.getWidth();
6766         
6767         if (tbd) {
6768             
6769             tbd.setSize(ctr.getWidth(),
6770                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6771             );
6772             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6773             cw -= barsize;
6774         }
6775         cw = Math.max(cw, this.totalWidth);
6776         this.getGridEl().select('tr',true).setWidth(cw);
6777         // resize 'expandable coloumn?
6778         
6779         return; // we doe not have a view in this design..
6780         
6781     },
6782     onBodyScroll: function()
6783     {
6784         
6785         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6786         this.mainHead.setStyle({
6787                     'position' : 'relative',
6788                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6789         });
6790         
6791         
6792     }
6793 });
6794
6795  
6796
6797  /*
6798  * - LGPL
6799  *
6800  * table cell
6801  * 
6802  */
6803
6804 /**
6805  * @class Roo.bootstrap.TableCell
6806  * @extends Roo.bootstrap.Component
6807  * Bootstrap TableCell class
6808  * @cfg {String} html cell contain text
6809  * @cfg {String} cls cell class
6810  * @cfg {String} tag cell tag (td|th) default td
6811  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6812  * @cfg {String} align Aligns the content in a cell
6813  * @cfg {String} axis Categorizes cells
6814  * @cfg {String} bgcolor Specifies the background color of a cell
6815  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6816  * @cfg {Number} colspan Specifies the number of columns a cell should span
6817  * @cfg {String} headers Specifies one or more header cells a cell is related to
6818  * @cfg {Number} height Sets the height of a cell
6819  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6820  * @cfg {Number} rowspan Sets the number of rows a cell should span
6821  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6822  * @cfg {String} valign Vertical aligns the content in a cell
6823  * @cfg {Number} width Specifies the width of a cell
6824  * 
6825  * @constructor
6826  * Create a new TableCell
6827  * @param {Object} config The config object
6828  */
6829
6830 Roo.bootstrap.TableCell = function(config){
6831     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6832 };
6833
6834 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6835     
6836     html: false,
6837     cls: false,
6838     tag: false,
6839     abbr: false,
6840     align: false,
6841     axis: false,
6842     bgcolor: false,
6843     charoff: false,
6844     colspan: false,
6845     headers: false,
6846     height: false,
6847     nowrap: false,
6848     rowspan: false,
6849     scope: false,
6850     valign: false,
6851     width: false,
6852     
6853     
6854     getAutoCreate : function(){
6855         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6856         
6857         cfg = {
6858             tag: 'td'
6859         };
6860         
6861         if(this.tag){
6862             cfg.tag = this.tag;
6863         }
6864         
6865         if (this.html) {
6866             cfg.html=this.html
6867         }
6868         if (this.cls) {
6869             cfg.cls=this.cls
6870         }
6871         if (this.abbr) {
6872             cfg.abbr=this.abbr
6873         }
6874         if (this.align) {
6875             cfg.align=this.align
6876         }
6877         if (this.axis) {
6878             cfg.axis=this.axis
6879         }
6880         if (this.bgcolor) {
6881             cfg.bgcolor=this.bgcolor
6882         }
6883         if (this.charoff) {
6884             cfg.charoff=this.charoff
6885         }
6886         if (this.colspan) {
6887             cfg.colspan=this.colspan
6888         }
6889         if (this.headers) {
6890             cfg.headers=this.headers
6891         }
6892         if (this.height) {
6893             cfg.height=this.height
6894         }
6895         if (this.nowrap) {
6896             cfg.nowrap=this.nowrap
6897         }
6898         if (this.rowspan) {
6899             cfg.rowspan=this.rowspan
6900         }
6901         if (this.scope) {
6902             cfg.scope=this.scope
6903         }
6904         if (this.valign) {
6905             cfg.valign=this.valign
6906         }
6907         if (this.width) {
6908             cfg.width=this.width
6909         }
6910         
6911         
6912         return cfg;
6913     }
6914    
6915 });
6916
6917  
6918
6919  /*
6920  * - LGPL
6921  *
6922  * table row
6923  * 
6924  */
6925
6926 /**
6927  * @class Roo.bootstrap.TableRow
6928  * @extends Roo.bootstrap.Component
6929  * Bootstrap TableRow class
6930  * @cfg {String} cls row class
6931  * @cfg {String} align Aligns the content in a table row
6932  * @cfg {String} bgcolor Specifies a background color for a table row
6933  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6934  * @cfg {String} valign Vertical aligns the content in a table row
6935  * 
6936  * @constructor
6937  * Create a new TableRow
6938  * @param {Object} config The config object
6939  */
6940
6941 Roo.bootstrap.TableRow = function(config){
6942     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6943 };
6944
6945 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6946     
6947     cls: false,
6948     align: false,
6949     bgcolor: false,
6950     charoff: false,
6951     valign: false,
6952     
6953     getAutoCreate : function(){
6954         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6955         
6956         cfg = {
6957             tag: 'tr'
6958         };
6959             
6960         if(this.cls){
6961             cfg.cls = this.cls;
6962         }
6963         if(this.align){
6964             cfg.align = this.align;
6965         }
6966         if(this.bgcolor){
6967             cfg.bgcolor = this.bgcolor;
6968         }
6969         if(this.charoff){
6970             cfg.charoff = this.charoff;
6971         }
6972         if(this.valign){
6973             cfg.valign = this.valign;
6974         }
6975         
6976         return cfg;
6977     }
6978    
6979 });
6980
6981  
6982
6983  /*
6984  * - LGPL
6985  *
6986  * table body
6987  * 
6988  */
6989
6990 /**
6991  * @class Roo.bootstrap.TableBody
6992  * @extends Roo.bootstrap.Component
6993  * Bootstrap TableBody class
6994  * @cfg {String} cls element class
6995  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6996  * @cfg {String} align Aligns the content inside the element
6997  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6998  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6999  * 
7000  * @constructor
7001  * Create a new TableBody
7002  * @param {Object} config The config object
7003  */
7004
7005 Roo.bootstrap.TableBody = function(config){
7006     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7007 };
7008
7009 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7010     
7011     cls: false,
7012     tag: false,
7013     align: false,
7014     charoff: false,
7015     valign: false,
7016     
7017     getAutoCreate : function(){
7018         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7019         
7020         cfg = {
7021             tag: 'tbody'
7022         };
7023             
7024         if (this.cls) {
7025             cfg.cls=this.cls
7026         }
7027         if(this.tag){
7028             cfg.tag = this.tag;
7029         }
7030         
7031         if(this.align){
7032             cfg.align = this.align;
7033         }
7034         if(this.charoff){
7035             cfg.charoff = this.charoff;
7036         }
7037         if(this.valign){
7038             cfg.valign = this.valign;
7039         }
7040         
7041         return cfg;
7042     }
7043     
7044     
7045 //    initEvents : function()
7046 //    {
7047 //        
7048 //        if(!this.store){
7049 //            return;
7050 //        }
7051 //        
7052 //        this.store = Roo.factory(this.store, Roo.data);
7053 //        this.store.on('load', this.onLoad, this);
7054 //        
7055 //        this.store.load();
7056 //        
7057 //    },
7058 //    
7059 //    onLoad: function () 
7060 //    {   
7061 //        this.fireEvent('load', this);
7062 //    }
7063 //    
7064 //   
7065 });
7066
7067  
7068
7069  /*
7070  * Based on:
7071  * Ext JS Library 1.1.1
7072  * Copyright(c) 2006-2007, Ext JS, LLC.
7073  *
7074  * Originally Released Under LGPL - original licence link has changed is not relivant.
7075  *
7076  * Fork - LGPL
7077  * <script type="text/javascript">
7078  */
7079
7080 // as we use this in bootstrap.
7081 Roo.namespace('Roo.form');
7082  /**
7083  * @class Roo.form.Action
7084  * Internal Class used to handle form actions
7085  * @constructor
7086  * @param {Roo.form.BasicForm} el The form element or its id
7087  * @param {Object} config Configuration options
7088  */
7089
7090  
7091  
7092 // define the action interface
7093 Roo.form.Action = function(form, options){
7094     this.form = form;
7095     this.options = options || {};
7096 };
7097 /**
7098  * Client Validation Failed
7099  * @const 
7100  */
7101 Roo.form.Action.CLIENT_INVALID = 'client';
7102 /**
7103  * Server Validation Failed
7104  * @const 
7105  */
7106 Roo.form.Action.SERVER_INVALID = 'server';
7107  /**
7108  * Connect to Server Failed
7109  * @const 
7110  */
7111 Roo.form.Action.CONNECT_FAILURE = 'connect';
7112 /**
7113  * Reading Data from Server Failed
7114  * @const 
7115  */
7116 Roo.form.Action.LOAD_FAILURE = 'load';
7117
7118 Roo.form.Action.prototype = {
7119     type : 'default',
7120     failureType : undefined,
7121     response : undefined,
7122     result : undefined,
7123
7124     // interface method
7125     run : function(options){
7126
7127     },
7128
7129     // interface method
7130     success : function(response){
7131
7132     },
7133
7134     // interface method
7135     handleResponse : function(response){
7136
7137     },
7138
7139     // default connection failure
7140     failure : function(response){
7141         
7142         this.response = response;
7143         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7144         this.form.afterAction(this, false);
7145     },
7146
7147     processResponse : function(response){
7148         this.response = response;
7149         if(!response.responseText){
7150             return true;
7151         }
7152         this.result = this.handleResponse(response);
7153         return this.result;
7154     },
7155
7156     // utility functions used internally
7157     getUrl : function(appendParams){
7158         var url = this.options.url || this.form.url || this.form.el.dom.action;
7159         if(appendParams){
7160             var p = this.getParams();
7161             if(p){
7162                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7163             }
7164         }
7165         return url;
7166     },
7167
7168     getMethod : function(){
7169         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7170     },
7171
7172     getParams : function(){
7173         var bp = this.form.baseParams;
7174         var p = this.options.params;
7175         if(p){
7176             if(typeof p == "object"){
7177                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7178             }else if(typeof p == 'string' && bp){
7179                 p += '&' + Roo.urlEncode(bp);
7180             }
7181         }else if(bp){
7182             p = Roo.urlEncode(bp);
7183         }
7184         return p;
7185     },
7186
7187     createCallback : function(){
7188         return {
7189             success: this.success,
7190             failure: this.failure,
7191             scope: this,
7192             timeout: (this.form.timeout*1000),
7193             upload: this.form.fileUpload ? this.success : undefined
7194         };
7195     }
7196 };
7197
7198 Roo.form.Action.Submit = function(form, options){
7199     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7200 };
7201
7202 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7203     type : 'submit',
7204
7205     haveProgress : false,
7206     uploadComplete : false,
7207     
7208     // uploadProgress indicator.
7209     uploadProgress : function()
7210     {
7211         if (!this.form.progressUrl) {
7212             return;
7213         }
7214         
7215         if (!this.haveProgress) {
7216             Roo.MessageBox.progress("Uploading", "Uploading");
7217         }
7218         if (this.uploadComplete) {
7219            Roo.MessageBox.hide();
7220            return;
7221         }
7222         
7223         this.haveProgress = true;
7224    
7225         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7226         
7227         var c = new Roo.data.Connection();
7228         c.request({
7229             url : this.form.progressUrl,
7230             params: {
7231                 id : uid
7232             },
7233             method: 'GET',
7234             success : function(req){
7235                //console.log(data);
7236                 var rdata = false;
7237                 var edata;
7238                 try  {
7239                    rdata = Roo.decode(req.responseText)
7240                 } catch (e) {
7241                     Roo.log("Invalid data from server..");
7242                     Roo.log(edata);
7243                     return;
7244                 }
7245                 if (!rdata || !rdata.success) {
7246                     Roo.log(rdata);
7247                     Roo.MessageBox.alert(Roo.encode(rdata));
7248                     return;
7249                 }
7250                 var data = rdata.data;
7251                 
7252                 if (this.uploadComplete) {
7253                    Roo.MessageBox.hide();
7254                    return;
7255                 }
7256                    
7257                 if (data){
7258                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7259                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7260                     );
7261                 }
7262                 this.uploadProgress.defer(2000,this);
7263             },
7264        
7265             failure: function(data) {
7266                 Roo.log('progress url failed ');
7267                 Roo.log(data);
7268             },
7269             scope : this
7270         });
7271            
7272     },
7273     
7274     
7275     run : function()
7276     {
7277         // run get Values on the form, so it syncs any secondary forms.
7278         this.form.getValues();
7279         
7280         var o = this.options;
7281         var method = this.getMethod();
7282         var isPost = method == 'POST';
7283         if(o.clientValidation === false || this.form.isValid()){
7284             
7285             if (this.form.progressUrl) {
7286                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7287                     (new Date() * 1) + '' + Math.random());
7288                     
7289             } 
7290             
7291             
7292             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7293                 form:this.form.el.dom,
7294                 url:this.getUrl(!isPost),
7295                 method: method,
7296                 params:isPost ? this.getParams() : null,
7297                 isUpload: this.form.fileUpload
7298             }));
7299             
7300             this.uploadProgress();
7301
7302         }else if (o.clientValidation !== false){ // client validation failed
7303             this.failureType = Roo.form.Action.CLIENT_INVALID;
7304             this.form.afterAction(this, false);
7305         }
7306     },
7307
7308     success : function(response)
7309     {
7310         this.uploadComplete= true;
7311         if (this.haveProgress) {
7312             Roo.MessageBox.hide();
7313         }
7314         
7315         
7316         var result = this.processResponse(response);
7317         if(result === true || result.success){
7318             this.form.afterAction(this, true);
7319             return;
7320         }
7321         if(result.errors){
7322             this.form.markInvalid(result.errors);
7323             this.failureType = Roo.form.Action.SERVER_INVALID;
7324         }
7325         this.form.afterAction(this, false);
7326     },
7327     failure : function(response)
7328     {
7329         this.uploadComplete= true;
7330         if (this.haveProgress) {
7331             Roo.MessageBox.hide();
7332         }
7333         
7334         this.response = response;
7335         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7336         this.form.afterAction(this, false);
7337     },
7338     
7339     handleResponse : function(response){
7340         if(this.form.errorReader){
7341             var rs = this.form.errorReader.read(response);
7342             var errors = [];
7343             if(rs.records){
7344                 for(var i = 0, len = rs.records.length; i < len; i++) {
7345                     var r = rs.records[i];
7346                     errors[i] = r.data;
7347                 }
7348             }
7349             if(errors.length < 1){
7350                 errors = null;
7351             }
7352             return {
7353                 success : rs.success,
7354                 errors : errors
7355             };
7356         }
7357         var ret = false;
7358         try {
7359             ret = Roo.decode(response.responseText);
7360         } catch (e) {
7361             ret = {
7362                 success: false,
7363                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7364                 errors : []
7365             };
7366         }
7367         return ret;
7368         
7369     }
7370 });
7371
7372
7373 Roo.form.Action.Load = function(form, options){
7374     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7375     this.reader = this.form.reader;
7376 };
7377
7378 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7379     type : 'load',
7380
7381     run : function(){
7382         
7383         Roo.Ajax.request(Roo.apply(
7384                 this.createCallback(), {
7385                     method:this.getMethod(),
7386                     url:this.getUrl(false),
7387                     params:this.getParams()
7388         }));
7389     },
7390
7391     success : function(response){
7392         
7393         var result = this.processResponse(response);
7394         if(result === true || !result.success || !result.data){
7395             this.failureType = Roo.form.Action.LOAD_FAILURE;
7396             this.form.afterAction(this, false);
7397             return;
7398         }
7399         this.form.clearInvalid();
7400         this.form.setValues(result.data);
7401         this.form.afterAction(this, true);
7402     },
7403
7404     handleResponse : function(response){
7405         if(this.form.reader){
7406             var rs = this.form.reader.read(response);
7407             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7408             return {
7409                 success : rs.success,
7410                 data : data
7411             };
7412         }
7413         return Roo.decode(response.responseText);
7414     }
7415 });
7416
7417 Roo.form.Action.ACTION_TYPES = {
7418     'load' : Roo.form.Action.Load,
7419     'submit' : Roo.form.Action.Submit
7420 };/*
7421  * - LGPL
7422  *
7423  * form
7424  *
7425  */
7426
7427 /**
7428  * @class Roo.bootstrap.Form
7429  * @extends Roo.bootstrap.Component
7430  * Bootstrap Form class
7431  * @cfg {String} method  GET | POST (default POST)
7432  * @cfg {String} labelAlign top | left (default top)
7433  * @cfg {String} align left  | right - for navbars
7434  * @cfg {Boolean} loadMask load mask when submit (default true)
7435
7436  *
7437  * @constructor
7438  * Create a new Form
7439  * @param {Object} config The config object
7440  */
7441
7442
7443 Roo.bootstrap.Form = function(config){
7444     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7445     
7446     Roo.bootstrap.Form.popover.apply();
7447     
7448     this.addEvents({
7449         /**
7450          * @event clientvalidation
7451          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7452          * @param {Form} this
7453          * @param {Boolean} valid true if the form has passed client-side validation
7454          */
7455         clientvalidation: true,
7456         /**
7457          * @event beforeaction
7458          * Fires before any action is performed. Return false to cancel the action.
7459          * @param {Form} this
7460          * @param {Action} action The action to be performed
7461          */
7462         beforeaction: true,
7463         /**
7464          * @event actionfailed
7465          * Fires when an action fails.
7466          * @param {Form} this
7467          * @param {Action} action The action that failed
7468          */
7469         actionfailed : true,
7470         /**
7471          * @event actioncomplete
7472          * Fires when an action is completed.
7473          * @param {Form} this
7474          * @param {Action} action The action that completed
7475          */
7476         actioncomplete : true
7477     });
7478
7479 };
7480
7481 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7482
7483      /**
7484      * @cfg {String} method
7485      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7486      */
7487     method : 'POST',
7488     /**
7489      * @cfg {String} url
7490      * The URL to use for form actions if one isn't supplied in the action options.
7491      */
7492     /**
7493      * @cfg {Boolean} fileUpload
7494      * Set to true if this form is a file upload.
7495      */
7496
7497     /**
7498      * @cfg {Object} baseParams
7499      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7500      */
7501
7502     /**
7503      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7504      */
7505     timeout: 30,
7506     /**
7507      * @cfg {Sting} align (left|right) for navbar forms
7508      */
7509     align : 'left',
7510
7511     // private
7512     activeAction : null,
7513
7514     /**
7515      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7516      * element by passing it or its id or mask the form itself by passing in true.
7517      * @type Mixed
7518      */
7519     waitMsgTarget : false,
7520
7521     loadMask : true,
7522     
7523     /**
7524      * @cfg {Boolean} errorMask (true|false) default false
7525      */
7526     errorMask : false,
7527
7528     getAutoCreate : function(){
7529
7530         var cfg = {
7531             tag: 'form',
7532             method : this.method || 'POST',
7533             id : this.id || Roo.id(),
7534             cls : ''
7535         };
7536         if (this.parent().xtype.match(/^Nav/)) {
7537             cfg.cls = 'navbar-form navbar-' + this.align;
7538
7539         }
7540
7541         if (this.labelAlign == 'left' ) {
7542             cfg.cls += ' form-horizontal';
7543         }
7544
7545
7546         return cfg;
7547     },
7548     initEvents : function()
7549     {
7550         this.el.on('submit', this.onSubmit, this);
7551         // this was added as random key presses on the form where triggering form submit.
7552         this.el.on('keypress', function(e) {
7553             if (e.getCharCode() != 13) {
7554                 return true;
7555             }
7556             // we might need to allow it for textareas.. and some other items.
7557             // check e.getTarget().
7558
7559             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7560                 return true;
7561             }
7562
7563             Roo.log("keypress blocked");
7564
7565             e.preventDefault();
7566             return false;
7567         });
7568         
7569     },
7570     // private
7571     onSubmit : function(e){
7572         e.stopEvent();
7573     },
7574
7575      /**
7576      * Returns true if client-side validation on the form is successful.
7577      * @return Boolean
7578      */
7579     isValid : function(){
7580         var items = this.getItems();
7581         var valid = true;
7582         var target = false;
7583         
7584         items.each(function(f){
7585             
7586             if(f.validate()){
7587                 return;
7588             }
7589             valid = false;
7590
7591             if(!target && f.el.isVisible(true)){
7592                 target = f;
7593             }
7594            
7595         });
7596         
7597         if(this.errorMask && !valid){
7598             Roo.bootstrap.Form.popover.mask(this, target);
7599         }
7600         
7601         return valid;
7602     },
7603     
7604     /**
7605      * Returns true if any fields in this form have changed since their original load.
7606      * @return Boolean
7607      */
7608     isDirty : function(){
7609         var dirty = false;
7610         var items = this.getItems();
7611         items.each(function(f){
7612            if(f.isDirty()){
7613                dirty = true;
7614                return false;
7615            }
7616            return true;
7617         });
7618         return dirty;
7619     },
7620      /**
7621      * Performs a predefined action (submit or load) or custom actions you define on this form.
7622      * @param {String} actionName The name of the action type
7623      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7624      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7625      * accept other config options):
7626      * <pre>
7627 Property          Type             Description
7628 ----------------  ---------------  ----------------------------------------------------------------------------------
7629 url               String           The url for the action (defaults to the form's url)
7630 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7631 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7632 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7633                                    validate the form on the client (defaults to false)
7634      * </pre>
7635      * @return {BasicForm} this
7636      */
7637     doAction : function(action, options){
7638         if(typeof action == 'string'){
7639             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7640         }
7641         if(this.fireEvent('beforeaction', this, action) !== false){
7642             this.beforeAction(action);
7643             action.run.defer(100, action);
7644         }
7645         return this;
7646     },
7647
7648     // private
7649     beforeAction : function(action){
7650         var o = action.options;
7651
7652         if(this.loadMask){
7653             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7654         }
7655         // not really supported yet.. ??
7656
7657         //if(this.waitMsgTarget === true){
7658         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7659         //}else if(this.waitMsgTarget){
7660         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7661         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7662         //}else {
7663         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7664        // }
7665
7666     },
7667
7668     // private
7669     afterAction : function(action, success){
7670         this.activeAction = null;
7671         var o = action.options;
7672
7673         //if(this.waitMsgTarget === true){
7674             this.el.unmask();
7675         //}else if(this.waitMsgTarget){
7676         //    this.waitMsgTarget.unmask();
7677         //}else{
7678         //    Roo.MessageBox.updateProgress(1);
7679         //    Roo.MessageBox.hide();
7680        // }
7681         //
7682         if(success){
7683             if(o.reset){
7684                 this.reset();
7685             }
7686             Roo.callback(o.success, o.scope, [this, action]);
7687             this.fireEvent('actioncomplete', this, action);
7688
7689         }else{
7690
7691             // failure condition..
7692             // we have a scenario where updates need confirming.
7693             // eg. if a locking scenario exists..
7694             // we look for { errors : { needs_confirm : true }} in the response.
7695             if (
7696                 (typeof(action.result) != 'undefined')  &&
7697                 (typeof(action.result.errors) != 'undefined')  &&
7698                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7699            ){
7700                 var _t = this;
7701                 Roo.log("not supported yet");
7702                  /*
7703
7704                 Roo.MessageBox.confirm(
7705                     "Change requires confirmation",
7706                     action.result.errorMsg,
7707                     function(r) {
7708                         if (r != 'yes') {
7709                             return;
7710                         }
7711                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7712                     }
7713
7714                 );
7715                 */
7716
7717
7718                 return;
7719             }
7720
7721             Roo.callback(o.failure, o.scope, [this, action]);
7722             // show an error message if no failed handler is set..
7723             if (!this.hasListener('actionfailed')) {
7724                 Roo.log("need to add dialog support");
7725                 /*
7726                 Roo.MessageBox.alert("Error",
7727                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7728                         action.result.errorMsg :
7729                         "Saving Failed, please check your entries or try again"
7730                 );
7731                 */
7732             }
7733
7734             this.fireEvent('actionfailed', this, action);
7735         }
7736
7737     },
7738     /**
7739      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7740      * @param {String} id The value to search for
7741      * @return Field
7742      */
7743     findField : function(id){
7744         var items = this.getItems();
7745         var field = items.get(id);
7746         if(!field){
7747              items.each(function(f){
7748                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7749                     field = f;
7750                     return false;
7751                 }
7752                 return true;
7753             });
7754         }
7755         return field || null;
7756     },
7757      /**
7758      * Mark fields in this form invalid in bulk.
7759      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7760      * @return {BasicForm} this
7761      */
7762     markInvalid : function(errors){
7763         if(errors instanceof Array){
7764             for(var i = 0, len = errors.length; i < len; i++){
7765                 var fieldError = errors[i];
7766                 var f = this.findField(fieldError.id);
7767                 if(f){
7768                     f.markInvalid(fieldError.msg);
7769                 }
7770             }
7771         }else{
7772             var field, id;
7773             for(id in errors){
7774                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7775                     field.markInvalid(errors[id]);
7776                 }
7777             }
7778         }
7779         //Roo.each(this.childForms || [], function (f) {
7780         //    f.markInvalid(errors);
7781         //});
7782
7783         return this;
7784     },
7785
7786     /**
7787      * Set values for fields in this form in bulk.
7788      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7789      * @return {BasicForm} this
7790      */
7791     setValues : function(values){
7792         if(values instanceof Array){ // array of objects
7793             for(var i = 0, len = values.length; i < len; i++){
7794                 var v = values[i];
7795                 var f = this.findField(v.id);
7796                 if(f){
7797                     f.setValue(v.value);
7798                     if(this.trackResetOnLoad){
7799                         f.originalValue = f.getValue();
7800                     }
7801                 }
7802             }
7803         }else{ // object hash
7804             var field, id;
7805             for(id in values){
7806                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7807
7808                     if (field.setFromData &&
7809                         field.valueField &&
7810                         field.displayField &&
7811                         // combos' with local stores can
7812                         // be queried via setValue()
7813                         // to set their value..
7814                         (field.store && !field.store.isLocal)
7815                         ) {
7816                         // it's a combo
7817                         var sd = { };
7818                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7819                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7820                         field.setFromData(sd);
7821
7822                     } else {
7823                         field.setValue(values[id]);
7824                     }
7825
7826
7827                     if(this.trackResetOnLoad){
7828                         field.originalValue = field.getValue();
7829                     }
7830                 }
7831             }
7832         }
7833
7834         //Roo.each(this.childForms || [], function (f) {
7835         //    f.setValues(values);
7836         //});
7837
7838         return this;
7839     },
7840
7841     /**
7842      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7843      * they are returned as an array.
7844      * @param {Boolean} asString
7845      * @return {Object}
7846      */
7847     getValues : function(asString){
7848         //if (this.childForms) {
7849             // copy values from the child forms
7850         //    Roo.each(this.childForms, function (f) {
7851         //        this.setValues(f.getValues());
7852         //    }, this);
7853         //}
7854
7855
7856
7857         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7858         if(asString === true){
7859             return fs;
7860         }
7861         return Roo.urlDecode(fs);
7862     },
7863
7864     /**
7865      * Returns the fields in this form as an object with key/value pairs.
7866      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7867      * @return {Object}
7868      */
7869     getFieldValues : function(with_hidden)
7870     {
7871         var items = this.getItems();
7872         var ret = {};
7873         items.each(function(f){
7874             if (!f.getName()) {
7875                 return;
7876             }
7877             var v = f.getValue();
7878             if (f.inputType =='radio') {
7879                 if (typeof(ret[f.getName()]) == 'undefined') {
7880                     ret[f.getName()] = ''; // empty..
7881                 }
7882
7883                 if (!f.el.dom.checked) {
7884                     return;
7885
7886                 }
7887                 v = f.el.dom.value;
7888
7889             }
7890
7891             // not sure if this supported any more..
7892             if ((typeof(v) == 'object') && f.getRawValue) {
7893                 v = f.getRawValue() ; // dates..
7894             }
7895             // combo boxes where name != hiddenName...
7896             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7897                 ret[f.name] = f.getRawValue();
7898             }
7899             ret[f.getName()] = v;
7900         });
7901
7902         return ret;
7903     },
7904
7905     /**
7906      * Clears all invalid messages in this form.
7907      * @return {BasicForm} this
7908      */
7909     clearInvalid : function(){
7910         var items = this.getItems();
7911
7912         items.each(function(f){
7913            f.clearInvalid();
7914         });
7915
7916
7917
7918         return this;
7919     },
7920
7921     /**
7922      * Resets this form.
7923      * @return {BasicForm} this
7924      */
7925     reset : function(){
7926         var items = this.getItems();
7927         items.each(function(f){
7928             f.reset();
7929         });
7930
7931         Roo.each(this.childForms || [], function (f) {
7932             f.reset();
7933         });
7934
7935
7936         return this;
7937     },
7938     getItems : function()
7939     {
7940         var r=new Roo.util.MixedCollection(false, function(o){
7941             return o.id || (o.id = Roo.id());
7942         });
7943         var iter = function(el) {
7944             if (el.inputEl) {
7945                 r.add(el);
7946             }
7947             if (!el.items) {
7948                 return;
7949             }
7950             Roo.each(el.items,function(e) {
7951                 iter(e);
7952             });
7953
7954
7955         };
7956
7957         iter(this);
7958         return r;
7959
7960
7961
7962
7963     }
7964
7965 });
7966
7967 Roo.apply(Roo.bootstrap.Form, {
7968     
7969     popover : {
7970         
7971         padding : 5,
7972         
7973         isApplied : false,
7974         
7975         isMasked : false,
7976         
7977         form : false,
7978         
7979         target : false,
7980         
7981         toolTip : false,
7982         
7983         intervalID : false,
7984         
7985         maskEl : false,
7986         
7987         apply : function()
7988         {
7989             if(this.isApplied){
7990                 return;
7991             }
7992             
7993             this.maskEl = {
7994                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
7995                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
7996                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
7997                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
7998             };
7999             
8000             this.maskEl.top.enableDisplayMode("block");
8001             this.maskEl.left.enableDisplayMode("block");
8002             this.maskEl.bottom.enableDisplayMode("block");
8003             this.maskEl.right.enableDisplayMode("block");
8004             
8005             this.toolTip = new Roo.bootstrap.Tooltip({
8006                 cls : 'roo-form-error-popover',
8007                 alignment : {
8008                     'left' : ['r-l', [-2,0], 'right'],
8009                     'right' : ['l-r', [2,0], 'left'],
8010                     'bottom' : ['tl-bl', [0,2], 'top'],
8011                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8012                 }
8013             });
8014             
8015             this.toolTip.render(Roo.get(document.body));
8016
8017             this.toolTip.el.enableDisplayMode("block");
8018             
8019             Roo.get(document.body).on('click', function(){
8020                 this.unmask();
8021             }, this);
8022             
8023             this.isApplied = true
8024         },
8025         
8026         mask : function(form, target)
8027         {
8028             this.form = form;
8029             
8030             this.target = target;
8031             
8032             if(!this.form.errorMask || !target.el){
8033                 return;
8034             }
8035             
8036             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8037             
8038             var scrolled = scrollable.getScroll();
8039             
8040             var ot = this.target.el.calcOffsetsTo(scrollable);
8041             
8042             scrollTo = ot[1] - 100;
8043             
8044             scrollable.scrollTo('top', scrollTo);
8045             
8046             var box = this.target.el.getBox();
8047
8048             var zIndex = Roo.bootstrap.Modal.zIndex++;
8049
8050             this.maskEl.top.setStyle('position', 'fixed');
8051             this.maskEl.top.setStyle('z-index', zIndex);
8052             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8053             this.maskEl.top.setXY([0, 0]);
8054             this.maskEl.top.show();
8055
8056             this.maskEl.left.setStyle('position', 'fixed');
8057             this.maskEl.left.setStyle('z-index', zIndex);
8058             this.maskEl.left.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8059             this.maskEl.left.setXY([box.right + this.padding, box.y - this.padding]);
8060             this.maskEl.left.show();
8061
8062             this.maskEl.bottom.setStyle('position', 'fixed');
8063             this.maskEl.bottom.setStyle('z-index', zIndex);
8064             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8065             this.maskEl.bottom.setXY([0, box.bottom + this.padding]);
8066             this.maskEl.bottom.show();
8067
8068             this.maskEl.right.setStyle('position', 'fixed');
8069             this.maskEl.right.setStyle('z-index', zIndex);
8070             this.maskEl.right.setSize(box.x - this.padding, box.height + this.padding * 2);
8071             this.maskEl.right.setXY([0, box.y - this.padding]);
8072             this.maskEl.right.show();
8073
8074
8075             this.toolTip.bindEl = this.target.el;
8076
8077             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8078
8079             var tip = this.target.blankText;
8080
8081             if(this.target.getValue() !== '' && this.target.regexText.length){
8082                 tip = this.target.regexText;
8083             }
8084
8085             this.toolTip.show(tip);
8086
8087             this.intervalID = window.setInterval(function() {
8088                 Roo.bootstrap.Form.popover.unmask();
8089             }, 10000);
8090
8091             window.onwheel = function(){ return false;};
8092             
8093             (function(){ this.isMasked = true; }).defer(500, this);
8094                 
8095             
8096             
8097         },
8098         
8099         unmask : function()
8100         {
8101             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8102                 return;
8103             }
8104             
8105             this.maskEl.top.setStyle('position', 'absolute');
8106             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8107             this.maskEl.top.hide();
8108
8109             this.maskEl.left.setStyle('position', 'absolute');
8110             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8111             this.maskEl.left.hide();
8112
8113             this.maskEl.bottom.setStyle('position', 'absolute');
8114             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8115             this.maskEl.bottom.hide();
8116
8117             this.maskEl.right.setStyle('position', 'absolute');
8118             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8119             this.maskEl.right.hide();
8120             
8121             this.toolTip.hide();
8122             
8123             this.toolTip.el.hide();
8124             
8125             window.onwheel = function(){ return true;};
8126             
8127             if(this.intervalID){
8128                 window.clearInterval(this.intervalID);
8129                 this.intervalID = false;
8130             }
8131             
8132             this.isMasked = false;
8133             
8134         }
8135         
8136     }
8137     
8138 });
8139
8140 /*
8141  * Based on:
8142  * Ext JS Library 1.1.1
8143  * Copyright(c) 2006-2007, Ext JS, LLC.
8144  *
8145  * Originally Released Under LGPL - original licence link has changed is not relivant.
8146  *
8147  * Fork - LGPL
8148  * <script type="text/javascript">
8149  */
8150 /**
8151  * @class Roo.form.VTypes
8152  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8153  * @singleton
8154  */
8155 Roo.form.VTypes = function(){
8156     // closure these in so they are only created once.
8157     var alpha = /^[a-zA-Z_]+$/;
8158     var alphanum = /^[a-zA-Z0-9_]+$/;
8159     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8160     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8161
8162     // All these messages and functions are configurable
8163     return {
8164         /**
8165          * The function used to validate email addresses
8166          * @param {String} value The email address
8167          */
8168         'email' : function(v){
8169             return email.test(v);
8170         },
8171         /**
8172          * The error text to display when the email validation function returns false
8173          * @type String
8174          */
8175         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8176         /**
8177          * The keystroke filter mask to be applied on email input
8178          * @type RegExp
8179          */
8180         'emailMask' : /[a-z0-9_\.\-@]/i,
8181
8182         /**
8183          * The function used to validate URLs
8184          * @param {String} value The URL
8185          */
8186         'url' : function(v){
8187             return url.test(v);
8188         },
8189         /**
8190          * The error text to display when the url validation function returns false
8191          * @type String
8192          */
8193         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8194         
8195         /**
8196          * The function used to validate alpha values
8197          * @param {String} value The value
8198          */
8199         'alpha' : function(v){
8200             return alpha.test(v);
8201         },
8202         /**
8203          * The error text to display when the alpha validation function returns false
8204          * @type String
8205          */
8206         'alphaText' : 'This field should only contain letters and _',
8207         /**
8208          * The keystroke filter mask to be applied on alpha input
8209          * @type RegExp
8210          */
8211         'alphaMask' : /[a-z_]/i,
8212
8213         /**
8214          * The function used to validate alphanumeric values
8215          * @param {String} value The value
8216          */
8217         'alphanum' : function(v){
8218             return alphanum.test(v);
8219         },
8220         /**
8221          * The error text to display when the alphanumeric validation function returns false
8222          * @type String
8223          */
8224         'alphanumText' : 'This field should only contain letters, numbers and _',
8225         /**
8226          * The keystroke filter mask to be applied on alphanumeric input
8227          * @type RegExp
8228          */
8229         'alphanumMask' : /[a-z0-9_]/i
8230     };
8231 }();/*
8232  * - LGPL
8233  *
8234  * Input
8235  * 
8236  */
8237
8238 /**
8239  * @class Roo.bootstrap.Input
8240  * @extends Roo.bootstrap.Component
8241  * Bootstrap Input class
8242  * @cfg {Boolean} disabled is it disabled
8243  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8244  * @cfg {String} name name of the input
8245  * @cfg {string} fieldLabel - the label associated
8246  * @cfg {string} placeholder - placeholder to put in text.
8247  * @cfg {string}  before - input group add on before
8248  * @cfg {string} after - input group add on after
8249  * @cfg {string} size - (lg|sm) or leave empty..
8250  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8251  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8252  * @cfg {Number} md colspan out of 12 for computer-sized screens
8253  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8254  * @cfg {string} value default value of the input
8255  * @cfg {Number} labelWidth set the width of label 
8256  * @cfg {Number} labellg set the width of label (1-12)
8257  * @cfg {Number} labelmd set the width of label (1-12)
8258  * @cfg {Number} labelsm set the width of label (1-12)
8259  * @cfg {Number} labelxs set the width of label (1-12)
8260  * @cfg {String} labelAlign (top|left)
8261  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8262  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8263  * @cfg {String} indicatorpos (left|right) default left
8264
8265  * @cfg {String} align (left|center|right) Default left
8266  * @cfg {Boolean} forceFeedback (true|false) Default false
8267  * 
8268  * 
8269  * 
8270  * 
8271  * @constructor
8272  * Create a new Input
8273  * @param {Object} config The config object
8274  */
8275
8276 Roo.bootstrap.Input = function(config){
8277     
8278     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8279     
8280     this.addEvents({
8281         /**
8282          * @event focus
8283          * Fires when this field receives input focus.
8284          * @param {Roo.form.Field} this
8285          */
8286         focus : true,
8287         /**
8288          * @event blur
8289          * Fires when this field loses input focus.
8290          * @param {Roo.form.Field} this
8291          */
8292         blur : true,
8293         /**
8294          * @event specialkey
8295          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8296          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8297          * @param {Roo.form.Field} this
8298          * @param {Roo.EventObject} e The event object
8299          */
8300         specialkey : true,
8301         /**
8302          * @event change
8303          * Fires just before the field blurs if the field value has changed.
8304          * @param {Roo.form.Field} this
8305          * @param {Mixed} newValue The new value
8306          * @param {Mixed} oldValue The original value
8307          */
8308         change : true,
8309         /**
8310          * @event invalid
8311          * Fires after the field has been marked as invalid.
8312          * @param {Roo.form.Field} this
8313          * @param {String} msg The validation message
8314          */
8315         invalid : true,
8316         /**
8317          * @event valid
8318          * Fires after the field has been validated with no errors.
8319          * @param {Roo.form.Field} this
8320          */
8321         valid : true,
8322          /**
8323          * @event keyup
8324          * Fires after the key up
8325          * @param {Roo.form.Field} this
8326          * @param {Roo.EventObject}  e The event Object
8327          */
8328         keyup : true
8329     });
8330 };
8331
8332 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8333      /**
8334      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8335       automatic validation (defaults to "keyup").
8336      */
8337     validationEvent : "keyup",
8338      /**
8339      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8340      */
8341     validateOnBlur : true,
8342     /**
8343      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8344      */
8345     validationDelay : 250,
8346      /**
8347      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8348      */
8349     focusClass : "x-form-focus",  // not needed???
8350     
8351        
8352     /**
8353      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8354      */
8355     invalidClass : "has-warning",
8356     
8357     /**
8358      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8359      */
8360     validClass : "has-success",
8361     
8362     /**
8363      * @cfg {Boolean} hasFeedback (true|false) default true
8364      */
8365     hasFeedback : true,
8366     
8367     /**
8368      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8369      */
8370     invalidFeedbackClass : "glyphicon-warning-sign",
8371     
8372     /**
8373      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8374      */
8375     validFeedbackClass : "glyphicon-ok",
8376     
8377     /**
8378      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8379      */
8380     selectOnFocus : false,
8381     
8382      /**
8383      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8384      */
8385     maskRe : null,
8386        /**
8387      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8388      */
8389     vtype : null,
8390     
8391       /**
8392      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8393      */
8394     disableKeyFilter : false,
8395     
8396        /**
8397      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8398      */
8399     disabled : false,
8400      /**
8401      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8402      */
8403     allowBlank : true,
8404     /**
8405      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8406      */
8407     blankText : "Please complete this mandatory field",
8408     
8409      /**
8410      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8411      */
8412     minLength : 0,
8413     /**
8414      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8415      */
8416     maxLength : Number.MAX_VALUE,
8417     /**
8418      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8419      */
8420     minLengthText : "The minimum length for this field is {0}",
8421     /**
8422      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8423      */
8424     maxLengthText : "The maximum length for this field is {0}",
8425   
8426     
8427     /**
8428      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8429      * If available, this function will be called only after the basic validators all return true, and will be passed the
8430      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8431      */
8432     validator : null,
8433     /**
8434      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8435      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8436      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8437      */
8438     regex : null,
8439     /**
8440      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8441      */
8442     regexText : "",
8443     
8444     autocomplete: false,
8445     
8446     
8447     fieldLabel : '',
8448     inputType : 'text',
8449     
8450     name : false,
8451     placeholder: false,
8452     before : false,
8453     after : false,
8454     size : false,
8455     hasFocus : false,
8456     preventMark: false,
8457     isFormField : true,
8458     value : '',
8459     labelWidth : 2,
8460     labelAlign : false,
8461     readOnly : false,
8462     align : false,
8463     formatedValue : false,
8464     forceFeedback : false,
8465     
8466     indicatorpos : 'left',
8467     
8468     labellg : 0,
8469     labelmd : 0,
8470     labelsm : 0,
8471     labelxs : 0,
8472     
8473     parentLabelAlign : function()
8474     {
8475         var parent = this;
8476         while (parent.parent()) {
8477             parent = parent.parent();
8478             if (typeof(parent.labelAlign) !='undefined') {
8479                 return parent.labelAlign;
8480             }
8481         }
8482         return 'left';
8483         
8484     },
8485     
8486     getAutoCreate : function()
8487     {
8488         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8489         
8490         var id = Roo.id();
8491         
8492         var cfg = {};
8493         
8494         if(this.inputType != 'hidden'){
8495             cfg.cls = 'form-group' //input-group
8496         }
8497         
8498         var input =  {
8499             tag: 'input',
8500             id : id,
8501             type : this.inputType,
8502             value : this.value,
8503             cls : 'form-control',
8504             placeholder : this.placeholder || '',
8505             autocomplete : this.autocomplete || 'new-password'
8506         };
8507         
8508         if(this.align){
8509             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8510         }
8511         
8512         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8513             input.maxLength = this.maxLength;
8514         }
8515         
8516         if (this.disabled) {
8517             input.disabled=true;
8518         }
8519         
8520         if (this.readOnly) {
8521             input.readonly=true;
8522         }
8523         
8524         if (this.name) {
8525             input.name = this.name;
8526         }
8527         
8528         if (this.size) {
8529             input.cls += ' input-' + this.size;
8530         }
8531         
8532         var settings=this;
8533         ['xs','sm','md','lg'].map(function(size){
8534             if (settings[size]) {
8535                 cfg.cls += ' col-' + size + '-' + settings[size];
8536             }
8537         });
8538         
8539         var inputblock = input;
8540         
8541         var feedback = {
8542             tag: 'span',
8543             cls: 'glyphicon form-control-feedback'
8544         };
8545             
8546         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8547             
8548             inputblock = {
8549                 cls : 'has-feedback',
8550                 cn :  [
8551                     input,
8552                     feedback
8553                 ] 
8554             };  
8555         }
8556         
8557         if (this.before || this.after) {
8558             
8559             inputblock = {
8560                 cls : 'input-group',
8561                 cn :  [] 
8562             };
8563             
8564             if (this.before && typeof(this.before) == 'string') {
8565                 
8566                 inputblock.cn.push({
8567                     tag :'span',
8568                     cls : 'roo-input-before input-group-addon',
8569                     html : this.before
8570                 });
8571             }
8572             if (this.before && typeof(this.before) == 'object') {
8573                 this.before = Roo.factory(this.before);
8574                 
8575                 inputblock.cn.push({
8576                     tag :'span',
8577                     cls : 'roo-input-before input-group-' +
8578                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8579                 });
8580             }
8581             
8582             inputblock.cn.push(input);
8583             
8584             if (this.after && typeof(this.after) == 'string') {
8585                 inputblock.cn.push({
8586                     tag :'span',
8587                     cls : 'roo-input-after input-group-addon',
8588                     html : this.after
8589                 });
8590             }
8591             if (this.after && typeof(this.after) == 'object') {
8592                 this.after = Roo.factory(this.after);
8593                 
8594                 inputblock.cn.push({
8595                     tag :'span',
8596                     cls : 'roo-input-after input-group-' +
8597                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8598                 });
8599             }
8600             
8601             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8602                 inputblock.cls += ' has-feedback';
8603                 inputblock.cn.push(feedback);
8604             }
8605         };
8606         
8607         if (align ==='left' && this.fieldLabel.length) {
8608             
8609             cfg.cls += ' roo-form-group-label-left';
8610             
8611             cfg.cn = [
8612                 {
8613                     tag : 'i',
8614                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8615                     tooltip : 'This field is required'
8616                 },
8617                 {
8618                     tag: 'label',
8619                     'for' :  id,
8620                     cls : 'control-label',
8621                     html : this.fieldLabel
8622
8623                 },
8624                 {
8625                     cls : "", 
8626                     cn: [
8627                         inputblock
8628                     ]
8629                 }
8630             ];
8631             
8632             var labelCfg = cfg.cn[1];
8633             var contentCfg = cfg.cn[2];
8634             
8635             if(this.indicatorpos == 'right'){
8636                 cfg.cn = [
8637                     {
8638                         tag: 'label',
8639                         'for' :  id,
8640                         cls : 'control-label',
8641                         html : this.fieldLabel
8642
8643                     },
8644                     {
8645                         tag : 'i',
8646                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8647                         tooltip : 'This field is required'
8648                     },
8649                     {
8650                         cls : "",
8651                         cn: [
8652                             inputblock
8653                         ]
8654                     }
8655
8656                 ];
8657                 
8658                 labelCfg = cfg.cn[0];
8659                 contentCfg = cfg.cn[2];
8660             
8661             }
8662             
8663             if(this.labelWidth > 12){
8664                 labelCfg.style = "width: " + this.labelWidth + 'px';
8665             }
8666             
8667             if(this.labelWidth < 13 && this.labelmd == 0){
8668                 this.labelmd = this.labelWidth;
8669             }
8670             
8671             if(this.labellg > 0){
8672                 labelCfg.cls += ' col-lg-' + this.labellg;
8673                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8674             }
8675             
8676             if(this.labelmd > 0){
8677                 labelCfg.cls += ' col-md-' + this.labelmd;
8678                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8679             }
8680             
8681             if(this.labelsm > 0){
8682                 labelCfg.cls += ' col-sm-' + this.labelsm;
8683                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8684             }
8685             
8686             if(this.labelxs > 0){
8687                 labelCfg.cls += ' col-xs-' + this.labelxs;
8688                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8689             }
8690             
8691             
8692         } else if ( this.fieldLabel.length) {
8693                 
8694             cfg.cn = [
8695                 {
8696                     tag : 'i',
8697                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8698                     tooltip : 'This field is required'
8699                 },
8700                 {
8701                     tag: 'label',
8702                    //cls : 'input-group-addon',
8703                     html : this.fieldLabel
8704
8705                 },
8706
8707                inputblock
8708
8709            ];
8710            
8711            if(this.indicatorpos == 'right'){
8712                 
8713                 cfg.cn = [
8714                     {
8715                         tag: 'label',
8716                        //cls : 'input-group-addon',
8717                         html : this.fieldLabel
8718
8719                     },
8720                     {
8721                         tag : 'i',
8722                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8723                         tooltip : 'This field is required'
8724                     },
8725
8726                    inputblock
8727
8728                ];
8729
8730             }
8731
8732         } else {
8733             
8734             cfg.cn = [
8735
8736                     inputblock
8737
8738             ];
8739                 
8740                 
8741         };
8742         
8743         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8744            cfg.cls += ' navbar-form';
8745         }
8746         
8747         if (this.parentType === 'NavGroup') {
8748            cfg.cls += ' navbar-form';
8749            cfg.tag = 'li';
8750         }
8751         
8752         return cfg;
8753         
8754     },
8755     /**
8756      * return the real input element.
8757      */
8758     inputEl: function ()
8759     {
8760         return this.el.select('input.form-control',true).first();
8761     },
8762     
8763     tooltipEl : function()
8764     {
8765         return this.inputEl();
8766     },
8767     
8768     indicatorEl : function()
8769     {
8770         var indicator = this.el.select('i.roo-required-indicator',true).first();
8771         
8772         if(!indicator){
8773             return false;
8774         }
8775         
8776         return indicator;
8777         
8778     },
8779     
8780     setDisabled : function(v)
8781     {
8782         var i  = this.inputEl().dom;
8783         if (!v) {
8784             i.removeAttribute('disabled');
8785             return;
8786             
8787         }
8788         i.setAttribute('disabled','true');
8789     },
8790     initEvents : function()
8791     {
8792           
8793         this.inputEl().on("keydown" , this.fireKey,  this);
8794         this.inputEl().on("focus", this.onFocus,  this);
8795         this.inputEl().on("blur", this.onBlur,  this);
8796         
8797         this.inputEl().relayEvent('keyup', this);
8798         
8799         this.indicator = this.indicatorEl();
8800         
8801         if(this.indicator){
8802             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8803             this.indicator.hide();
8804         }
8805  
8806         // reference to original value for reset
8807         this.originalValue = this.getValue();
8808         //Roo.form.TextField.superclass.initEvents.call(this);
8809         if(this.validationEvent == 'keyup'){
8810             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8811             this.inputEl().on('keyup', this.filterValidation, this);
8812         }
8813         else if(this.validationEvent !== false){
8814             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8815         }
8816         
8817         if(this.selectOnFocus){
8818             this.on("focus", this.preFocus, this);
8819             
8820         }
8821         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8822             this.inputEl().on("keypress", this.filterKeys, this);
8823         } else {
8824             this.inputEl().relayEvent('keypress', this);
8825         }
8826        /* if(this.grow){
8827             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8828             this.el.on("click", this.autoSize,  this);
8829         }
8830         */
8831         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8832             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8833         }
8834         
8835         if (typeof(this.before) == 'object') {
8836             this.before.render(this.el.select('.roo-input-before',true).first());
8837         }
8838         if (typeof(this.after) == 'object') {
8839             this.after.render(this.el.select('.roo-input-after',true).first());
8840         }
8841         
8842         
8843     },
8844     filterValidation : function(e){
8845         if(!e.isNavKeyPress()){
8846             this.validationTask.delay(this.validationDelay);
8847         }
8848     },
8849      /**
8850      * Validates the field value
8851      * @return {Boolean} True if the value is valid, else false
8852      */
8853     validate : function(){
8854         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8855         if(this.disabled || this.validateValue(this.getRawValue())){
8856             this.markValid();
8857             return true;
8858         }
8859         
8860         this.markInvalid();
8861         return false;
8862     },
8863     
8864     
8865     /**
8866      * Validates a value according to the field's validation rules and marks the field as invalid
8867      * if the validation fails
8868      * @param {Mixed} value The value to validate
8869      * @return {Boolean} True if the value is valid, else false
8870      */
8871     validateValue : function(value){
8872         if(value.length < 1)  { // if it's blank
8873             if(this.allowBlank){
8874                 return true;
8875             }
8876             return false;
8877         }
8878         
8879         if(value.length < this.minLength){
8880             return false;
8881         }
8882         if(value.length > this.maxLength){
8883             return false;
8884         }
8885         if(this.vtype){
8886             var vt = Roo.form.VTypes;
8887             if(!vt[this.vtype](value, this)){
8888                 return false;
8889             }
8890         }
8891         if(typeof this.validator == "function"){
8892             var msg = this.validator(value);
8893             if(msg !== true){
8894                 return false;
8895             }
8896         }
8897         
8898         if(this.regex && !this.regex.test(value)){
8899             return false;
8900         }
8901         
8902         return true;
8903     },
8904
8905     
8906     
8907      // private
8908     fireKey : function(e){
8909         //Roo.log('field ' + e.getKey());
8910         if(e.isNavKeyPress()){
8911             this.fireEvent("specialkey", this, e);
8912         }
8913     },
8914     focus : function (selectText){
8915         if(this.rendered){
8916             this.inputEl().focus();
8917             if(selectText === true){
8918                 this.inputEl().dom.select();
8919             }
8920         }
8921         return this;
8922     } ,
8923     
8924     onFocus : function(){
8925         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8926            // this.el.addClass(this.focusClass);
8927         }
8928         if(!this.hasFocus){
8929             this.hasFocus = true;
8930             this.startValue = this.getValue();
8931             this.fireEvent("focus", this);
8932         }
8933     },
8934     
8935     beforeBlur : Roo.emptyFn,
8936
8937     
8938     // private
8939     onBlur : function(){
8940         this.beforeBlur();
8941         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8942             //this.el.removeClass(this.focusClass);
8943         }
8944         this.hasFocus = false;
8945         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8946             this.validate();
8947         }
8948         var v = this.getValue();
8949         if(String(v) !== String(this.startValue)){
8950             this.fireEvent('change', this, v, this.startValue);
8951         }
8952         this.fireEvent("blur", this);
8953     },
8954     
8955     /**
8956      * Resets the current field value to the originally loaded value and clears any validation messages
8957      */
8958     reset : function(){
8959         this.setValue(this.originalValue);
8960         this.validate();
8961     },
8962      /**
8963      * Returns the name of the field
8964      * @return {Mixed} name The name field
8965      */
8966     getName: function(){
8967         return this.name;
8968     },
8969      /**
8970      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8971      * @return {Mixed} value The field value
8972      */
8973     getValue : function(){
8974         
8975         var v = this.inputEl().getValue();
8976         
8977         return v;
8978     },
8979     /**
8980      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8981      * @return {Mixed} value The field value
8982      */
8983     getRawValue : function(){
8984         var v = this.inputEl().getValue();
8985         
8986         return v;
8987     },
8988     
8989     /**
8990      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8991      * @param {Mixed} value The value to set
8992      */
8993     setRawValue : function(v){
8994         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8995     },
8996     
8997     selectText : function(start, end){
8998         var v = this.getRawValue();
8999         if(v.length > 0){
9000             start = start === undefined ? 0 : start;
9001             end = end === undefined ? v.length : end;
9002             var d = this.inputEl().dom;
9003             if(d.setSelectionRange){
9004                 d.setSelectionRange(start, end);
9005             }else if(d.createTextRange){
9006                 var range = d.createTextRange();
9007                 range.moveStart("character", start);
9008                 range.moveEnd("character", v.length-end);
9009                 range.select();
9010             }
9011         }
9012     },
9013     
9014     /**
9015      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9016      * @param {Mixed} value The value to set
9017      */
9018     setValue : function(v){
9019         this.value = v;
9020         if(this.rendered){
9021             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9022             this.validate();
9023         }
9024     },
9025     
9026     /*
9027     processValue : function(value){
9028         if(this.stripCharsRe){
9029             var newValue = value.replace(this.stripCharsRe, '');
9030             if(newValue !== value){
9031                 this.setRawValue(newValue);
9032                 return newValue;
9033             }
9034         }
9035         return value;
9036     },
9037   */
9038     preFocus : function(){
9039         
9040         if(this.selectOnFocus){
9041             this.inputEl().dom.select();
9042         }
9043     },
9044     filterKeys : function(e){
9045         var k = e.getKey();
9046         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9047             return;
9048         }
9049         var c = e.getCharCode(), cc = String.fromCharCode(c);
9050         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9051             return;
9052         }
9053         if(!this.maskRe.test(cc)){
9054             e.stopEvent();
9055         }
9056     },
9057      /**
9058      * Clear any invalid styles/messages for this field
9059      */
9060     clearInvalid : function(){
9061         
9062         if(!this.el || this.preventMark){ // not rendered
9063             return;
9064         }
9065         
9066         if(this.indicator){
9067             this.indicator.hide();
9068         }
9069         
9070         this.el.removeClass(this.invalidClass);
9071         
9072         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9073             
9074             var feedback = this.el.select('.form-control-feedback', true).first();
9075             
9076             if(feedback){
9077                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9078             }
9079             
9080         }
9081         
9082         this.fireEvent('valid', this);
9083     },
9084     
9085      /**
9086      * Mark this field as valid
9087      */
9088     markValid : function()
9089     {
9090         if(!this.el  || this.preventMark){ // not rendered
9091             return;
9092         }
9093         
9094         this.el.removeClass([this.invalidClass, this.validClass]);
9095         
9096         var feedback = this.el.select('.form-control-feedback', true).first();
9097             
9098         if(feedback){
9099             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9100         }
9101
9102         if(this.disabled){
9103             return;
9104         }
9105         
9106         if(this.allowBlank && !this.getRawValue().length){
9107             return;
9108         }
9109         
9110         if(this.indicator){
9111             this.indicator.hide();
9112         }
9113         
9114         this.el.addClass(this.validClass);
9115         
9116         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9117             
9118             var feedback = this.el.select('.form-control-feedback', true).first();
9119             
9120             if(feedback){
9121                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9122                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9123             }
9124             
9125         }
9126         
9127         this.fireEvent('valid', this);
9128     },
9129     
9130      /**
9131      * Mark this field as invalid
9132      * @param {String} msg The validation message
9133      */
9134     markInvalid : function(msg)
9135     {
9136         if(!this.el  || this.preventMark){ // not rendered
9137             return;
9138         }
9139         
9140         this.el.removeClass([this.invalidClass, this.validClass]);
9141         
9142         var feedback = this.el.select('.form-control-feedback', true).first();
9143             
9144         if(feedback){
9145             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9146         }
9147
9148         if(this.disabled){
9149             return;
9150         }
9151         
9152         if(this.allowBlank && !this.getRawValue().length){
9153             return;
9154         }
9155         
9156         if(this.indicator){
9157             this.indicator.show();
9158         }
9159         
9160         this.el.addClass(this.invalidClass);
9161         
9162         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9163             
9164             var feedback = this.el.select('.form-control-feedback', true).first();
9165             
9166             if(feedback){
9167                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9168                 
9169                 if(this.getValue().length || this.forceFeedback){
9170                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9171                 }
9172                 
9173             }
9174             
9175         }
9176         
9177         this.fireEvent('invalid', this, msg);
9178     },
9179     // private
9180     SafariOnKeyDown : function(event)
9181     {
9182         // this is a workaround for a password hang bug on chrome/ webkit.
9183         if (this.inputEl().dom.type != 'password') {
9184             return;
9185         }
9186         
9187         var isSelectAll = false;
9188         
9189         if(this.inputEl().dom.selectionEnd > 0){
9190             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9191         }
9192         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9193             event.preventDefault();
9194             this.setValue('');
9195             return;
9196         }
9197         
9198         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9199             
9200             event.preventDefault();
9201             // this is very hacky as keydown always get's upper case.
9202             //
9203             var cc = String.fromCharCode(event.getCharCode());
9204             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9205             
9206         }
9207     },
9208     adjustWidth : function(tag, w){
9209         tag = tag.toLowerCase();
9210         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9211             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9212                 if(tag == 'input'){
9213                     return w + 2;
9214                 }
9215                 if(tag == 'textarea'){
9216                     return w-2;
9217                 }
9218             }else if(Roo.isOpera){
9219                 if(tag == 'input'){
9220                     return w + 2;
9221                 }
9222                 if(tag == 'textarea'){
9223                     return w-2;
9224                 }
9225             }
9226         }
9227         return w;
9228     }
9229     
9230 });
9231
9232  
9233 /*
9234  * - LGPL
9235  *
9236  * Input
9237  * 
9238  */
9239
9240 /**
9241  * @class Roo.bootstrap.TextArea
9242  * @extends Roo.bootstrap.Input
9243  * Bootstrap TextArea class
9244  * @cfg {Number} cols Specifies the visible width of a text area
9245  * @cfg {Number} rows Specifies the visible number of lines in a text area
9246  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9247  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9248  * @cfg {string} html text
9249  * 
9250  * @constructor
9251  * Create a new TextArea
9252  * @param {Object} config The config object
9253  */
9254
9255 Roo.bootstrap.TextArea = function(config){
9256     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9257    
9258 };
9259
9260 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9261      
9262     cols : false,
9263     rows : 5,
9264     readOnly : false,
9265     warp : 'soft',
9266     resize : false,
9267     value: false,
9268     html: false,
9269     
9270     getAutoCreate : function(){
9271         
9272         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9273         
9274         var id = Roo.id();
9275         
9276         var cfg = {};
9277         
9278         var input =  {
9279             tag: 'textarea',
9280             id : id,
9281             warp : this.warp,
9282             rows : this.rows,
9283             value : this.value || '',
9284             html: this.html || '',
9285             cls : 'form-control',
9286             placeholder : this.placeholder || '' 
9287             
9288         };
9289         
9290         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9291             input.maxLength = this.maxLength;
9292         }
9293         
9294         if(this.resize){
9295             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9296         }
9297         
9298         if(this.cols){
9299             input.cols = this.cols;
9300         }
9301         
9302         if (this.readOnly) {
9303             input.readonly = true;
9304         }
9305         
9306         if (this.name) {
9307             input.name = this.name;
9308         }
9309         
9310         if (this.size) {
9311             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9312         }
9313         
9314         var settings=this;
9315         ['xs','sm','md','lg'].map(function(size){
9316             if (settings[size]) {
9317                 cfg.cls += ' col-' + size + '-' + settings[size];
9318             }
9319         });
9320         
9321         var inputblock = input;
9322         
9323         if(this.hasFeedback && !this.allowBlank){
9324             
9325             var feedback = {
9326                 tag: 'span',
9327                 cls: 'glyphicon form-control-feedback'
9328             };
9329
9330             inputblock = {
9331                 cls : 'has-feedback',
9332                 cn :  [
9333                     input,
9334                     feedback
9335                 ] 
9336             };  
9337         }
9338         
9339         
9340         if (this.before || this.after) {
9341             
9342             inputblock = {
9343                 cls : 'input-group',
9344                 cn :  [] 
9345             };
9346             if (this.before) {
9347                 inputblock.cn.push({
9348                     tag :'span',
9349                     cls : 'input-group-addon',
9350                     html : this.before
9351                 });
9352             }
9353             
9354             inputblock.cn.push(input);
9355             
9356             if(this.hasFeedback && !this.allowBlank){
9357                 inputblock.cls += ' has-feedback';
9358                 inputblock.cn.push(feedback);
9359             }
9360             
9361             if (this.after) {
9362                 inputblock.cn.push({
9363                     tag :'span',
9364                     cls : 'input-group-addon',
9365                     html : this.after
9366                 });
9367             }
9368             
9369         }
9370         
9371         if (align ==='left' && this.fieldLabel.length) {
9372             cfg.cn = [
9373                 {
9374                     tag: 'label',
9375                     'for' :  id,
9376                     cls : 'control-label',
9377                     html : this.fieldLabel
9378                 },
9379                 {
9380                     cls : "",
9381                     cn: [
9382                         inputblock
9383                     ]
9384                 }
9385
9386             ];
9387             
9388             if(this.labelWidth > 12){
9389                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9390             }
9391
9392             if(this.labelWidth < 13 && this.labelmd == 0){
9393                 this.labelmd = this.labelWidth;
9394             }
9395
9396             if(this.labellg > 0){
9397                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9398                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9399             }
9400
9401             if(this.labelmd > 0){
9402                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9403                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9404             }
9405
9406             if(this.labelsm > 0){
9407                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9408                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9409             }
9410
9411             if(this.labelxs > 0){
9412                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9413                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9414             }
9415             
9416         } else if ( this.fieldLabel.length) {
9417             cfg.cn = [
9418
9419                {
9420                    tag: 'label',
9421                    //cls : 'input-group-addon',
9422                    html : this.fieldLabel
9423
9424                },
9425
9426                inputblock
9427
9428            ];
9429
9430         } else {
9431
9432             cfg.cn = [
9433
9434                 inputblock
9435
9436             ];
9437                 
9438         }
9439         
9440         if (this.disabled) {
9441             input.disabled=true;
9442         }
9443         
9444         return cfg;
9445         
9446     },
9447     /**
9448      * return the real textarea element.
9449      */
9450     inputEl: function ()
9451     {
9452         return this.el.select('textarea.form-control',true).first();
9453     },
9454     
9455     /**
9456      * Clear any invalid styles/messages for this field
9457      */
9458     clearInvalid : function()
9459     {
9460         
9461         if(!this.el || this.preventMark){ // not rendered
9462             return;
9463         }
9464         
9465         var label = this.el.select('label', true).first();
9466         var icon = this.el.select('i.fa-star', true).first();
9467         
9468         if(label && icon){
9469             icon.remove();
9470         }
9471         
9472         this.el.removeClass(this.invalidClass);
9473         
9474         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9475             
9476             var feedback = this.el.select('.form-control-feedback', true).first();
9477             
9478             if(feedback){
9479                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9480             }
9481             
9482         }
9483         
9484         this.fireEvent('valid', this);
9485     },
9486     
9487      /**
9488      * Mark this field as valid
9489      */
9490     markValid : function()
9491     {
9492         if(!this.el  || this.preventMark){ // not rendered
9493             return;
9494         }
9495         
9496         this.el.removeClass([this.invalidClass, this.validClass]);
9497         
9498         var feedback = this.el.select('.form-control-feedback', true).first();
9499             
9500         if(feedback){
9501             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9502         }
9503
9504         if(this.disabled || this.allowBlank){
9505             return;
9506         }
9507         
9508         var label = this.el.select('label', true).first();
9509         var icon = this.el.select('i.fa-star', true).first();
9510         
9511         if(label && icon){
9512             icon.remove();
9513         }
9514         
9515         this.el.addClass(this.validClass);
9516         
9517         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9518             
9519             var feedback = this.el.select('.form-control-feedback', true).first();
9520             
9521             if(feedback){
9522                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9523                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9524             }
9525             
9526         }
9527         
9528         this.fireEvent('valid', this);
9529     },
9530     
9531      /**
9532      * Mark this field as invalid
9533      * @param {String} msg The validation message
9534      */
9535     markInvalid : function(msg)
9536     {
9537         if(!this.el  || this.preventMark){ // not rendered
9538             return;
9539         }
9540         
9541         this.el.removeClass([this.invalidClass, this.validClass]);
9542         
9543         var feedback = this.el.select('.form-control-feedback', true).first();
9544             
9545         if(feedback){
9546             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9547         }
9548
9549         if(this.disabled || this.allowBlank){
9550             return;
9551         }
9552         
9553         var label = this.el.select('label', true).first();
9554         var icon = this.el.select('i.fa-star', true).first();
9555         
9556         if(!this.getValue().length && label && !icon){
9557             this.el.createChild({
9558                 tag : 'i',
9559                 cls : 'text-danger fa fa-lg fa-star',
9560                 tooltip : 'This field is required',
9561                 style : 'margin-right:5px;'
9562             }, label, true);
9563         }
9564
9565         this.el.addClass(this.invalidClass);
9566         
9567         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9568             
9569             var feedback = this.el.select('.form-control-feedback', true).first();
9570             
9571             if(feedback){
9572                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9573                 
9574                 if(this.getValue().length || this.forceFeedback){
9575                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9576                 }
9577                 
9578             }
9579             
9580         }
9581         
9582         this.fireEvent('invalid', this, msg);
9583     }
9584 });
9585
9586  
9587 /*
9588  * - LGPL
9589  *
9590  * trigger field - base class for combo..
9591  * 
9592  */
9593  
9594 /**
9595  * @class Roo.bootstrap.TriggerField
9596  * @extends Roo.bootstrap.Input
9597  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9598  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9599  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9600  * for which you can provide a custom implementation.  For example:
9601  * <pre><code>
9602 var trigger = new Roo.bootstrap.TriggerField();
9603 trigger.onTriggerClick = myTriggerFn;
9604 trigger.applyTo('my-field');
9605 </code></pre>
9606  *
9607  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9608  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9609  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9610  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9611  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9612
9613  * @constructor
9614  * Create a new TriggerField.
9615  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9616  * to the base TextField)
9617  */
9618 Roo.bootstrap.TriggerField = function(config){
9619     this.mimicing = false;
9620     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9621 };
9622
9623 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9624     /**
9625      * @cfg {String} triggerClass A CSS class to apply to the trigger
9626      */
9627      /**
9628      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9629      */
9630     hideTrigger:false,
9631
9632     /**
9633      * @cfg {Boolean} removable (true|false) special filter default false
9634      */
9635     removable : false,
9636     
9637     /** @cfg {Boolean} grow @hide */
9638     /** @cfg {Number} growMin @hide */
9639     /** @cfg {Number} growMax @hide */
9640
9641     /**
9642      * @hide 
9643      * @method
9644      */
9645     autoSize: Roo.emptyFn,
9646     // private
9647     monitorTab : true,
9648     // private
9649     deferHeight : true,
9650
9651     
9652     actionMode : 'wrap',
9653     
9654     caret : false,
9655     
9656     
9657     getAutoCreate : function(){
9658        
9659         var align = this.labelAlign || this.parentLabelAlign();
9660         
9661         var id = Roo.id();
9662         
9663         var cfg = {
9664             cls: 'form-group' //input-group
9665         };
9666         
9667         
9668         var input =  {
9669             tag: 'input',
9670             id : id,
9671             type : this.inputType,
9672             cls : 'form-control',
9673             autocomplete: 'new-password',
9674             placeholder : this.placeholder || '' 
9675             
9676         };
9677         if (this.name) {
9678             input.name = this.name;
9679         }
9680         if (this.size) {
9681             input.cls += ' input-' + this.size;
9682         }
9683         
9684         if (this.disabled) {
9685             input.disabled=true;
9686         }
9687         
9688         var inputblock = input;
9689         
9690         if(this.hasFeedback && !this.allowBlank){
9691             
9692             var feedback = {
9693                 tag: 'span',
9694                 cls: 'glyphicon form-control-feedback'
9695             };
9696             
9697             if(this.removable && !this.editable && !this.tickable){
9698                 inputblock = {
9699                     cls : 'has-feedback',
9700                     cn :  [
9701                         inputblock,
9702                         {
9703                             tag: 'button',
9704                             html : 'x',
9705                             cls : 'roo-combo-removable-btn close'
9706                         },
9707                         feedback
9708                     ] 
9709                 };
9710             } else {
9711                 inputblock = {
9712                     cls : 'has-feedback',
9713                     cn :  [
9714                         inputblock,
9715                         feedback
9716                     ] 
9717                 };
9718             }
9719
9720         } else {
9721             if(this.removable && !this.editable && !this.tickable){
9722                 inputblock = {
9723                     cls : 'roo-removable',
9724                     cn :  [
9725                         inputblock,
9726                         {
9727                             tag: 'button',
9728                             html : 'x',
9729                             cls : 'roo-combo-removable-btn close'
9730                         }
9731                     ] 
9732                 };
9733             }
9734         }
9735         
9736         if (this.before || this.after) {
9737             
9738             inputblock = {
9739                 cls : 'input-group',
9740                 cn :  [] 
9741             };
9742             if (this.before) {
9743                 inputblock.cn.push({
9744                     tag :'span',
9745                     cls : 'input-group-addon',
9746                     html : this.before
9747                 });
9748             }
9749             
9750             inputblock.cn.push(input);
9751             
9752             if(this.hasFeedback && !this.allowBlank){
9753                 inputblock.cls += ' has-feedback';
9754                 inputblock.cn.push(feedback);
9755             }
9756             
9757             if (this.after) {
9758                 inputblock.cn.push({
9759                     tag :'span',
9760                     cls : 'input-group-addon',
9761                     html : this.after
9762                 });
9763             }
9764             
9765         };
9766         
9767         var box = {
9768             tag: 'div',
9769             cn: [
9770                 {
9771                     tag: 'input',
9772                     type : 'hidden',
9773                     cls: 'form-hidden-field'
9774                 },
9775                 inputblock
9776             ]
9777             
9778         };
9779         
9780         if(this.multiple){
9781             box = {
9782                 tag: 'div',
9783                 cn: [
9784                     {
9785                         tag: 'input',
9786                         type : 'hidden',
9787                         cls: 'form-hidden-field'
9788                     },
9789                     {
9790                         tag: 'ul',
9791                         cls: 'roo-select2-choices',
9792                         cn:[
9793                             {
9794                                 tag: 'li',
9795                                 cls: 'roo-select2-search-field',
9796                                 cn: [
9797
9798                                     inputblock
9799                                 ]
9800                             }
9801                         ]
9802                     }
9803                 ]
9804             }
9805         };
9806         
9807         var combobox = {
9808             cls: 'roo-select2-container input-group',
9809             cn: [
9810                 box
9811 //                {
9812 //                    tag: 'ul',
9813 //                    cls: 'typeahead typeahead-long dropdown-menu',
9814 //                    style: 'display:none'
9815 //                }
9816             ]
9817         };
9818         
9819         if(!this.multiple && this.showToggleBtn){
9820             
9821             var caret = {
9822                         tag: 'span',
9823                         cls: 'caret'
9824              };
9825             if (this.caret != false) {
9826                 caret = {
9827                      tag: 'i',
9828                      cls: 'fa fa-' + this.caret
9829                 };
9830                 
9831             }
9832             
9833             combobox.cn.push({
9834                 tag :'span',
9835                 cls : 'input-group-addon btn dropdown-toggle',
9836                 cn : [
9837                     caret,
9838                     {
9839                         tag: 'span',
9840                         cls: 'combobox-clear',
9841                         cn  : [
9842                             {
9843                                 tag : 'i',
9844                                 cls: 'icon-remove'
9845                             }
9846                         ]
9847                     }
9848                 ]
9849
9850             })
9851         }
9852         
9853         if(this.multiple){
9854             combobox.cls += ' roo-select2-container-multi';
9855         }
9856         
9857         if (align ==='left' && this.fieldLabel.length) {
9858             
9859             cfg.cls += ' roo-form-group-label-left';
9860
9861             cfg.cn = [
9862                 {
9863                     tag : 'i',
9864                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9865                     tooltip : 'This field is required'
9866                 },
9867                 {
9868                     tag: 'label',
9869                     'for' :  id,
9870                     cls : 'control-label',
9871                     html : this.fieldLabel
9872
9873                 },
9874                 {
9875                     cls : "", 
9876                     cn: [
9877                         combobox
9878                     ]
9879                 }
9880
9881             ];
9882             
9883             var labelCfg = cfg.cn[1];
9884             var contentCfg = cfg.cn[2];
9885             
9886             if(this.indicatorpos == 'right'){
9887                 cfg.cn = [
9888                     {
9889                         tag: 'label',
9890                         'for' :  id,
9891                         cls : 'control-label',
9892                         html : this.fieldLabel
9893                     },
9894                     {
9895                         tag : 'i',
9896                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9897                         tooltip : 'This field is required'
9898                     },
9899                     {
9900                         cls : "", 
9901                         cn: [
9902                             combobox
9903                         ]
9904                     }
9905
9906                 ];
9907                 
9908                 labelCfg = cfg.cn[0];
9909                 contentCfg = cfg.cn[2];
9910             }
9911             
9912             if(this.labelWidth > 12){
9913                 labelCfg.style = "width: " + this.labelWidth + 'px';
9914             }
9915             
9916             if(this.labelWidth < 13 && this.labelmd == 0){
9917                 this.labelmd = this.labelWidth;
9918             }
9919             
9920             if(this.labellg > 0){
9921                 labelCfg.cls += ' col-lg-' + this.labellg;
9922                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9923             }
9924             
9925             if(this.labelmd > 0){
9926                 labelCfg.cls += ' col-md-' + this.labelmd;
9927                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9928             }
9929             
9930             if(this.labelsm > 0){
9931                 labelCfg.cls += ' col-sm-' + this.labelsm;
9932                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9933             }
9934             
9935             if(this.labelxs > 0){
9936                 labelCfg.cls += ' col-xs-' + this.labelxs;
9937                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9938             }
9939             
9940         } else if ( this.fieldLabel.length) {
9941 //                Roo.log(" label");
9942             cfg.cn = [
9943                 {
9944                    tag : 'i',
9945                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9946                    tooltip : 'This field is required'
9947                },
9948                {
9949                    tag: 'label',
9950                    //cls : 'input-group-addon',
9951                    html : this.fieldLabel
9952
9953                },
9954
9955                combobox
9956
9957             ];
9958             
9959             if(this.indicatorpos == 'right'){
9960                 
9961                 cfg.cn = [
9962                     {
9963                        tag: 'label',
9964                        //cls : 'input-group-addon',
9965                        html : this.fieldLabel
9966
9967                     },
9968                     {
9969                        tag : 'i',
9970                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9971                        tooltip : 'This field is required'
9972                     },
9973                     
9974                     combobox
9975
9976                 ];
9977
9978             }
9979
9980         } else {
9981             
9982 //                Roo.log(" no label && no align");
9983                 cfg = combobox
9984                      
9985                 
9986         }
9987         
9988         var settings=this;
9989         ['xs','sm','md','lg'].map(function(size){
9990             if (settings[size]) {
9991                 cfg.cls += ' col-' + size + '-' + settings[size];
9992             }
9993         });
9994         
9995         return cfg;
9996         
9997     },
9998     
9999     
10000     
10001     // private
10002     onResize : function(w, h){
10003 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10004 //        if(typeof w == 'number'){
10005 //            var x = w - this.trigger.getWidth();
10006 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10007 //            this.trigger.setStyle('left', x+'px');
10008 //        }
10009     },
10010
10011     // private
10012     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10013
10014     // private
10015     getResizeEl : function(){
10016         return this.inputEl();
10017     },
10018
10019     // private
10020     getPositionEl : function(){
10021         return this.inputEl();
10022     },
10023
10024     // private
10025     alignErrorIcon : function(){
10026         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10027     },
10028
10029     // private
10030     initEvents : function(){
10031         
10032         this.createList();
10033         
10034         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10035         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10036         if(!this.multiple && this.showToggleBtn){
10037             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10038             if(this.hideTrigger){
10039                 this.trigger.setDisplayed(false);
10040             }
10041             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10042         }
10043         
10044         if(this.multiple){
10045             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10046         }
10047         
10048         if(this.removable && !this.editable && !this.tickable){
10049             var close = this.closeTriggerEl();
10050             
10051             if(close){
10052                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10053                 close.on('click', this.removeBtnClick, this, close);
10054             }
10055         }
10056         
10057         //this.trigger.addClassOnOver('x-form-trigger-over');
10058         //this.trigger.addClassOnClick('x-form-trigger-click');
10059         
10060         //if(!this.width){
10061         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10062         //}
10063     },
10064     
10065     closeTriggerEl : function()
10066     {
10067         var close = this.el.select('.roo-combo-removable-btn', true).first();
10068         return close ? close : false;
10069     },
10070     
10071     removeBtnClick : function(e, h, el)
10072     {
10073         e.preventDefault();
10074         
10075         if(this.fireEvent("remove", this) !== false){
10076             this.reset();
10077             this.fireEvent("afterremove", this)
10078         }
10079     },
10080     
10081     createList : function()
10082     {
10083         this.list = Roo.get(document.body).createChild({
10084             tag: 'ul',
10085             cls: 'typeahead typeahead-long dropdown-menu',
10086             style: 'display:none'
10087         });
10088         
10089         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10090         
10091     },
10092
10093     // private
10094     initTrigger : function(){
10095        
10096     },
10097
10098     // private
10099     onDestroy : function(){
10100         if(this.trigger){
10101             this.trigger.removeAllListeners();
10102           //  this.trigger.remove();
10103         }
10104         //if(this.wrap){
10105         //    this.wrap.remove();
10106         //}
10107         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10108     },
10109
10110     // private
10111     onFocus : function(){
10112         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10113         /*
10114         if(!this.mimicing){
10115             this.wrap.addClass('x-trigger-wrap-focus');
10116             this.mimicing = true;
10117             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10118             if(this.monitorTab){
10119                 this.el.on("keydown", this.checkTab, this);
10120             }
10121         }
10122         */
10123     },
10124
10125     // private
10126     checkTab : function(e){
10127         if(e.getKey() == e.TAB){
10128             this.triggerBlur();
10129         }
10130     },
10131
10132     // private
10133     onBlur : function(){
10134         // do nothing
10135     },
10136
10137     // private
10138     mimicBlur : function(e, t){
10139         /*
10140         if(!this.wrap.contains(t) && this.validateBlur()){
10141             this.triggerBlur();
10142         }
10143         */
10144     },
10145
10146     // private
10147     triggerBlur : function(){
10148         this.mimicing = false;
10149         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10150         if(this.monitorTab){
10151             this.el.un("keydown", this.checkTab, this);
10152         }
10153         //this.wrap.removeClass('x-trigger-wrap-focus');
10154         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10155     },
10156
10157     // private
10158     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10159     validateBlur : function(e, t){
10160         return true;
10161     },
10162
10163     // private
10164     onDisable : function(){
10165         this.inputEl().dom.disabled = true;
10166         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10167         //if(this.wrap){
10168         //    this.wrap.addClass('x-item-disabled');
10169         //}
10170     },
10171
10172     // private
10173     onEnable : function(){
10174         this.inputEl().dom.disabled = false;
10175         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10176         //if(this.wrap){
10177         //    this.el.removeClass('x-item-disabled');
10178         //}
10179     },
10180
10181     // private
10182     onShow : function(){
10183         var ae = this.getActionEl();
10184         
10185         if(ae){
10186             ae.dom.style.display = '';
10187             ae.dom.style.visibility = 'visible';
10188         }
10189     },
10190
10191     // private
10192     
10193     onHide : function(){
10194         var ae = this.getActionEl();
10195         ae.dom.style.display = 'none';
10196     },
10197
10198     /**
10199      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10200      * by an implementing function.
10201      * @method
10202      * @param {EventObject} e
10203      */
10204     onTriggerClick : Roo.emptyFn
10205 });
10206  /*
10207  * Based on:
10208  * Ext JS Library 1.1.1
10209  * Copyright(c) 2006-2007, Ext JS, LLC.
10210  *
10211  * Originally Released Under LGPL - original licence link has changed is not relivant.
10212  *
10213  * Fork - LGPL
10214  * <script type="text/javascript">
10215  */
10216
10217
10218 /**
10219  * @class Roo.data.SortTypes
10220  * @singleton
10221  * Defines the default sorting (casting?) comparison functions used when sorting data.
10222  */
10223 Roo.data.SortTypes = {
10224     /**
10225      * Default sort that does nothing
10226      * @param {Mixed} s The value being converted
10227      * @return {Mixed} The comparison value
10228      */
10229     none : function(s){
10230         return s;
10231     },
10232     
10233     /**
10234      * The regular expression used to strip tags
10235      * @type {RegExp}
10236      * @property
10237      */
10238     stripTagsRE : /<\/?[^>]+>/gi,
10239     
10240     /**
10241      * Strips all HTML tags to sort on text only
10242      * @param {Mixed} s The value being converted
10243      * @return {String} The comparison value
10244      */
10245     asText : function(s){
10246         return String(s).replace(this.stripTagsRE, "");
10247     },
10248     
10249     /**
10250      * Strips all HTML tags to sort on text only - Case insensitive
10251      * @param {Mixed} s The value being converted
10252      * @return {String} The comparison value
10253      */
10254     asUCText : function(s){
10255         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10256     },
10257     
10258     /**
10259      * Case insensitive string
10260      * @param {Mixed} s The value being converted
10261      * @return {String} The comparison value
10262      */
10263     asUCString : function(s) {
10264         return String(s).toUpperCase();
10265     },
10266     
10267     /**
10268      * Date sorting
10269      * @param {Mixed} s The value being converted
10270      * @return {Number} The comparison value
10271      */
10272     asDate : function(s) {
10273         if(!s){
10274             return 0;
10275         }
10276         if(s instanceof Date){
10277             return s.getTime();
10278         }
10279         return Date.parse(String(s));
10280     },
10281     
10282     /**
10283      * Float sorting
10284      * @param {Mixed} s The value being converted
10285      * @return {Float} The comparison value
10286      */
10287     asFloat : function(s) {
10288         var val = parseFloat(String(s).replace(/,/g, ""));
10289         if(isNaN(val)) {
10290             val = 0;
10291         }
10292         return val;
10293     },
10294     
10295     /**
10296      * Integer sorting
10297      * @param {Mixed} s The value being converted
10298      * @return {Number} The comparison value
10299      */
10300     asInt : function(s) {
10301         var val = parseInt(String(s).replace(/,/g, ""));
10302         if(isNaN(val)) {
10303             val = 0;
10304         }
10305         return val;
10306     }
10307 };/*
10308  * Based on:
10309  * Ext JS Library 1.1.1
10310  * Copyright(c) 2006-2007, Ext JS, LLC.
10311  *
10312  * Originally Released Under LGPL - original licence link has changed is not relivant.
10313  *
10314  * Fork - LGPL
10315  * <script type="text/javascript">
10316  */
10317
10318 /**
10319 * @class Roo.data.Record
10320  * Instances of this class encapsulate both record <em>definition</em> information, and record
10321  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10322  * to access Records cached in an {@link Roo.data.Store} object.<br>
10323  * <p>
10324  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10325  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10326  * objects.<br>
10327  * <p>
10328  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10329  * @constructor
10330  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10331  * {@link #create}. The parameters are the same.
10332  * @param {Array} data An associative Array of data values keyed by the field name.
10333  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10334  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10335  * not specified an integer id is generated.
10336  */
10337 Roo.data.Record = function(data, id){
10338     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10339     this.data = data;
10340 };
10341
10342 /**
10343  * Generate a constructor for a specific record layout.
10344  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10345  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10346  * Each field definition object may contain the following properties: <ul>
10347  * <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,
10348  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10349  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10350  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10351  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10352  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10353  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10354  * this may be omitted.</p></li>
10355  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10356  * <ul><li>auto (Default, implies no conversion)</li>
10357  * <li>string</li>
10358  * <li>int</li>
10359  * <li>float</li>
10360  * <li>boolean</li>
10361  * <li>date</li></ul></p></li>
10362  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10363  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10364  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10365  * by the Reader into an object that will be stored in the Record. It is passed the
10366  * following parameters:<ul>
10367  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10368  * </ul></p></li>
10369  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10370  * </ul>
10371  * <br>usage:<br><pre><code>
10372 var TopicRecord = Roo.data.Record.create(
10373     {name: 'title', mapping: 'topic_title'},
10374     {name: 'author', mapping: 'username'},
10375     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10376     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10377     {name: 'lastPoster', mapping: 'user2'},
10378     {name: 'excerpt', mapping: 'post_text'}
10379 );
10380
10381 var myNewRecord = new TopicRecord({
10382     title: 'Do my job please',
10383     author: 'noobie',
10384     totalPosts: 1,
10385     lastPost: new Date(),
10386     lastPoster: 'Animal',
10387     excerpt: 'No way dude!'
10388 });
10389 myStore.add(myNewRecord);
10390 </code></pre>
10391  * @method create
10392  * @static
10393  */
10394 Roo.data.Record.create = function(o){
10395     var f = function(){
10396         f.superclass.constructor.apply(this, arguments);
10397     };
10398     Roo.extend(f, Roo.data.Record);
10399     var p = f.prototype;
10400     p.fields = new Roo.util.MixedCollection(false, function(field){
10401         return field.name;
10402     });
10403     for(var i = 0, len = o.length; i < len; i++){
10404         p.fields.add(new Roo.data.Field(o[i]));
10405     }
10406     f.getField = function(name){
10407         return p.fields.get(name);  
10408     };
10409     return f;
10410 };
10411
10412 Roo.data.Record.AUTO_ID = 1000;
10413 Roo.data.Record.EDIT = 'edit';
10414 Roo.data.Record.REJECT = 'reject';
10415 Roo.data.Record.COMMIT = 'commit';
10416
10417 Roo.data.Record.prototype = {
10418     /**
10419      * Readonly flag - true if this record has been modified.
10420      * @type Boolean
10421      */
10422     dirty : false,
10423     editing : false,
10424     error: null,
10425     modified: null,
10426
10427     // private
10428     join : function(store){
10429         this.store = store;
10430     },
10431
10432     /**
10433      * Set the named field to the specified value.
10434      * @param {String} name The name of the field to set.
10435      * @param {Object} value The value to set the field to.
10436      */
10437     set : function(name, value){
10438         if(this.data[name] == value){
10439             return;
10440         }
10441         this.dirty = true;
10442         if(!this.modified){
10443             this.modified = {};
10444         }
10445         if(typeof this.modified[name] == 'undefined'){
10446             this.modified[name] = this.data[name];
10447         }
10448         this.data[name] = value;
10449         if(!this.editing && this.store){
10450             this.store.afterEdit(this);
10451         }       
10452     },
10453
10454     /**
10455      * Get the value of the named field.
10456      * @param {String} name The name of the field to get the value of.
10457      * @return {Object} The value of the field.
10458      */
10459     get : function(name){
10460         return this.data[name]; 
10461     },
10462
10463     // private
10464     beginEdit : function(){
10465         this.editing = true;
10466         this.modified = {}; 
10467     },
10468
10469     // private
10470     cancelEdit : function(){
10471         this.editing = false;
10472         delete this.modified;
10473     },
10474
10475     // private
10476     endEdit : function(){
10477         this.editing = false;
10478         if(this.dirty && this.store){
10479             this.store.afterEdit(this);
10480         }
10481     },
10482
10483     /**
10484      * Usually called by the {@link Roo.data.Store} which owns the Record.
10485      * Rejects all changes made to the Record since either creation, or the last commit operation.
10486      * Modified fields are reverted to their original values.
10487      * <p>
10488      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10489      * of reject operations.
10490      */
10491     reject : function(){
10492         var m = this.modified;
10493         for(var n in m){
10494             if(typeof m[n] != "function"){
10495                 this.data[n] = m[n];
10496             }
10497         }
10498         this.dirty = false;
10499         delete this.modified;
10500         this.editing = false;
10501         if(this.store){
10502             this.store.afterReject(this);
10503         }
10504     },
10505
10506     /**
10507      * Usually called by the {@link Roo.data.Store} which owns the Record.
10508      * Commits all changes made to the Record since either creation, or the last commit operation.
10509      * <p>
10510      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10511      * of commit operations.
10512      */
10513     commit : function(){
10514         this.dirty = false;
10515         delete this.modified;
10516         this.editing = false;
10517         if(this.store){
10518             this.store.afterCommit(this);
10519         }
10520     },
10521
10522     // private
10523     hasError : function(){
10524         return this.error != null;
10525     },
10526
10527     // private
10528     clearError : function(){
10529         this.error = null;
10530     },
10531
10532     /**
10533      * Creates a copy of this record.
10534      * @param {String} id (optional) A new record id if you don't want to use this record's id
10535      * @return {Record}
10536      */
10537     copy : function(newId) {
10538         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10539     }
10540 };/*
10541  * Based on:
10542  * Ext JS Library 1.1.1
10543  * Copyright(c) 2006-2007, Ext JS, LLC.
10544  *
10545  * Originally Released Under LGPL - original licence link has changed is not relivant.
10546  *
10547  * Fork - LGPL
10548  * <script type="text/javascript">
10549  */
10550
10551
10552
10553 /**
10554  * @class Roo.data.Store
10555  * @extends Roo.util.Observable
10556  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10557  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10558  * <p>
10559  * 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
10560  * has no knowledge of the format of the data returned by the Proxy.<br>
10561  * <p>
10562  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10563  * instances from the data object. These records are cached and made available through accessor functions.
10564  * @constructor
10565  * Creates a new Store.
10566  * @param {Object} config A config object containing the objects needed for the Store to access data,
10567  * and read the data into Records.
10568  */
10569 Roo.data.Store = function(config){
10570     this.data = new Roo.util.MixedCollection(false);
10571     this.data.getKey = function(o){
10572         return o.id;
10573     };
10574     this.baseParams = {};
10575     // private
10576     this.paramNames = {
10577         "start" : "start",
10578         "limit" : "limit",
10579         "sort" : "sort",
10580         "dir" : "dir",
10581         "multisort" : "_multisort"
10582     };
10583
10584     if(config && config.data){
10585         this.inlineData = config.data;
10586         delete config.data;
10587     }
10588
10589     Roo.apply(this, config);
10590     
10591     if(this.reader){ // reader passed
10592         this.reader = Roo.factory(this.reader, Roo.data);
10593         this.reader.xmodule = this.xmodule || false;
10594         if(!this.recordType){
10595             this.recordType = this.reader.recordType;
10596         }
10597         if(this.reader.onMetaChange){
10598             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10599         }
10600     }
10601
10602     if(this.recordType){
10603         this.fields = this.recordType.prototype.fields;
10604     }
10605     this.modified = [];
10606
10607     this.addEvents({
10608         /**
10609          * @event datachanged
10610          * Fires when the data cache has changed, and a widget which is using this Store
10611          * as a Record cache should refresh its view.
10612          * @param {Store} this
10613          */
10614         datachanged : true,
10615         /**
10616          * @event metachange
10617          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10618          * @param {Store} this
10619          * @param {Object} meta The JSON metadata
10620          */
10621         metachange : true,
10622         /**
10623          * @event add
10624          * Fires when Records have been added to the Store
10625          * @param {Store} this
10626          * @param {Roo.data.Record[]} records The array of Records added
10627          * @param {Number} index The index at which the record(s) were added
10628          */
10629         add : true,
10630         /**
10631          * @event remove
10632          * Fires when a Record has been removed from the Store
10633          * @param {Store} this
10634          * @param {Roo.data.Record} record The Record that was removed
10635          * @param {Number} index The index at which the record was removed
10636          */
10637         remove : true,
10638         /**
10639          * @event update
10640          * Fires when a Record has been updated
10641          * @param {Store} this
10642          * @param {Roo.data.Record} record The Record that was updated
10643          * @param {String} operation The update operation being performed.  Value may be one of:
10644          * <pre><code>
10645  Roo.data.Record.EDIT
10646  Roo.data.Record.REJECT
10647  Roo.data.Record.COMMIT
10648          * </code></pre>
10649          */
10650         update : true,
10651         /**
10652          * @event clear
10653          * Fires when the data cache has been cleared.
10654          * @param {Store} this
10655          */
10656         clear : true,
10657         /**
10658          * @event beforeload
10659          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10660          * the load action will be canceled.
10661          * @param {Store} this
10662          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10663          */
10664         beforeload : true,
10665         /**
10666          * @event beforeloadadd
10667          * Fires after a new set of Records has been loaded.
10668          * @param {Store} this
10669          * @param {Roo.data.Record[]} records The Records that were loaded
10670          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10671          */
10672         beforeloadadd : true,
10673         /**
10674          * @event load
10675          * Fires after a new set of Records has been loaded, before they are added to the store.
10676          * @param {Store} this
10677          * @param {Roo.data.Record[]} records The Records that were loaded
10678          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10679          * @params {Object} return from reader
10680          */
10681         load : true,
10682         /**
10683          * @event loadexception
10684          * Fires if an exception occurs in the Proxy during loading.
10685          * Called with the signature of the Proxy's "loadexception" event.
10686          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10687          * 
10688          * @param {Proxy} 
10689          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10690          * @param {Object} load options 
10691          * @param {Object} jsonData from your request (normally this contains the Exception)
10692          */
10693         loadexception : true
10694     });
10695     
10696     if(this.proxy){
10697         this.proxy = Roo.factory(this.proxy, Roo.data);
10698         this.proxy.xmodule = this.xmodule || false;
10699         this.relayEvents(this.proxy,  ["loadexception"]);
10700     }
10701     this.sortToggle = {};
10702     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10703
10704     Roo.data.Store.superclass.constructor.call(this);
10705
10706     if(this.inlineData){
10707         this.loadData(this.inlineData);
10708         delete this.inlineData;
10709     }
10710 };
10711
10712 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10713      /**
10714     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10715     * without a remote query - used by combo/forms at present.
10716     */
10717     
10718     /**
10719     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10720     */
10721     /**
10722     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10723     */
10724     /**
10725     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10726     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10727     */
10728     /**
10729     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10730     * on any HTTP request
10731     */
10732     /**
10733     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10734     */
10735     /**
10736     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10737     */
10738     multiSort: false,
10739     /**
10740     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10741     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10742     */
10743     remoteSort : false,
10744
10745     /**
10746     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10747      * loaded or when a record is removed. (defaults to false).
10748     */
10749     pruneModifiedRecords : false,
10750
10751     // private
10752     lastOptions : null,
10753
10754     /**
10755      * Add Records to the Store and fires the add event.
10756      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10757      */
10758     add : function(records){
10759         records = [].concat(records);
10760         for(var i = 0, len = records.length; i < len; i++){
10761             records[i].join(this);
10762         }
10763         var index = this.data.length;
10764         this.data.addAll(records);
10765         this.fireEvent("add", this, records, index);
10766     },
10767
10768     /**
10769      * Remove a Record from the Store and fires the remove event.
10770      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10771      */
10772     remove : function(record){
10773         var index = this.data.indexOf(record);
10774         this.data.removeAt(index);
10775         if(this.pruneModifiedRecords){
10776             this.modified.remove(record);
10777         }
10778         this.fireEvent("remove", this, record, index);
10779     },
10780
10781     /**
10782      * Remove all Records from the Store and fires the clear event.
10783      */
10784     removeAll : function(){
10785         this.data.clear();
10786         if(this.pruneModifiedRecords){
10787             this.modified = [];
10788         }
10789         this.fireEvent("clear", this);
10790     },
10791
10792     /**
10793      * Inserts Records to the Store at the given index and fires the add event.
10794      * @param {Number} index The start index at which to insert the passed Records.
10795      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10796      */
10797     insert : function(index, records){
10798         records = [].concat(records);
10799         for(var i = 0, len = records.length; i < len; i++){
10800             this.data.insert(index, records[i]);
10801             records[i].join(this);
10802         }
10803         this.fireEvent("add", this, records, index);
10804     },
10805
10806     /**
10807      * Get the index within the cache of the passed Record.
10808      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10809      * @return {Number} The index of the passed Record. Returns -1 if not found.
10810      */
10811     indexOf : function(record){
10812         return this.data.indexOf(record);
10813     },
10814
10815     /**
10816      * Get the index within the cache of the Record with the passed id.
10817      * @param {String} id The id of the Record to find.
10818      * @return {Number} The index of the Record. Returns -1 if not found.
10819      */
10820     indexOfId : function(id){
10821         return this.data.indexOfKey(id);
10822     },
10823
10824     /**
10825      * Get the Record with the specified id.
10826      * @param {String} id The id of the Record to find.
10827      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10828      */
10829     getById : function(id){
10830         return this.data.key(id);
10831     },
10832
10833     /**
10834      * Get the Record at the specified index.
10835      * @param {Number} index The index of the Record to find.
10836      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10837      */
10838     getAt : function(index){
10839         return this.data.itemAt(index);
10840     },
10841
10842     /**
10843      * Returns a range of Records between specified indices.
10844      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10845      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10846      * @return {Roo.data.Record[]} An array of Records
10847      */
10848     getRange : function(start, end){
10849         return this.data.getRange(start, end);
10850     },
10851
10852     // private
10853     storeOptions : function(o){
10854         o = Roo.apply({}, o);
10855         delete o.callback;
10856         delete o.scope;
10857         this.lastOptions = o;
10858     },
10859
10860     /**
10861      * Loads the Record cache from the configured Proxy using the configured Reader.
10862      * <p>
10863      * If using remote paging, then the first load call must specify the <em>start</em>
10864      * and <em>limit</em> properties in the options.params property to establish the initial
10865      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10866      * <p>
10867      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10868      * and this call will return before the new data has been loaded. Perform any post-processing
10869      * in a callback function, or in a "load" event handler.</strong>
10870      * <p>
10871      * @param {Object} options An object containing properties which control loading options:<ul>
10872      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10873      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10874      * passed the following arguments:<ul>
10875      * <li>r : Roo.data.Record[]</li>
10876      * <li>options: Options object from the load call</li>
10877      * <li>success: Boolean success indicator</li></ul></li>
10878      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10879      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10880      * </ul>
10881      */
10882     load : function(options){
10883         options = options || {};
10884         if(this.fireEvent("beforeload", this, options) !== false){
10885             this.storeOptions(options);
10886             var p = Roo.apply(options.params || {}, this.baseParams);
10887             // if meta was not loaded from remote source.. try requesting it.
10888             if (!this.reader.metaFromRemote) {
10889                 p._requestMeta = 1;
10890             }
10891             if(this.sortInfo && this.remoteSort){
10892                 var pn = this.paramNames;
10893                 p[pn["sort"]] = this.sortInfo.field;
10894                 p[pn["dir"]] = this.sortInfo.direction;
10895             }
10896             if (this.multiSort) {
10897                 var pn = this.paramNames;
10898                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10899             }
10900             
10901             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10902         }
10903     },
10904
10905     /**
10906      * Reloads the Record cache from the configured Proxy using the configured Reader and
10907      * the options from the last load operation performed.
10908      * @param {Object} options (optional) An object containing properties which may override the options
10909      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10910      * the most recently used options are reused).
10911      */
10912     reload : function(options){
10913         this.load(Roo.applyIf(options||{}, this.lastOptions));
10914     },
10915
10916     // private
10917     // Called as a callback by the Reader during a load operation.
10918     loadRecords : function(o, options, success){
10919         if(!o || success === false){
10920             if(success !== false){
10921                 this.fireEvent("load", this, [], options, o);
10922             }
10923             if(options.callback){
10924                 options.callback.call(options.scope || this, [], options, false);
10925             }
10926             return;
10927         }
10928         // if data returned failure - throw an exception.
10929         if (o.success === false) {
10930             // show a message if no listener is registered.
10931             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10932                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10933             }
10934             // loadmask wil be hooked into this..
10935             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10936             return;
10937         }
10938         var r = o.records, t = o.totalRecords || r.length;
10939         
10940         this.fireEvent("beforeloadadd", this, r, options, o);
10941         
10942         if(!options || options.add !== true){
10943             if(this.pruneModifiedRecords){
10944                 this.modified = [];
10945             }
10946             for(var i = 0, len = r.length; i < len; i++){
10947                 r[i].join(this);
10948             }
10949             if(this.snapshot){
10950                 this.data = this.snapshot;
10951                 delete this.snapshot;
10952             }
10953             this.data.clear();
10954             this.data.addAll(r);
10955             this.totalLength = t;
10956             this.applySort();
10957             this.fireEvent("datachanged", this);
10958         }else{
10959             this.totalLength = Math.max(t, this.data.length+r.length);
10960             this.add(r);
10961         }
10962         this.fireEvent("load", this, r, options, o);
10963         if(options.callback){
10964             options.callback.call(options.scope || this, r, options, true);
10965         }
10966     },
10967
10968
10969     /**
10970      * Loads data from a passed data block. A Reader which understands the format of the data
10971      * must have been configured in the constructor.
10972      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10973      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10974      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10975      */
10976     loadData : function(o, append){
10977         var r = this.reader.readRecords(o);
10978         this.loadRecords(r, {add: append}, true);
10979     },
10980
10981     /**
10982      * Gets the number of cached records.
10983      * <p>
10984      * <em>If using paging, this may not be the total size of the dataset. If the data object
10985      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10986      * the data set size</em>
10987      */
10988     getCount : function(){
10989         return this.data.length || 0;
10990     },
10991
10992     /**
10993      * Gets the total number of records in the dataset as returned by the server.
10994      * <p>
10995      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10996      * the dataset size</em>
10997      */
10998     getTotalCount : function(){
10999         return this.totalLength || 0;
11000     },
11001
11002     /**
11003      * Returns the sort state of the Store as an object with two properties:
11004      * <pre><code>
11005  field {String} The name of the field by which the Records are sorted
11006  direction {String} The sort order, "ASC" or "DESC"
11007      * </code></pre>
11008      */
11009     getSortState : function(){
11010         return this.sortInfo;
11011     },
11012
11013     // private
11014     applySort : function(){
11015         if(this.sortInfo && !this.remoteSort){
11016             var s = this.sortInfo, f = s.field;
11017             var st = this.fields.get(f).sortType;
11018             var fn = function(r1, r2){
11019                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11020                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11021             };
11022             this.data.sort(s.direction, fn);
11023             if(this.snapshot && this.snapshot != this.data){
11024                 this.snapshot.sort(s.direction, fn);
11025             }
11026         }
11027     },
11028
11029     /**
11030      * Sets the default sort column and order to be used by the next load operation.
11031      * @param {String} fieldName The name of the field to sort by.
11032      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11033      */
11034     setDefaultSort : function(field, dir){
11035         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11036     },
11037
11038     /**
11039      * Sort the Records.
11040      * If remote sorting is used, the sort is performed on the server, and the cache is
11041      * reloaded. If local sorting is used, the cache is sorted internally.
11042      * @param {String} fieldName The name of the field to sort by.
11043      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11044      */
11045     sort : function(fieldName, dir){
11046         var f = this.fields.get(fieldName);
11047         if(!dir){
11048             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11049             
11050             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11051                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11052             }else{
11053                 dir = f.sortDir;
11054             }
11055         }
11056         this.sortToggle[f.name] = dir;
11057         this.sortInfo = {field: f.name, direction: dir};
11058         if(!this.remoteSort){
11059             this.applySort();
11060             this.fireEvent("datachanged", this);
11061         }else{
11062             this.load(this.lastOptions);
11063         }
11064     },
11065
11066     /**
11067      * Calls the specified function for each of the Records in the cache.
11068      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11069      * Returning <em>false</em> aborts and exits the iteration.
11070      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11071      */
11072     each : function(fn, scope){
11073         this.data.each(fn, scope);
11074     },
11075
11076     /**
11077      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11078      * (e.g., during paging).
11079      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11080      */
11081     getModifiedRecords : function(){
11082         return this.modified;
11083     },
11084
11085     // private
11086     createFilterFn : function(property, value, anyMatch){
11087         if(!value.exec){ // not a regex
11088             value = String(value);
11089             if(value.length == 0){
11090                 return false;
11091             }
11092             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11093         }
11094         return function(r){
11095             return value.test(r.data[property]);
11096         };
11097     },
11098
11099     /**
11100      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11101      * @param {String} property A field on your records
11102      * @param {Number} start The record index to start at (defaults to 0)
11103      * @param {Number} end The last record index to include (defaults to length - 1)
11104      * @return {Number} The sum
11105      */
11106     sum : function(property, start, end){
11107         var rs = this.data.items, v = 0;
11108         start = start || 0;
11109         end = (end || end === 0) ? end : rs.length-1;
11110
11111         for(var i = start; i <= end; i++){
11112             v += (rs[i].data[property] || 0);
11113         }
11114         return v;
11115     },
11116
11117     /**
11118      * Filter the records by a specified property.
11119      * @param {String} field A field on your records
11120      * @param {String/RegExp} value Either a string that the field
11121      * should start with or a RegExp to test against the field
11122      * @param {Boolean} anyMatch True to match any part not just the beginning
11123      */
11124     filter : function(property, value, anyMatch){
11125         var fn = this.createFilterFn(property, value, anyMatch);
11126         return fn ? this.filterBy(fn) : this.clearFilter();
11127     },
11128
11129     /**
11130      * Filter by a function. The specified function will be called with each
11131      * record in this data source. If the function returns true the record is included,
11132      * otherwise it is filtered.
11133      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11134      * @param {Object} scope (optional) The scope of the function (defaults to this)
11135      */
11136     filterBy : function(fn, scope){
11137         this.snapshot = this.snapshot || this.data;
11138         this.data = this.queryBy(fn, scope||this);
11139         this.fireEvent("datachanged", this);
11140     },
11141
11142     /**
11143      * Query the records by a specified property.
11144      * @param {String} field A field on your records
11145      * @param {String/RegExp} value Either a string that the field
11146      * should start with or a RegExp to test against the field
11147      * @param {Boolean} anyMatch True to match any part not just the beginning
11148      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11149      */
11150     query : function(property, value, anyMatch){
11151         var fn = this.createFilterFn(property, value, anyMatch);
11152         return fn ? this.queryBy(fn) : this.data.clone();
11153     },
11154
11155     /**
11156      * Query by a function. The specified function will be called with each
11157      * record in this data source. If the function returns true the record is included
11158      * in the results.
11159      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11160      * @param {Object} scope (optional) The scope of the function (defaults to this)
11161       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11162      **/
11163     queryBy : function(fn, scope){
11164         var data = this.snapshot || this.data;
11165         return data.filterBy(fn, scope||this);
11166     },
11167
11168     /**
11169      * Collects unique values for a particular dataIndex from this store.
11170      * @param {String} dataIndex The property to collect
11171      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11172      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11173      * @return {Array} An array of the unique values
11174      **/
11175     collect : function(dataIndex, allowNull, bypassFilter){
11176         var d = (bypassFilter === true && this.snapshot) ?
11177                 this.snapshot.items : this.data.items;
11178         var v, sv, r = [], l = {};
11179         for(var i = 0, len = d.length; i < len; i++){
11180             v = d[i].data[dataIndex];
11181             sv = String(v);
11182             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11183                 l[sv] = true;
11184                 r[r.length] = v;
11185             }
11186         }
11187         return r;
11188     },
11189
11190     /**
11191      * Revert to a view of the Record cache with no filtering applied.
11192      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11193      */
11194     clearFilter : function(suppressEvent){
11195         if(this.snapshot && this.snapshot != this.data){
11196             this.data = this.snapshot;
11197             delete this.snapshot;
11198             if(suppressEvent !== true){
11199                 this.fireEvent("datachanged", this);
11200             }
11201         }
11202     },
11203
11204     // private
11205     afterEdit : function(record){
11206         if(this.modified.indexOf(record) == -1){
11207             this.modified.push(record);
11208         }
11209         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11210     },
11211     
11212     // private
11213     afterReject : function(record){
11214         this.modified.remove(record);
11215         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11216     },
11217
11218     // private
11219     afterCommit : function(record){
11220         this.modified.remove(record);
11221         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11222     },
11223
11224     /**
11225      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11226      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11227      */
11228     commitChanges : function(){
11229         var m = this.modified.slice(0);
11230         this.modified = [];
11231         for(var i = 0, len = m.length; i < len; i++){
11232             m[i].commit();
11233         }
11234     },
11235
11236     /**
11237      * Cancel outstanding changes on all changed records.
11238      */
11239     rejectChanges : function(){
11240         var m = this.modified.slice(0);
11241         this.modified = [];
11242         for(var i = 0, len = m.length; i < len; i++){
11243             m[i].reject();
11244         }
11245     },
11246
11247     onMetaChange : function(meta, rtype, o){
11248         this.recordType = rtype;
11249         this.fields = rtype.prototype.fields;
11250         delete this.snapshot;
11251         this.sortInfo = meta.sortInfo || this.sortInfo;
11252         this.modified = [];
11253         this.fireEvent('metachange', this, this.reader.meta);
11254     },
11255     
11256     moveIndex : function(data, type)
11257     {
11258         var index = this.indexOf(data);
11259         
11260         var newIndex = index + type;
11261         
11262         this.remove(data);
11263         
11264         this.insert(newIndex, data);
11265         
11266     }
11267 });/*
11268  * Based on:
11269  * Ext JS Library 1.1.1
11270  * Copyright(c) 2006-2007, Ext JS, LLC.
11271  *
11272  * Originally Released Under LGPL - original licence link has changed is not relivant.
11273  *
11274  * Fork - LGPL
11275  * <script type="text/javascript">
11276  */
11277
11278 /**
11279  * @class Roo.data.SimpleStore
11280  * @extends Roo.data.Store
11281  * Small helper class to make creating Stores from Array data easier.
11282  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11283  * @cfg {Array} fields An array of field definition objects, or field name strings.
11284  * @cfg {Array} data The multi-dimensional array of data
11285  * @constructor
11286  * @param {Object} config
11287  */
11288 Roo.data.SimpleStore = function(config){
11289     Roo.data.SimpleStore.superclass.constructor.call(this, {
11290         isLocal : true,
11291         reader: new Roo.data.ArrayReader({
11292                 id: config.id
11293             },
11294             Roo.data.Record.create(config.fields)
11295         ),
11296         proxy : new Roo.data.MemoryProxy(config.data)
11297     });
11298     this.load();
11299 };
11300 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11301  * Based on:
11302  * Ext JS Library 1.1.1
11303  * Copyright(c) 2006-2007, Ext JS, LLC.
11304  *
11305  * Originally Released Under LGPL - original licence link has changed is not relivant.
11306  *
11307  * Fork - LGPL
11308  * <script type="text/javascript">
11309  */
11310
11311 /**
11312 /**
11313  * @extends Roo.data.Store
11314  * @class Roo.data.JsonStore
11315  * Small helper class to make creating Stores for JSON data easier. <br/>
11316 <pre><code>
11317 var store = new Roo.data.JsonStore({
11318     url: 'get-images.php',
11319     root: 'images',
11320     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11321 });
11322 </code></pre>
11323  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11324  * JsonReader and HttpProxy (unless inline data is provided).</b>
11325  * @cfg {Array} fields An array of field definition objects, or field name strings.
11326  * @constructor
11327  * @param {Object} config
11328  */
11329 Roo.data.JsonStore = function(c){
11330     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11331         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11332         reader: new Roo.data.JsonReader(c, c.fields)
11333     }));
11334 };
11335 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11336  * Based on:
11337  * Ext JS Library 1.1.1
11338  * Copyright(c) 2006-2007, Ext JS, LLC.
11339  *
11340  * Originally Released Under LGPL - original licence link has changed is not relivant.
11341  *
11342  * Fork - LGPL
11343  * <script type="text/javascript">
11344  */
11345
11346  
11347 Roo.data.Field = function(config){
11348     if(typeof config == "string"){
11349         config = {name: config};
11350     }
11351     Roo.apply(this, config);
11352     
11353     if(!this.type){
11354         this.type = "auto";
11355     }
11356     
11357     var st = Roo.data.SortTypes;
11358     // named sortTypes are supported, here we look them up
11359     if(typeof this.sortType == "string"){
11360         this.sortType = st[this.sortType];
11361     }
11362     
11363     // set default sortType for strings and dates
11364     if(!this.sortType){
11365         switch(this.type){
11366             case "string":
11367                 this.sortType = st.asUCString;
11368                 break;
11369             case "date":
11370                 this.sortType = st.asDate;
11371                 break;
11372             default:
11373                 this.sortType = st.none;
11374         }
11375     }
11376
11377     // define once
11378     var stripRe = /[\$,%]/g;
11379
11380     // prebuilt conversion function for this field, instead of
11381     // switching every time we're reading a value
11382     if(!this.convert){
11383         var cv, dateFormat = this.dateFormat;
11384         switch(this.type){
11385             case "":
11386             case "auto":
11387             case undefined:
11388                 cv = function(v){ return v; };
11389                 break;
11390             case "string":
11391                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11392                 break;
11393             case "int":
11394                 cv = function(v){
11395                     return v !== undefined && v !== null && v !== '' ?
11396                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11397                     };
11398                 break;
11399             case "float":
11400                 cv = function(v){
11401                     return v !== undefined && v !== null && v !== '' ?
11402                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11403                     };
11404                 break;
11405             case "bool":
11406             case "boolean":
11407                 cv = function(v){ return v === true || v === "true" || v == 1; };
11408                 break;
11409             case "date":
11410                 cv = function(v){
11411                     if(!v){
11412                         return '';
11413                     }
11414                     if(v instanceof Date){
11415                         return v;
11416                     }
11417                     if(dateFormat){
11418                         if(dateFormat == "timestamp"){
11419                             return new Date(v*1000);
11420                         }
11421                         return Date.parseDate(v, dateFormat);
11422                     }
11423                     var parsed = Date.parse(v);
11424                     return parsed ? new Date(parsed) : null;
11425                 };
11426              break;
11427             
11428         }
11429         this.convert = cv;
11430     }
11431 };
11432
11433 Roo.data.Field.prototype = {
11434     dateFormat: null,
11435     defaultValue: "",
11436     mapping: null,
11437     sortType : null,
11438     sortDir : "ASC"
11439 };/*
11440  * Based on:
11441  * Ext JS Library 1.1.1
11442  * Copyright(c) 2006-2007, Ext JS, LLC.
11443  *
11444  * Originally Released Under LGPL - original licence link has changed is not relivant.
11445  *
11446  * Fork - LGPL
11447  * <script type="text/javascript">
11448  */
11449  
11450 // Base class for reading structured data from a data source.  This class is intended to be
11451 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11452
11453 /**
11454  * @class Roo.data.DataReader
11455  * Base class for reading structured data from a data source.  This class is intended to be
11456  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11457  */
11458
11459 Roo.data.DataReader = function(meta, recordType){
11460     
11461     this.meta = meta;
11462     
11463     this.recordType = recordType instanceof Array ? 
11464         Roo.data.Record.create(recordType) : recordType;
11465 };
11466
11467 Roo.data.DataReader.prototype = {
11468      /**
11469      * Create an empty record
11470      * @param {Object} data (optional) - overlay some values
11471      * @return {Roo.data.Record} record created.
11472      */
11473     newRow :  function(d) {
11474         var da =  {};
11475         this.recordType.prototype.fields.each(function(c) {
11476             switch( c.type) {
11477                 case 'int' : da[c.name] = 0; break;
11478                 case 'date' : da[c.name] = new Date(); break;
11479                 case 'float' : da[c.name] = 0.0; break;
11480                 case 'boolean' : da[c.name] = false; break;
11481                 default : da[c.name] = ""; break;
11482             }
11483             
11484         });
11485         return new this.recordType(Roo.apply(da, d));
11486     }
11487     
11488 };/*
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  * @class Roo.data.DataProxy
11501  * @extends Roo.data.Observable
11502  * This class is an abstract base class for implementations which provide retrieval of
11503  * unformatted data objects.<br>
11504  * <p>
11505  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11506  * (of the appropriate type which knows how to parse the data object) to provide a block of
11507  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11508  * <p>
11509  * Custom implementations must implement the load method as described in
11510  * {@link Roo.data.HttpProxy#load}.
11511  */
11512 Roo.data.DataProxy = function(){
11513     this.addEvents({
11514         /**
11515          * @event beforeload
11516          * Fires before a network request is made to retrieve a data object.
11517          * @param {Object} This DataProxy object.
11518          * @param {Object} params The params parameter to the load function.
11519          */
11520         beforeload : true,
11521         /**
11522          * @event load
11523          * Fires before the load method's callback is called.
11524          * @param {Object} This DataProxy object.
11525          * @param {Object} o The data object.
11526          * @param {Object} arg The callback argument object passed to the load function.
11527          */
11528         load : true,
11529         /**
11530          * @event loadexception
11531          * Fires if an Exception occurs during data retrieval.
11532          * @param {Object} This DataProxy object.
11533          * @param {Object} o The data object.
11534          * @param {Object} arg The callback argument object passed to the load function.
11535          * @param {Object} e The Exception.
11536          */
11537         loadexception : true
11538     });
11539     Roo.data.DataProxy.superclass.constructor.call(this);
11540 };
11541
11542 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11543
11544     /**
11545      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11546      */
11547 /*
11548  * Based on:
11549  * Ext JS Library 1.1.1
11550  * Copyright(c) 2006-2007, Ext JS, LLC.
11551  *
11552  * Originally Released Under LGPL - original licence link has changed is not relivant.
11553  *
11554  * Fork - LGPL
11555  * <script type="text/javascript">
11556  */
11557 /**
11558  * @class Roo.data.MemoryProxy
11559  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11560  * to the Reader when its load method is called.
11561  * @constructor
11562  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11563  */
11564 Roo.data.MemoryProxy = function(data){
11565     if (data.data) {
11566         data = data.data;
11567     }
11568     Roo.data.MemoryProxy.superclass.constructor.call(this);
11569     this.data = data;
11570 };
11571
11572 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11573     
11574     /**
11575      * Load data from the requested source (in this case an in-memory
11576      * data object passed to the constructor), read the data object into
11577      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11578      * process that block using the passed callback.
11579      * @param {Object} params This parameter is not used by the MemoryProxy class.
11580      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11581      * object into a block of Roo.data.Records.
11582      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11583      * The function must be passed <ul>
11584      * <li>The Record block object</li>
11585      * <li>The "arg" argument from the load function</li>
11586      * <li>A boolean success indicator</li>
11587      * </ul>
11588      * @param {Object} scope The scope in which to call the callback
11589      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11590      */
11591     load : function(params, reader, callback, scope, arg){
11592         params = params || {};
11593         var result;
11594         try {
11595             result = reader.readRecords(this.data);
11596         }catch(e){
11597             this.fireEvent("loadexception", this, arg, null, e);
11598             callback.call(scope, null, arg, false);
11599             return;
11600         }
11601         callback.call(scope, result, arg, true);
11602     },
11603     
11604     // private
11605     update : function(params, records){
11606         
11607     }
11608 });/*
11609  * Based on:
11610  * Ext JS Library 1.1.1
11611  * Copyright(c) 2006-2007, Ext JS, LLC.
11612  *
11613  * Originally Released Under LGPL - original licence link has changed is not relivant.
11614  *
11615  * Fork - LGPL
11616  * <script type="text/javascript">
11617  */
11618 /**
11619  * @class Roo.data.HttpProxy
11620  * @extends Roo.data.DataProxy
11621  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11622  * configured to reference a certain URL.<br><br>
11623  * <p>
11624  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11625  * from which the running page was served.<br><br>
11626  * <p>
11627  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11628  * <p>
11629  * Be aware that to enable the browser to parse an XML document, the server must set
11630  * the Content-Type header in the HTTP response to "text/xml".
11631  * @constructor
11632  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11633  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11634  * will be used to make the request.
11635  */
11636 Roo.data.HttpProxy = function(conn){
11637     Roo.data.HttpProxy.superclass.constructor.call(this);
11638     // is conn a conn config or a real conn?
11639     this.conn = conn;
11640     this.useAjax = !conn || !conn.events;
11641   
11642 };
11643
11644 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11645     // thse are take from connection...
11646     
11647     /**
11648      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11649      */
11650     /**
11651      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11652      * extra parameters to each request made by this object. (defaults to undefined)
11653      */
11654     /**
11655      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11656      *  to each request made by this object. (defaults to undefined)
11657      */
11658     /**
11659      * @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)
11660      */
11661     /**
11662      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11663      */
11664      /**
11665      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11666      * @type Boolean
11667      */
11668   
11669
11670     /**
11671      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11672      * @type Boolean
11673      */
11674     /**
11675      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11676      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11677      * a finer-grained basis than the DataProxy events.
11678      */
11679     getConnection : function(){
11680         return this.useAjax ? Roo.Ajax : this.conn;
11681     },
11682
11683     /**
11684      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11685      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11686      * process that block using the passed callback.
11687      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11688      * for the request to the remote server.
11689      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11690      * object into a block of Roo.data.Records.
11691      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11692      * The function must be passed <ul>
11693      * <li>The Record block object</li>
11694      * <li>The "arg" argument from the load function</li>
11695      * <li>A boolean success indicator</li>
11696      * </ul>
11697      * @param {Object} scope The scope in which to call the callback
11698      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11699      */
11700     load : function(params, reader, callback, scope, arg){
11701         if(this.fireEvent("beforeload", this, params) !== false){
11702             var  o = {
11703                 params : params || {},
11704                 request: {
11705                     callback : callback,
11706                     scope : scope,
11707                     arg : arg
11708                 },
11709                 reader: reader,
11710                 callback : this.loadResponse,
11711                 scope: this
11712             };
11713             if(this.useAjax){
11714                 Roo.applyIf(o, this.conn);
11715                 if(this.activeRequest){
11716                     Roo.Ajax.abort(this.activeRequest);
11717                 }
11718                 this.activeRequest = Roo.Ajax.request(o);
11719             }else{
11720                 this.conn.request(o);
11721             }
11722         }else{
11723             callback.call(scope||this, null, arg, false);
11724         }
11725     },
11726
11727     // private
11728     loadResponse : function(o, success, response){
11729         delete this.activeRequest;
11730         if(!success){
11731             this.fireEvent("loadexception", this, o, response);
11732             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11733             return;
11734         }
11735         var result;
11736         try {
11737             result = o.reader.read(response);
11738         }catch(e){
11739             this.fireEvent("loadexception", this, o, response, e);
11740             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11741             return;
11742         }
11743         
11744         this.fireEvent("load", this, o, o.request.arg);
11745         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11746     },
11747
11748     // private
11749     update : function(dataSet){
11750
11751     },
11752
11753     // private
11754     updateResponse : function(dataSet){
11755
11756     }
11757 });/*
11758  * Based on:
11759  * Ext JS Library 1.1.1
11760  * Copyright(c) 2006-2007, Ext JS, LLC.
11761  *
11762  * Originally Released Under LGPL - original licence link has changed is not relivant.
11763  *
11764  * Fork - LGPL
11765  * <script type="text/javascript">
11766  */
11767
11768 /**
11769  * @class Roo.data.ScriptTagProxy
11770  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11771  * other than the originating domain of the running page.<br><br>
11772  * <p>
11773  * <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
11774  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11775  * <p>
11776  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11777  * source code that is used as the source inside a &lt;script> tag.<br><br>
11778  * <p>
11779  * In order for the browser to process the returned data, the server must wrap the data object
11780  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11781  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11782  * depending on whether the callback name was passed:
11783  * <p>
11784  * <pre><code>
11785 boolean scriptTag = false;
11786 String cb = request.getParameter("callback");
11787 if (cb != null) {
11788     scriptTag = true;
11789     response.setContentType("text/javascript");
11790 } else {
11791     response.setContentType("application/x-json");
11792 }
11793 Writer out = response.getWriter();
11794 if (scriptTag) {
11795     out.write(cb + "(");
11796 }
11797 out.print(dataBlock.toJsonString());
11798 if (scriptTag) {
11799     out.write(");");
11800 }
11801 </pre></code>
11802  *
11803  * @constructor
11804  * @param {Object} config A configuration object.
11805  */
11806 Roo.data.ScriptTagProxy = function(config){
11807     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11808     Roo.apply(this, config);
11809     this.head = document.getElementsByTagName("head")[0];
11810 };
11811
11812 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11813
11814 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11815     /**
11816      * @cfg {String} url The URL from which to request the data object.
11817      */
11818     /**
11819      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11820      */
11821     timeout : 30000,
11822     /**
11823      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11824      * the server the name of the callback function set up by the load call to process the returned data object.
11825      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11826      * javascript output which calls this named function passing the data object as its only parameter.
11827      */
11828     callbackParam : "callback",
11829     /**
11830      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11831      * name to the request.
11832      */
11833     nocache : true,
11834
11835     /**
11836      * Load data from the configured URL, read the data object into
11837      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11838      * process that block using the passed callback.
11839      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11840      * for the request to the remote server.
11841      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11842      * object into a block of Roo.data.Records.
11843      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11844      * The function must be passed <ul>
11845      * <li>The Record block object</li>
11846      * <li>The "arg" argument from the load function</li>
11847      * <li>A boolean success indicator</li>
11848      * </ul>
11849      * @param {Object} scope The scope in which to call the callback
11850      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11851      */
11852     load : function(params, reader, callback, scope, arg){
11853         if(this.fireEvent("beforeload", this, params) !== false){
11854
11855             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11856
11857             var url = this.url;
11858             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11859             if(this.nocache){
11860                 url += "&_dc=" + (new Date().getTime());
11861             }
11862             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11863             var trans = {
11864                 id : transId,
11865                 cb : "stcCallback"+transId,
11866                 scriptId : "stcScript"+transId,
11867                 params : params,
11868                 arg : arg,
11869                 url : url,
11870                 callback : callback,
11871                 scope : scope,
11872                 reader : reader
11873             };
11874             var conn = this;
11875
11876             window[trans.cb] = function(o){
11877                 conn.handleResponse(o, trans);
11878             };
11879
11880             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11881
11882             if(this.autoAbort !== false){
11883                 this.abort();
11884             }
11885
11886             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11887
11888             var script = document.createElement("script");
11889             script.setAttribute("src", url);
11890             script.setAttribute("type", "text/javascript");
11891             script.setAttribute("id", trans.scriptId);
11892             this.head.appendChild(script);
11893
11894             this.trans = trans;
11895         }else{
11896             callback.call(scope||this, null, arg, false);
11897         }
11898     },
11899
11900     // private
11901     isLoading : function(){
11902         return this.trans ? true : false;
11903     },
11904
11905     /**
11906      * Abort the current server request.
11907      */
11908     abort : function(){
11909         if(this.isLoading()){
11910             this.destroyTrans(this.trans);
11911         }
11912     },
11913
11914     // private
11915     destroyTrans : function(trans, isLoaded){
11916         this.head.removeChild(document.getElementById(trans.scriptId));
11917         clearTimeout(trans.timeoutId);
11918         if(isLoaded){
11919             window[trans.cb] = undefined;
11920             try{
11921                 delete window[trans.cb];
11922             }catch(e){}
11923         }else{
11924             // if hasn't been loaded, wait for load to remove it to prevent script error
11925             window[trans.cb] = function(){
11926                 window[trans.cb] = undefined;
11927                 try{
11928                     delete window[trans.cb];
11929                 }catch(e){}
11930             };
11931         }
11932     },
11933
11934     // private
11935     handleResponse : function(o, trans){
11936         this.trans = false;
11937         this.destroyTrans(trans, true);
11938         var result;
11939         try {
11940             result = trans.reader.readRecords(o);
11941         }catch(e){
11942             this.fireEvent("loadexception", this, o, trans.arg, e);
11943             trans.callback.call(trans.scope||window, null, trans.arg, false);
11944             return;
11945         }
11946         this.fireEvent("load", this, o, trans.arg);
11947         trans.callback.call(trans.scope||window, result, trans.arg, true);
11948     },
11949
11950     // private
11951     handleFailure : function(trans){
11952         this.trans = false;
11953         this.destroyTrans(trans, false);
11954         this.fireEvent("loadexception", this, null, trans.arg);
11955         trans.callback.call(trans.scope||window, null, trans.arg, false);
11956     }
11957 });/*
11958  * Based on:
11959  * Ext JS Library 1.1.1
11960  * Copyright(c) 2006-2007, Ext JS, LLC.
11961  *
11962  * Originally Released Under LGPL - original licence link has changed is not relivant.
11963  *
11964  * Fork - LGPL
11965  * <script type="text/javascript">
11966  */
11967
11968 /**
11969  * @class Roo.data.JsonReader
11970  * @extends Roo.data.DataReader
11971  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11972  * based on mappings in a provided Roo.data.Record constructor.
11973  * 
11974  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11975  * in the reply previously. 
11976  * 
11977  * <p>
11978  * Example code:
11979  * <pre><code>
11980 var RecordDef = Roo.data.Record.create([
11981     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11982     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11983 ]);
11984 var myReader = new Roo.data.JsonReader({
11985     totalProperty: "results",    // The property which contains the total dataset size (optional)
11986     root: "rows",                // The property which contains an Array of row objects
11987     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11988 }, RecordDef);
11989 </code></pre>
11990  * <p>
11991  * This would consume a JSON file like this:
11992  * <pre><code>
11993 { 'results': 2, 'rows': [
11994     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11995     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11996 }
11997 </code></pre>
11998  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11999  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12000  * paged from the remote server.
12001  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12002  * @cfg {String} root name of the property which contains the Array of row objects.
12003  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12004  * @cfg {Array} fields Array of field definition objects
12005  * @constructor
12006  * Create a new JsonReader
12007  * @param {Object} meta Metadata configuration options
12008  * @param {Object} recordType Either an Array of field definition objects,
12009  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12010  */
12011 Roo.data.JsonReader = function(meta, recordType){
12012     
12013     meta = meta || {};
12014     // set some defaults:
12015     Roo.applyIf(meta, {
12016         totalProperty: 'total',
12017         successProperty : 'success',
12018         root : 'data',
12019         id : 'id'
12020     });
12021     
12022     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12023 };
12024 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12025     
12026     /**
12027      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12028      * Used by Store query builder to append _requestMeta to params.
12029      * 
12030      */
12031     metaFromRemote : false,
12032     /**
12033      * This method is only used by a DataProxy which has retrieved data from a remote server.
12034      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12035      * @return {Object} data A data block which is used by an Roo.data.Store object as
12036      * a cache of Roo.data.Records.
12037      */
12038     read : function(response){
12039         var json = response.responseText;
12040        
12041         var o = /* eval:var:o */ eval("("+json+")");
12042         if(!o) {
12043             throw {message: "JsonReader.read: Json object not found"};
12044         }
12045         
12046         if(o.metaData){
12047             
12048             delete this.ef;
12049             this.metaFromRemote = true;
12050             this.meta = o.metaData;
12051             this.recordType = Roo.data.Record.create(o.metaData.fields);
12052             this.onMetaChange(this.meta, this.recordType, o);
12053         }
12054         return this.readRecords(o);
12055     },
12056
12057     // private function a store will implement
12058     onMetaChange : function(meta, recordType, o){
12059
12060     },
12061
12062     /**
12063          * @ignore
12064          */
12065     simpleAccess: function(obj, subsc) {
12066         return obj[subsc];
12067     },
12068
12069         /**
12070          * @ignore
12071          */
12072     getJsonAccessor: function(){
12073         var re = /[\[\.]/;
12074         return function(expr) {
12075             try {
12076                 return(re.test(expr))
12077                     ? new Function("obj", "return obj." + expr)
12078                     : function(obj){
12079                         return obj[expr];
12080                     };
12081             } catch(e){}
12082             return Roo.emptyFn;
12083         };
12084     }(),
12085
12086     /**
12087      * Create a data block containing Roo.data.Records from an XML document.
12088      * @param {Object} o An object which contains an Array of row objects in the property specified
12089      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12090      * which contains the total size of the dataset.
12091      * @return {Object} data A data block which is used by an Roo.data.Store object as
12092      * a cache of Roo.data.Records.
12093      */
12094     readRecords : function(o){
12095         /**
12096          * After any data loads, the raw JSON data is available for further custom processing.
12097          * @type Object
12098          */
12099         this.o = o;
12100         var s = this.meta, Record = this.recordType,
12101             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12102
12103 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12104         if (!this.ef) {
12105             if(s.totalProperty) {
12106                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12107                 }
12108                 if(s.successProperty) {
12109                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12110                 }
12111                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12112                 if (s.id) {
12113                         var g = this.getJsonAccessor(s.id);
12114                         this.getId = function(rec) {
12115                                 var r = g(rec);  
12116                                 return (r === undefined || r === "") ? null : r;
12117                         };
12118                 } else {
12119                         this.getId = function(){return null;};
12120                 }
12121             this.ef = [];
12122             for(var jj = 0; jj < fl; jj++){
12123                 f = fi[jj];
12124                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12125                 this.ef[jj] = this.getJsonAccessor(map);
12126             }
12127         }
12128
12129         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12130         if(s.totalProperty){
12131             var vt = parseInt(this.getTotal(o), 10);
12132             if(!isNaN(vt)){
12133                 totalRecords = vt;
12134             }
12135         }
12136         if(s.successProperty){
12137             var vs = this.getSuccess(o);
12138             if(vs === false || vs === 'false'){
12139                 success = false;
12140             }
12141         }
12142         var records = [];
12143         for(var i = 0; i < c; i++){
12144                 var n = root[i];
12145             var values = {};
12146             var id = this.getId(n);
12147             for(var j = 0; j < fl; j++){
12148                 f = fi[j];
12149             var v = this.ef[j](n);
12150             if (!f.convert) {
12151                 Roo.log('missing convert for ' + f.name);
12152                 Roo.log(f);
12153                 continue;
12154             }
12155             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12156             }
12157             var record = new Record(values, id);
12158             record.json = n;
12159             records[i] = record;
12160         }
12161         return {
12162             raw : o,
12163             success : success,
12164             records : records,
12165             totalRecords : totalRecords
12166         };
12167     }
12168 });/*
12169  * Based on:
12170  * Ext JS Library 1.1.1
12171  * Copyright(c) 2006-2007, Ext JS, LLC.
12172  *
12173  * Originally Released Under LGPL - original licence link has changed is not relivant.
12174  *
12175  * Fork - LGPL
12176  * <script type="text/javascript">
12177  */
12178
12179 /**
12180  * @class Roo.data.ArrayReader
12181  * @extends Roo.data.DataReader
12182  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12183  * Each element of that Array represents a row of data fields. The
12184  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12185  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12186  * <p>
12187  * Example code:.
12188  * <pre><code>
12189 var RecordDef = Roo.data.Record.create([
12190     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12191     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12192 ]);
12193 var myReader = new Roo.data.ArrayReader({
12194     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12195 }, RecordDef);
12196 </code></pre>
12197  * <p>
12198  * This would consume an Array like this:
12199  * <pre><code>
12200 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12201   </code></pre>
12202  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12203  * @constructor
12204  * Create a new JsonReader
12205  * @param {Object} meta Metadata configuration options.
12206  * @param {Object} recordType Either an Array of field definition objects
12207  * as specified to {@link Roo.data.Record#create},
12208  * or an {@link Roo.data.Record} object
12209  * created using {@link Roo.data.Record#create}.
12210  */
12211 Roo.data.ArrayReader = function(meta, recordType){
12212     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12213 };
12214
12215 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12216     /**
12217      * Create a data block containing Roo.data.Records from an XML document.
12218      * @param {Object} o An Array of row objects which represents the dataset.
12219      * @return {Object} data A data block which is used by an Roo.data.Store object as
12220      * a cache of Roo.data.Records.
12221      */
12222     readRecords : function(o){
12223         var sid = this.meta ? this.meta.id : null;
12224         var recordType = this.recordType, fields = recordType.prototype.fields;
12225         var records = [];
12226         var root = o;
12227             for(var i = 0; i < root.length; i++){
12228                     var n = root[i];
12229                 var values = {};
12230                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12231                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12232                 var f = fields.items[j];
12233                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12234                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12235                 v = f.convert(v);
12236                 values[f.name] = v;
12237             }
12238                 var record = new recordType(values, id);
12239                 record.json = n;
12240                 records[records.length] = record;
12241             }
12242             return {
12243                 records : records,
12244                 totalRecords : records.length
12245             };
12246     }
12247 });/*
12248  * - LGPL
12249  * * 
12250  */
12251
12252 /**
12253  * @class Roo.bootstrap.ComboBox
12254  * @extends Roo.bootstrap.TriggerField
12255  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12256  * @cfg {Boolean} append (true|false) default false
12257  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12258  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12259  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12260  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12261  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12262  * @cfg {Boolean} animate default true
12263  * @cfg {Boolean} emptyResultText only for touch device
12264  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12265  * @constructor
12266  * Create a new ComboBox.
12267  * @param {Object} config Configuration options
12268  */
12269 Roo.bootstrap.ComboBox = function(config){
12270     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12271     this.addEvents({
12272         /**
12273          * @event expand
12274          * Fires when the dropdown list is expanded
12275              * @param {Roo.bootstrap.ComboBox} combo This combo box
12276              */
12277         'expand' : true,
12278         /**
12279          * @event collapse
12280          * Fires when the dropdown list is collapsed
12281              * @param {Roo.bootstrap.ComboBox} combo This combo box
12282              */
12283         'collapse' : true,
12284         /**
12285          * @event beforeselect
12286          * Fires before a list item is selected. Return false to cancel the selection.
12287              * @param {Roo.bootstrap.ComboBox} combo This combo box
12288              * @param {Roo.data.Record} record The data record returned from the underlying store
12289              * @param {Number} index The index of the selected item in the dropdown list
12290              */
12291         'beforeselect' : true,
12292         /**
12293          * @event select
12294          * Fires when a list item is selected
12295              * @param {Roo.bootstrap.ComboBox} combo This combo box
12296              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12297              * @param {Number} index The index of the selected item in the dropdown list
12298              */
12299         'select' : true,
12300         /**
12301          * @event beforequery
12302          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12303          * The event object passed has these properties:
12304              * @param {Roo.bootstrap.ComboBox} combo This combo box
12305              * @param {String} query The query
12306              * @param {Boolean} forceAll true to force "all" query
12307              * @param {Boolean} cancel true to cancel the query
12308              * @param {Object} e The query event object
12309              */
12310         'beforequery': true,
12311          /**
12312          * @event add
12313          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12314              * @param {Roo.bootstrap.ComboBox} combo This combo box
12315              */
12316         'add' : true,
12317         /**
12318          * @event edit
12319          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12320              * @param {Roo.bootstrap.ComboBox} combo This combo box
12321              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12322              */
12323         'edit' : true,
12324         /**
12325          * @event remove
12326          * Fires when the remove value from the combobox array
12327              * @param {Roo.bootstrap.ComboBox} combo This combo box
12328              */
12329         'remove' : true,
12330         /**
12331          * @event afterremove
12332          * Fires when the remove value from the combobox array
12333              * @param {Roo.bootstrap.ComboBox} combo This combo box
12334              */
12335         'afterremove' : true,
12336         /**
12337          * @event specialfilter
12338          * Fires when specialfilter
12339             * @param {Roo.bootstrap.ComboBox} combo This combo box
12340             */
12341         'specialfilter' : true,
12342         /**
12343          * @event tick
12344          * Fires when tick the element
12345             * @param {Roo.bootstrap.ComboBox} combo This combo box
12346             */
12347         'tick' : true,
12348         /**
12349          * @event touchviewdisplay
12350          * Fires when touch view require special display (default is using displayField)
12351             * @param {Roo.bootstrap.ComboBox} combo This combo box
12352             * @param {Object} cfg set html .
12353             */
12354         'touchviewdisplay' : true
12355         
12356     });
12357     
12358     this.item = [];
12359     this.tickItems = [];
12360     
12361     this.selectedIndex = -1;
12362     if(this.mode == 'local'){
12363         if(config.queryDelay === undefined){
12364             this.queryDelay = 10;
12365         }
12366         if(config.minChars === undefined){
12367             this.minChars = 0;
12368         }
12369     }
12370 };
12371
12372 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12373      
12374     /**
12375      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12376      * rendering into an Roo.Editor, defaults to false)
12377      */
12378     /**
12379      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12380      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12381      */
12382     /**
12383      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12384      */
12385     /**
12386      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12387      * the dropdown list (defaults to undefined, with no header element)
12388      */
12389
12390      /**
12391      * @cfg {String/Roo.Template} tpl The template to use to render the output
12392      */
12393      
12394      /**
12395      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12396      */
12397     listWidth: undefined,
12398     /**
12399      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12400      * mode = 'remote' or 'text' if mode = 'local')
12401      */
12402     displayField: undefined,
12403     
12404     /**
12405      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12406      * mode = 'remote' or 'value' if mode = 'local'). 
12407      * Note: use of a valueField requires the user make a selection
12408      * in order for a value to be mapped.
12409      */
12410     valueField: undefined,
12411     /**
12412      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12413      */
12414     modalTitle : '',
12415     
12416     /**
12417      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12418      * field's data value (defaults to the underlying DOM element's name)
12419      */
12420     hiddenName: undefined,
12421     /**
12422      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12423      */
12424     listClass: '',
12425     /**
12426      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12427      */
12428     selectedClass: 'active',
12429     
12430     /**
12431      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12432      */
12433     shadow:'sides',
12434     /**
12435      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12436      * anchor positions (defaults to 'tl-bl')
12437      */
12438     listAlign: 'tl-bl?',
12439     /**
12440      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12441      */
12442     maxHeight: 300,
12443     /**
12444      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12445      * query specified by the allQuery config option (defaults to 'query')
12446      */
12447     triggerAction: 'query',
12448     /**
12449      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12450      * (defaults to 4, does not apply if editable = false)
12451      */
12452     minChars : 4,
12453     /**
12454      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12455      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12456      */
12457     typeAhead: false,
12458     /**
12459      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12460      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12461      */
12462     queryDelay: 500,
12463     /**
12464      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12465      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12466      */
12467     pageSize: 0,
12468     /**
12469      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12470      * when editable = true (defaults to false)
12471      */
12472     selectOnFocus:false,
12473     /**
12474      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12475      */
12476     queryParam: 'query',
12477     /**
12478      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12479      * when mode = 'remote' (defaults to 'Loading...')
12480      */
12481     loadingText: 'Loading...',
12482     /**
12483      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12484      */
12485     resizable: false,
12486     /**
12487      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12488      */
12489     handleHeight : 8,
12490     /**
12491      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12492      * traditional select (defaults to true)
12493      */
12494     editable: true,
12495     /**
12496      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12497      */
12498     allQuery: '',
12499     /**
12500      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12501      */
12502     mode: 'remote',
12503     /**
12504      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12505      * listWidth has a higher value)
12506      */
12507     minListWidth : 70,
12508     /**
12509      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12510      * allow the user to set arbitrary text into the field (defaults to false)
12511      */
12512     forceSelection:false,
12513     /**
12514      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12515      * if typeAhead = true (defaults to 250)
12516      */
12517     typeAheadDelay : 250,
12518     /**
12519      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12520      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12521      */
12522     valueNotFoundText : undefined,
12523     /**
12524      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12525      */
12526     blockFocus : false,
12527     
12528     /**
12529      * @cfg {Boolean} disableClear Disable showing of clear button.
12530      */
12531     disableClear : false,
12532     /**
12533      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12534      */
12535     alwaysQuery : false,
12536     
12537     /**
12538      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12539      */
12540     multiple : false,
12541     
12542     /**
12543      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12544      */
12545     invalidClass : "has-warning",
12546     
12547     /**
12548      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12549      */
12550     validClass : "has-success",
12551     
12552     /**
12553      * @cfg {Boolean} specialFilter (true|false) special filter default false
12554      */
12555     specialFilter : false,
12556     
12557     /**
12558      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12559      */
12560     mobileTouchView : true,
12561     
12562     /**
12563      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12564      */
12565     useNativeIOS : false,
12566     
12567     ios_options : false,
12568     
12569     //private
12570     addicon : false,
12571     editicon: false,
12572     
12573     page: 0,
12574     hasQuery: false,
12575     append: false,
12576     loadNext: false,
12577     autoFocus : true,
12578     tickable : false,
12579     btnPosition : 'right',
12580     triggerList : true,
12581     showToggleBtn : true,
12582     animate : true,
12583     emptyResultText: 'Empty',
12584     triggerText : 'Select',
12585     
12586     // element that contains real text value.. (when hidden is used..)
12587     
12588     getAutoCreate : function()
12589     {
12590         var cfg = false;
12591         
12592         /*
12593          * Render classic select for iso
12594          */
12595         
12596         if(Roo.isIOS && this.useNativeIOS){
12597             cfg = this.getAutoCreateNativeIOS();
12598             return cfg;
12599         }
12600         
12601         /*
12602          * Touch Devices
12603          */
12604         
12605         if(Roo.isTouch && this.mobileTouchView){
12606             cfg = this.getAutoCreateTouchView();
12607             return cfg;;
12608         }
12609         
12610         /*
12611          *  Normal ComboBox
12612          */
12613         if(!this.tickable){
12614             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12615             if(this.name == 'info_year_invest_id_display_name'){
12616                 Roo.log('cfg.................................................');
12617                 Roo.log(cfg);
12618             }
12619             return cfg;
12620         }
12621         
12622         /*
12623          *  ComboBox with tickable selections
12624          */
12625              
12626         var align = this.labelAlign || this.parentLabelAlign();
12627         
12628         cfg = {
12629             cls : 'form-group roo-combobox-tickable' //input-group
12630         };
12631         
12632         var btn_text_select = '';
12633         var btn_text_done = '';
12634         var btn_text_cancel = '';
12635         
12636         if (this.btn_text_show) {
12637             btn_text_select = 'Select';
12638             btn_text_done = 'Done';
12639             btn_text_cancel = 'Cancel'; 
12640         }
12641         
12642         var buttons = {
12643             tag : 'div',
12644             cls : 'tickable-buttons',
12645             cn : [
12646                 {
12647                     tag : 'button',
12648                     type : 'button',
12649                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12650                     //html : this.triggerText
12651                     html: btn_text_select
12652                 },
12653                 {
12654                     tag : 'button',
12655                     type : 'button',
12656                     name : 'ok',
12657                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12658                     //html : 'Done'
12659                     html: btn_text_done
12660                 },
12661                 {
12662                     tag : 'button',
12663                     type : 'button',
12664                     name : 'cancel',
12665                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12666                     //html : 'Cancel'
12667                     html: btn_text_cancel
12668                 }
12669             ]
12670         };
12671         
12672         if(this.editable){
12673             buttons.cn.unshift({
12674                 tag: 'input',
12675                 cls: 'roo-select2-search-field-input'
12676             });
12677         }
12678         
12679         var _this = this;
12680         
12681         Roo.each(buttons.cn, function(c){
12682             if (_this.size) {
12683                 c.cls += ' btn-' + _this.size;
12684             }
12685
12686             if (_this.disabled) {
12687                 c.disabled = true;
12688             }
12689         });
12690         
12691         var box = {
12692             tag: 'div',
12693             cn: [
12694                 {
12695                     tag: 'input',
12696                     type : 'hidden',
12697                     cls: 'form-hidden-field'
12698                 },
12699                 {
12700                     tag: 'ul',
12701                     cls: 'roo-select2-choices',
12702                     cn:[
12703                         {
12704                             tag: 'li',
12705                             cls: 'roo-select2-search-field',
12706                             cn: [
12707                                 buttons
12708                             ]
12709                         }
12710                     ]
12711                 }
12712             ]
12713         };
12714         
12715         var combobox = {
12716             cls: 'roo-select2-container input-group roo-select2-container-multi',
12717             cn: [
12718                 box
12719 //                {
12720 //                    tag: 'ul',
12721 //                    cls: 'typeahead typeahead-long dropdown-menu',
12722 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12723 //                }
12724             ]
12725         };
12726         
12727         if(this.hasFeedback && !this.allowBlank){
12728             
12729             var feedback = {
12730                 tag: 'span',
12731                 cls: 'glyphicon form-control-feedback'
12732             };
12733
12734             combobox.cn.push(feedback);
12735         }
12736         
12737         
12738         if (align ==='left' && this.fieldLabel.length) {
12739             
12740             cfg.cls += ' roo-form-group-label-left';
12741             
12742             cfg.cn = [
12743                 {
12744                     tag : 'i',
12745                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12746                     tooltip : 'This field is required'
12747                 },
12748                 {
12749                     tag: 'label',
12750                     'for' :  id,
12751                     cls : 'control-label',
12752                     html : this.fieldLabel
12753
12754                 },
12755                 {
12756                     cls : "", 
12757                     cn: [
12758                         combobox
12759                     ]
12760                 }
12761
12762             ];
12763             
12764             var labelCfg = cfg.cn[1];
12765             var contentCfg = cfg.cn[2];
12766             
12767
12768             if(this.indicatorpos == 'right'){
12769                 
12770                 cfg.cn = [
12771                     {
12772                         tag: 'label',
12773                         'for' :  id,
12774                         cls : 'control-label',
12775                         html : this.fieldLabel
12776
12777                     },
12778                     {
12779                         tag : 'i',
12780                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12781                         tooltip : 'This field is required'
12782                     },
12783                     {
12784                         cls : "",
12785                         cn: [
12786                             combobox
12787                         ]
12788                     }
12789
12790                 ];
12791                 
12792                 labelCfg = cfg.cn[0];
12793                 contentCfg = cfg.cn[2];
12794             
12795             }
12796             
12797             if(this.labelWidth > 12){
12798                 labelCfg.style = "width: " + this.labelWidth + 'px';
12799             }
12800             
12801             if(this.labelWidth < 13 && this.labelmd == 0){
12802                 this.labelmd = this.labelWidth;
12803             }
12804             
12805             if(this.labellg > 0){
12806                 labelCfg.cls += ' col-lg-' + this.labellg;
12807                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12808             }
12809             
12810             if(this.labelmd > 0){
12811                 labelCfg.cls += ' col-md-' + this.labelmd;
12812                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12813             }
12814             
12815             if(this.labelsm > 0){
12816                 labelCfg.cls += ' col-sm-' + this.labelsm;
12817                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12818             }
12819             
12820             if(this.labelxs > 0){
12821                 labelCfg.cls += ' col-xs-' + this.labelxs;
12822                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12823             }
12824                 
12825                 
12826         } else if ( this.fieldLabel.length) {
12827 //                Roo.log(" label");
12828                  cfg.cn = [
12829                     {
12830                         tag : 'i',
12831                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12832                         tooltip : 'This field is required'
12833                     },
12834                     {
12835                         tag: 'label',
12836                         //cls : 'input-group-addon',
12837                         html : this.fieldLabel
12838                         
12839                     },
12840                     
12841                     combobox
12842                     
12843                 ];
12844                 
12845                 if(this.indicatorpos == 'right'){
12846                     
12847                     cfg.cn = [
12848                         {
12849                             tag: 'label',
12850                             //cls : 'input-group-addon',
12851                             html : this.fieldLabel
12852
12853                         },
12854                         
12855                         {
12856                             tag : 'i',
12857                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12858                             tooltip : 'This field is required'
12859                         },
12860                         
12861                         combobox
12862
12863                     ];
12864                 
12865                 }
12866
12867         } else {
12868             
12869 //                Roo.log(" no label && no align");
12870                 cfg = combobox
12871                      
12872                 
12873         }
12874          
12875         var settings=this;
12876         ['xs','sm','md','lg'].map(function(size){
12877             if (settings[size]) {
12878                 cfg.cls += ' col-' + size + '-' + settings[size];
12879             }
12880         });
12881         
12882         return cfg;
12883         
12884     },
12885     
12886     _initEventsCalled : false,
12887     
12888     // private
12889     initEvents: function()
12890     {   
12891         if (this._initEventsCalled) { // as we call render... prevent looping...
12892             return;
12893         }
12894         this._initEventsCalled = true;
12895         
12896         if (!this.store) {
12897             throw "can not find store for combo";
12898         }
12899         
12900         this.store = Roo.factory(this.store, Roo.data);
12901         
12902         // if we are building from html. then this element is so complex, that we can not really
12903         // use the rendered HTML.
12904         // so we have to trash and replace the previous code.
12905         if (Roo.XComponent.build_from_html) {
12906             
12907             // remove this element....
12908             var e = this.el.dom, k=0;
12909             while (e ) { e = e.previousSibling;  ++k;}
12910
12911             this.el.remove();
12912             
12913             this.el=false;
12914             this.rendered = false;
12915             
12916             this.render(this.parent().getChildContainer(true), k);
12917             
12918             
12919             
12920         }
12921         
12922         if(Roo.isIOS && this.useNativeIOS){
12923             this.initIOSView();
12924             return;
12925         }
12926         
12927         /*
12928          * Touch Devices
12929          */
12930         
12931         if(Roo.isTouch && this.mobileTouchView){
12932             this.initTouchView();
12933             return;
12934         }
12935         
12936         if(this.tickable){
12937             this.initTickableEvents();
12938             return;
12939         }
12940         
12941         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12942         
12943         if(this.hiddenName){
12944             
12945             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12946             
12947             this.hiddenField.dom.value =
12948                 this.hiddenValue !== undefined ? this.hiddenValue :
12949                 this.value !== undefined ? this.value : '';
12950
12951             // prevent input submission
12952             this.el.dom.removeAttribute('name');
12953             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12954              
12955              
12956         }
12957         //if(Roo.isGecko){
12958         //    this.el.dom.setAttribute('autocomplete', 'off');
12959         //}
12960         
12961         var cls = 'x-combo-list';
12962         
12963         //this.list = new Roo.Layer({
12964         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12965         //});
12966         
12967         var _this = this;
12968         
12969         (function(){
12970             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12971             _this.list.setWidth(lw);
12972         }).defer(100);
12973         
12974         this.list.on('mouseover', this.onViewOver, this);
12975         this.list.on('mousemove', this.onViewMove, this);
12976         
12977         this.list.on('scroll', this.onViewScroll, this);
12978         
12979         /*
12980         this.list.swallowEvent('mousewheel');
12981         this.assetHeight = 0;
12982
12983         if(this.title){
12984             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12985             this.assetHeight += this.header.getHeight();
12986         }
12987
12988         this.innerList = this.list.createChild({cls:cls+'-inner'});
12989         this.innerList.on('mouseover', this.onViewOver, this);
12990         this.innerList.on('mousemove', this.onViewMove, this);
12991         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12992         
12993         if(this.allowBlank && !this.pageSize && !this.disableClear){
12994             this.footer = this.list.createChild({cls:cls+'-ft'});
12995             this.pageTb = new Roo.Toolbar(this.footer);
12996            
12997         }
12998         if(this.pageSize){
12999             this.footer = this.list.createChild({cls:cls+'-ft'});
13000             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13001                     {pageSize: this.pageSize});
13002             
13003         }
13004         
13005         if (this.pageTb && this.allowBlank && !this.disableClear) {
13006             var _this = this;
13007             this.pageTb.add(new Roo.Toolbar.Fill(), {
13008                 cls: 'x-btn-icon x-btn-clear',
13009                 text: '&#160;',
13010                 handler: function()
13011                 {
13012                     _this.collapse();
13013                     _this.clearValue();
13014                     _this.onSelect(false, -1);
13015                 }
13016             });
13017         }
13018         if (this.footer) {
13019             this.assetHeight += this.footer.getHeight();
13020         }
13021         */
13022             
13023         if(!this.tpl){
13024             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13025         }
13026
13027         this.view = new Roo.View(this.list, this.tpl, {
13028             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13029         });
13030         //this.view.wrapEl.setDisplayed(false);
13031         this.view.on('click', this.onViewClick, this);
13032         
13033         
13034         
13035         this.store.on('beforeload', this.onBeforeLoad, this);
13036         this.store.on('load', this.onLoad, this);
13037         this.store.on('loadexception', this.onLoadException, this);
13038         /*
13039         if(this.resizable){
13040             this.resizer = new Roo.Resizable(this.list,  {
13041                pinned:true, handles:'se'
13042             });
13043             this.resizer.on('resize', function(r, w, h){
13044                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13045                 this.listWidth = w;
13046                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13047                 this.restrictHeight();
13048             }, this);
13049             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13050         }
13051         */
13052         if(!this.editable){
13053             this.editable = true;
13054             this.setEditable(false);
13055         }
13056         
13057         /*
13058         
13059         if (typeof(this.events.add.listeners) != 'undefined') {
13060             
13061             this.addicon = this.wrap.createChild(
13062                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13063        
13064             this.addicon.on('click', function(e) {
13065                 this.fireEvent('add', this);
13066             }, this);
13067         }
13068         if (typeof(this.events.edit.listeners) != 'undefined') {
13069             
13070             this.editicon = this.wrap.createChild(
13071                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13072             if (this.addicon) {
13073                 this.editicon.setStyle('margin-left', '40px');
13074             }
13075             this.editicon.on('click', function(e) {
13076                 
13077                 // we fire even  if inothing is selected..
13078                 this.fireEvent('edit', this, this.lastData );
13079                 
13080             }, this);
13081         }
13082         */
13083         
13084         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13085             "up" : function(e){
13086                 this.inKeyMode = true;
13087                 this.selectPrev();
13088             },
13089
13090             "down" : function(e){
13091                 if(!this.isExpanded()){
13092                     this.onTriggerClick();
13093                 }else{
13094                     this.inKeyMode = true;
13095                     this.selectNext();
13096                 }
13097             },
13098
13099             "enter" : function(e){
13100 //                this.onViewClick();
13101                 //return true;
13102                 this.collapse();
13103                 
13104                 if(this.fireEvent("specialkey", this, e)){
13105                     this.onViewClick(false);
13106                 }
13107                 
13108                 return true;
13109             },
13110
13111             "esc" : function(e){
13112                 this.collapse();
13113             },
13114
13115             "tab" : function(e){
13116                 this.collapse();
13117                 
13118                 if(this.fireEvent("specialkey", this, e)){
13119                     this.onViewClick(false);
13120                 }
13121                 
13122                 return true;
13123             },
13124
13125             scope : this,
13126
13127             doRelay : function(foo, bar, hname){
13128                 if(hname == 'down' || this.scope.isExpanded()){
13129                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13130                 }
13131                 return true;
13132             },
13133
13134             forceKeyDown: true
13135         });
13136         
13137         
13138         this.queryDelay = Math.max(this.queryDelay || 10,
13139                 this.mode == 'local' ? 10 : 250);
13140         
13141         
13142         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13143         
13144         if(this.typeAhead){
13145             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13146         }
13147         if(this.editable !== false){
13148             this.inputEl().on("keyup", this.onKeyUp, this);
13149         }
13150         if(this.forceSelection){
13151             this.inputEl().on('blur', this.doForce, this);
13152         }
13153         
13154         if(this.multiple){
13155             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13156             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13157         }
13158     },
13159     
13160     initTickableEvents: function()
13161     {   
13162         this.createList();
13163         
13164         if(this.hiddenName){
13165             
13166             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13167             
13168             this.hiddenField.dom.value =
13169                 this.hiddenValue !== undefined ? this.hiddenValue :
13170                 this.value !== undefined ? this.value : '';
13171
13172             // prevent input submission
13173             this.el.dom.removeAttribute('name');
13174             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13175              
13176              
13177         }
13178         
13179 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13180         
13181         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13182         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13183         if(this.triggerList){
13184             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13185         }
13186          
13187         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13188         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13189         
13190         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13191         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13192         
13193         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13194         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13195         
13196         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13197         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13198         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13199         
13200         this.okBtn.hide();
13201         this.cancelBtn.hide();
13202         
13203         var _this = this;
13204         
13205         (function(){
13206             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13207             _this.list.setWidth(lw);
13208         }).defer(100);
13209         
13210         this.list.on('mouseover', this.onViewOver, this);
13211         this.list.on('mousemove', this.onViewMove, this);
13212         
13213         this.list.on('scroll', this.onViewScroll, this);
13214         
13215         if(!this.tpl){
13216             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>';
13217         }
13218
13219         this.view = new Roo.View(this.list, this.tpl, {
13220             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13221         });
13222         
13223         //this.view.wrapEl.setDisplayed(false);
13224         this.view.on('click', this.onViewClick, this);
13225         
13226         
13227         
13228         this.store.on('beforeload', this.onBeforeLoad, this);
13229         this.store.on('load', this.onLoad, this);
13230         this.store.on('loadexception', this.onLoadException, this);
13231         
13232         if(this.editable){
13233             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13234                 "up" : function(e){
13235                     this.inKeyMode = true;
13236                     this.selectPrev();
13237                 },
13238
13239                 "down" : function(e){
13240                     this.inKeyMode = true;
13241                     this.selectNext();
13242                 },
13243
13244                 "enter" : function(e){
13245                     if(this.fireEvent("specialkey", this, e)){
13246                         this.onViewClick(false);
13247                     }
13248                     
13249                     return true;
13250                 },
13251
13252                 "esc" : function(e){
13253                     this.onTickableFooterButtonClick(e, false, false);
13254                 },
13255
13256                 "tab" : function(e){
13257                     this.fireEvent("specialkey", this, e);
13258                     
13259                     this.onTickableFooterButtonClick(e, false, false);
13260                     
13261                     return true;
13262                 },
13263
13264                 scope : this,
13265
13266                 doRelay : function(e, fn, key){
13267                     if(this.scope.isExpanded()){
13268                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13269                     }
13270                     return true;
13271                 },
13272
13273                 forceKeyDown: true
13274             });
13275         }
13276         
13277         this.queryDelay = Math.max(this.queryDelay || 10,
13278                 this.mode == 'local' ? 10 : 250);
13279         
13280         
13281         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13282         
13283         if(this.typeAhead){
13284             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13285         }
13286         
13287         if(this.editable !== false){
13288             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13289         }
13290         
13291     },
13292
13293     onDestroy : function(){
13294         if(this.view){
13295             this.view.setStore(null);
13296             this.view.el.removeAllListeners();
13297             this.view.el.remove();
13298             this.view.purgeListeners();
13299         }
13300         if(this.list){
13301             this.list.dom.innerHTML  = '';
13302         }
13303         
13304         if(this.store){
13305             this.store.un('beforeload', this.onBeforeLoad, this);
13306             this.store.un('load', this.onLoad, this);
13307             this.store.un('loadexception', this.onLoadException, this);
13308         }
13309         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13310     },
13311
13312     // private
13313     fireKey : function(e){
13314         if(e.isNavKeyPress() && !this.list.isVisible()){
13315             this.fireEvent("specialkey", this, e);
13316         }
13317     },
13318
13319     // private
13320     onResize: function(w, h){
13321 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13322 //        
13323 //        if(typeof w != 'number'){
13324 //            // we do not handle it!?!?
13325 //            return;
13326 //        }
13327 //        var tw = this.trigger.getWidth();
13328 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13329 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13330 //        var x = w - tw;
13331 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13332 //            
13333 //        //this.trigger.setStyle('left', x+'px');
13334 //        
13335 //        if(this.list && this.listWidth === undefined){
13336 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13337 //            this.list.setWidth(lw);
13338 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13339 //        }
13340         
13341     
13342         
13343     },
13344
13345     /**
13346      * Allow or prevent the user from directly editing the field text.  If false is passed,
13347      * the user will only be able to select from the items defined in the dropdown list.  This method
13348      * is the runtime equivalent of setting the 'editable' config option at config time.
13349      * @param {Boolean} value True to allow the user to directly edit the field text
13350      */
13351     setEditable : function(value){
13352         if(value == this.editable){
13353             return;
13354         }
13355         this.editable = value;
13356         if(!value){
13357             this.inputEl().dom.setAttribute('readOnly', true);
13358             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13359             this.inputEl().addClass('x-combo-noedit');
13360         }else{
13361             this.inputEl().dom.setAttribute('readOnly', false);
13362             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13363             this.inputEl().removeClass('x-combo-noedit');
13364         }
13365     },
13366
13367     // private
13368     
13369     onBeforeLoad : function(combo,opts){
13370         if(!this.hasFocus){
13371             return;
13372         }
13373          if (!opts.add) {
13374             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13375          }
13376         this.restrictHeight();
13377         this.selectedIndex = -1;
13378     },
13379
13380     // private
13381     onLoad : function(){
13382         
13383         this.hasQuery = false;
13384         
13385         if(!this.hasFocus){
13386             return;
13387         }
13388         
13389         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13390             this.loading.hide();
13391         }
13392              
13393         if(this.store.getCount() > 0){
13394             this.expand();
13395             this.restrictHeight();
13396             if(this.lastQuery == this.allQuery){
13397                 if(this.editable && !this.tickable){
13398                     this.inputEl().dom.select();
13399                 }
13400                 
13401                 if(
13402                     !this.selectByValue(this.value, true) &&
13403                     this.autoFocus && 
13404                     (
13405                         !this.store.lastOptions ||
13406                         typeof(this.store.lastOptions.add) == 'undefined' || 
13407                         this.store.lastOptions.add != true
13408                     )
13409                 ){
13410                     this.select(0, true);
13411                 }
13412             }else{
13413                 if(this.autoFocus){
13414                     this.selectNext();
13415                 }
13416                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13417                     this.taTask.delay(this.typeAheadDelay);
13418                 }
13419             }
13420         }else{
13421             this.onEmptyResults();
13422         }
13423         
13424         //this.el.focus();
13425     },
13426     // private
13427     onLoadException : function()
13428     {
13429         this.hasQuery = false;
13430         
13431         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13432             this.loading.hide();
13433         }
13434         
13435         if(this.tickable && this.editable){
13436             return;
13437         }
13438         
13439         this.collapse();
13440         // only causes errors at present
13441         //Roo.log(this.store.reader.jsonData);
13442         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13443             // fixme
13444             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13445         //}
13446         
13447         
13448     },
13449     // private
13450     onTypeAhead : function(){
13451         if(this.store.getCount() > 0){
13452             var r = this.store.getAt(0);
13453             var newValue = r.data[this.displayField];
13454             var len = newValue.length;
13455             var selStart = this.getRawValue().length;
13456             
13457             if(selStart != len){
13458                 this.setRawValue(newValue);
13459                 this.selectText(selStart, newValue.length);
13460             }
13461         }
13462     },
13463
13464     // private
13465     onSelect : function(record, index){
13466         
13467         if(this.fireEvent('beforeselect', this, record, index) !== false){
13468         
13469             this.setFromData(index > -1 ? record.data : false);
13470             
13471             this.collapse();
13472             this.fireEvent('select', this, record, index);
13473         }
13474     },
13475
13476     /**
13477      * Returns the currently selected field value or empty string if no value is set.
13478      * @return {String} value The selected value
13479      */
13480     getValue : function()
13481     {
13482         if(Roo.isIOS && this.useNativeIOS){
13483             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13484         }
13485         
13486         if(this.multiple){
13487             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13488         }
13489         
13490         if(this.valueField){
13491             return typeof this.value != 'undefined' ? this.value : '';
13492         }else{
13493             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13494         }
13495     },
13496     
13497     getRawValue : function()
13498     {
13499         if(Roo.isIOS && this.useNativeIOS){
13500             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13501         }
13502         
13503         var v = this.inputEl().getValue();
13504         
13505         return v;
13506     },
13507
13508     /**
13509      * Clears any text/value currently set in the field
13510      */
13511     clearValue : function(){
13512         
13513         if(this.hiddenField){
13514             this.hiddenField.dom.value = '';
13515         }
13516         this.value = '';
13517         this.setRawValue('');
13518         this.lastSelectionText = '';
13519         this.lastData = false;
13520         
13521         var close = this.closeTriggerEl();
13522         
13523         if(close){
13524             close.hide();
13525         }
13526         
13527         this.validate();
13528         
13529     },
13530
13531     /**
13532      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13533      * will be displayed in the field.  If the value does not match the data value of an existing item,
13534      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13535      * Otherwise the field will be blank (although the value will still be set).
13536      * @param {String} value The value to match
13537      */
13538     setValue : function(v)
13539     {
13540         if(Roo.isIOS && this.useNativeIOS){
13541             this.setIOSValue(v);
13542             return;
13543         }
13544         
13545         if(this.multiple){
13546             this.syncValue();
13547             return;
13548         }
13549         
13550         var text = v;
13551         if(this.valueField){
13552             var r = this.findRecord(this.valueField, v);
13553             if(r){
13554                 text = r.data[this.displayField];
13555             }else if(this.valueNotFoundText !== undefined){
13556                 text = this.valueNotFoundText;
13557             }
13558         }
13559         this.lastSelectionText = text;
13560         if(this.hiddenField){
13561             this.hiddenField.dom.value = v;
13562         }
13563         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13564         this.value = v;
13565         
13566         var close = this.closeTriggerEl();
13567         
13568         if(close){
13569             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13570         }
13571         
13572         this.validate();
13573     },
13574     /**
13575      * @property {Object} the last set data for the element
13576      */
13577     
13578     lastData : false,
13579     /**
13580      * Sets the value of the field based on a object which is related to the record format for the store.
13581      * @param {Object} value the value to set as. or false on reset?
13582      */
13583     setFromData : function(o){
13584         
13585         if(this.multiple){
13586             this.addItem(o);
13587             return;
13588         }
13589             
13590         var dv = ''; // display value
13591         var vv = ''; // value value..
13592         this.lastData = o;
13593         if (this.displayField) {
13594             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13595         } else {
13596             // this is an error condition!!!
13597             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13598         }
13599         
13600         if(this.valueField){
13601             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13602         }
13603         
13604         var close = this.closeTriggerEl();
13605         
13606         if(close){
13607             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13608         }
13609         
13610         if(this.hiddenField){
13611             this.hiddenField.dom.value = vv;
13612             
13613             this.lastSelectionText = dv;
13614             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13615             this.value = vv;
13616             return;
13617         }
13618         // no hidden field.. - we store the value in 'value', but still display
13619         // display field!!!!
13620         this.lastSelectionText = dv;
13621         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13622         this.value = vv;
13623         
13624         
13625         
13626     },
13627     // private
13628     reset : function(){
13629         // overridden so that last data is reset..
13630         
13631         if(this.multiple){
13632             this.clearItem();
13633             return;
13634         }
13635         
13636         this.setValue(this.originalValue);
13637         //this.clearInvalid();
13638         this.lastData = false;
13639         if (this.view) {
13640             this.view.clearSelections();
13641         }
13642         
13643         this.validate();
13644     },
13645     // private
13646     findRecord : function(prop, value){
13647         var record;
13648         if(this.store.getCount() > 0){
13649             this.store.each(function(r){
13650                 if(r.data[prop] == value){
13651                     record = r;
13652                     return false;
13653                 }
13654                 return true;
13655             });
13656         }
13657         return record;
13658     },
13659     
13660     getName: function()
13661     {
13662         // returns hidden if it's set..
13663         if (!this.rendered) {return ''};
13664         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13665         
13666     },
13667     // private
13668     onViewMove : function(e, t){
13669         this.inKeyMode = false;
13670     },
13671
13672     // private
13673     onViewOver : function(e, t){
13674         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13675             return;
13676         }
13677         var item = this.view.findItemFromChild(t);
13678         
13679         if(item){
13680             var index = this.view.indexOf(item);
13681             this.select(index, false);
13682         }
13683     },
13684
13685     // private
13686     onViewClick : function(view, doFocus, el, e)
13687     {
13688         var index = this.view.getSelectedIndexes()[0];
13689         
13690         var r = this.store.getAt(index);
13691         
13692         if(this.tickable){
13693             
13694             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13695                 return;
13696             }
13697             
13698             var rm = false;
13699             var _this = this;
13700             
13701             Roo.each(this.tickItems, function(v,k){
13702                 
13703                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13704                     Roo.log(v);
13705                     _this.tickItems.splice(k, 1);
13706                     
13707                     if(typeof(e) == 'undefined' && view == false){
13708                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13709                     }
13710                     
13711                     rm = true;
13712                     return;
13713                 }
13714             });
13715             
13716             if(rm){
13717                 return;
13718             }
13719             
13720             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13721                 this.tickItems.push(r.data);
13722             }
13723             
13724             if(typeof(e) == 'undefined' && view == false){
13725                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13726             }
13727                     
13728             return;
13729         }
13730         
13731         if(r){
13732             this.onSelect(r, index);
13733         }
13734         if(doFocus !== false && !this.blockFocus){
13735             this.inputEl().focus();
13736         }
13737     },
13738
13739     // private
13740     restrictHeight : function(){
13741         //this.innerList.dom.style.height = '';
13742         //var inner = this.innerList.dom;
13743         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13744         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13745         //this.list.beginUpdate();
13746         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13747         this.list.alignTo(this.inputEl(), this.listAlign);
13748         this.list.alignTo(this.inputEl(), this.listAlign);
13749         //this.list.endUpdate();
13750     },
13751
13752     // private
13753     onEmptyResults : function(){
13754         
13755         if(this.tickable && this.editable){
13756             this.restrictHeight();
13757             return;
13758         }
13759         
13760         this.collapse();
13761     },
13762
13763     /**
13764      * Returns true if the dropdown list is expanded, else false.
13765      */
13766     isExpanded : function(){
13767         return this.list.isVisible();
13768     },
13769
13770     /**
13771      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13772      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13773      * @param {String} value The data value of the item to select
13774      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13775      * selected item if it is not currently in view (defaults to true)
13776      * @return {Boolean} True if the value matched an item in the list, else false
13777      */
13778     selectByValue : function(v, scrollIntoView){
13779         if(v !== undefined && v !== null){
13780             var r = this.findRecord(this.valueField || this.displayField, v);
13781             if(r){
13782                 this.select(this.store.indexOf(r), scrollIntoView);
13783                 return true;
13784             }
13785         }
13786         return false;
13787     },
13788
13789     /**
13790      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13791      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13792      * @param {Number} index The zero-based index of the list item to select
13793      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13794      * selected item if it is not currently in view (defaults to true)
13795      */
13796     select : function(index, scrollIntoView){
13797         this.selectedIndex = index;
13798         this.view.select(index);
13799         if(scrollIntoView !== false){
13800             var el = this.view.getNode(index);
13801             /*
13802              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13803              */
13804             if(el){
13805                 this.list.scrollChildIntoView(el, false);
13806             }
13807         }
13808     },
13809
13810     // private
13811     selectNext : function(){
13812         var ct = this.store.getCount();
13813         if(ct > 0){
13814             if(this.selectedIndex == -1){
13815                 this.select(0);
13816             }else if(this.selectedIndex < ct-1){
13817                 this.select(this.selectedIndex+1);
13818             }
13819         }
13820     },
13821
13822     // private
13823     selectPrev : function(){
13824         var ct = this.store.getCount();
13825         if(ct > 0){
13826             if(this.selectedIndex == -1){
13827                 this.select(0);
13828             }else if(this.selectedIndex != 0){
13829                 this.select(this.selectedIndex-1);
13830             }
13831         }
13832     },
13833
13834     // private
13835     onKeyUp : function(e){
13836         if(this.editable !== false && !e.isSpecialKey()){
13837             this.lastKey = e.getKey();
13838             this.dqTask.delay(this.queryDelay);
13839         }
13840     },
13841
13842     // private
13843     validateBlur : function(){
13844         return !this.list || !this.list.isVisible();   
13845     },
13846
13847     // private
13848     initQuery : function(){
13849         
13850         var v = this.getRawValue();
13851         
13852         if(this.tickable && this.editable){
13853             v = this.tickableInputEl().getValue();
13854         }
13855         
13856         this.doQuery(v);
13857     },
13858
13859     // private
13860     doForce : function(){
13861         if(this.inputEl().dom.value.length > 0){
13862             this.inputEl().dom.value =
13863                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13864              
13865         }
13866     },
13867
13868     /**
13869      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13870      * query allowing the query action to be canceled if needed.
13871      * @param {String} query The SQL query to execute
13872      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13873      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13874      * saved in the current store (defaults to false)
13875      */
13876     doQuery : function(q, forceAll){
13877         
13878         if(q === undefined || q === null){
13879             q = '';
13880         }
13881         var qe = {
13882             query: q,
13883             forceAll: forceAll,
13884             combo: this,
13885             cancel:false
13886         };
13887         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13888             return false;
13889         }
13890         q = qe.query;
13891         
13892         forceAll = qe.forceAll;
13893         if(forceAll === true || (q.length >= this.minChars)){
13894             
13895             this.hasQuery = true;
13896             
13897             if(this.lastQuery != q || this.alwaysQuery){
13898                 this.lastQuery = q;
13899                 if(this.mode == 'local'){
13900                     this.selectedIndex = -1;
13901                     if(forceAll){
13902                         this.store.clearFilter();
13903                     }else{
13904                         
13905                         if(this.specialFilter){
13906                             this.fireEvent('specialfilter', this);
13907                             this.onLoad();
13908                             return;
13909                         }
13910                         
13911                         this.store.filter(this.displayField, q);
13912                     }
13913                     
13914                     this.store.fireEvent("datachanged", this.store);
13915                     
13916                     this.onLoad();
13917                     
13918                     
13919                 }else{
13920                     
13921                     this.store.baseParams[this.queryParam] = q;
13922                     
13923                     var options = {params : this.getParams(q)};
13924                     
13925                     if(this.loadNext){
13926                         options.add = true;
13927                         options.params.start = this.page * this.pageSize;
13928                     }
13929                     
13930                     this.store.load(options);
13931                     
13932                     /*
13933                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13934                      *  we should expand the list on onLoad
13935                      *  so command out it
13936                      */
13937 //                    this.expand();
13938                 }
13939             }else{
13940                 this.selectedIndex = -1;
13941                 this.onLoad();   
13942             }
13943         }
13944         
13945         this.loadNext = false;
13946     },
13947     
13948     // private
13949     getParams : function(q){
13950         var p = {};
13951         //p[this.queryParam] = q;
13952         
13953         if(this.pageSize){
13954             p.start = 0;
13955             p.limit = this.pageSize;
13956         }
13957         return p;
13958     },
13959
13960     /**
13961      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13962      */
13963     collapse : function(){
13964         if(!this.isExpanded()){
13965             return;
13966         }
13967         
13968         this.list.hide();
13969         
13970         this.hasFocus = false;
13971         
13972         if(this.tickable){
13973             this.okBtn.hide();
13974             this.cancelBtn.hide();
13975             this.trigger.show();
13976             
13977             if(this.editable){
13978                 this.tickableInputEl().dom.value = '';
13979                 this.tickableInputEl().blur();
13980             }
13981             
13982         }
13983         
13984         Roo.get(document).un('mousedown', this.collapseIf, this);
13985         Roo.get(document).un('mousewheel', this.collapseIf, this);
13986         if (!this.editable) {
13987             Roo.get(document).un('keydown', this.listKeyPress, this);
13988         }
13989         this.fireEvent('collapse', this);
13990         
13991         this.validate();
13992     },
13993
13994     // private
13995     collapseIf : function(e){
13996         var in_combo  = e.within(this.el);
13997         var in_list =  e.within(this.list);
13998         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13999         
14000         if (in_combo || in_list || is_list) {
14001             //e.stopPropagation();
14002             return;
14003         }
14004         
14005         if(this.tickable){
14006             this.onTickableFooterButtonClick(e, false, false);
14007         }
14008
14009         this.collapse();
14010         
14011     },
14012
14013     /**
14014      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14015      */
14016     expand : function(){
14017        
14018         if(this.isExpanded() || !this.hasFocus){
14019             return;
14020         }
14021         
14022         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14023         this.list.setWidth(lw);
14024         
14025         Roo.log('expand');
14026         
14027         this.list.show();
14028         
14029         this.restrictHeight();
14030         
14031         if(this.tickable){
14032             
14033             this.tickItems = Roo.apply([], this.item);
14034             
14035             this.okBtn.show();
14036             this.cancelBtn.show();
14037             this.trigger.hide();
14038             
14039             if(this.editable){
14040                 this.tickableInputEl().focus();
14041             }
14042             
14043         }
14044         
14045         Roo.get(document).on('mousedown', this.collapseIf, this);
14046         Roo.get(document).on('mousewheel', this.collapseIf, this);
14047         if (!this.editable) {
14048             Roo.get(document).on('keydown', this.listKeyPress, this);
14049         }
14050         
14051         this.fireEvent('expand', this);
14052     },
14053
14054     // private
14055     // Implements the default empty TriggerField.onTriggerClick function
14056     onTriggerClick : function(e)
14057     {
14058         Roo.log('trigger click');
14059         
14060         if(this.disabled || !this.triggerList){
14061             return;
14062         }
14063         
14064         this.page = 0;
14065         this.loadNext = false;
14066         
14067         if(this.isExpanded()){
14068             this.collapse();
14069             if (!this.blockFocus) {
14070                 this.inputEl().focus();
14071             }
14072             
14073         }else {
14074             this.hasFocus = true;
14075             if(this.triggerAction == 'all') {
14076                 this.doQuery(this.allQuery, true);
14077             } else {
14078                 this.doQuery(this.getRawValue());
14079             }
14080             if (!this.blockFocus) {
14081                 this.inputEl().focus();
14082             }
14083         }
14084     },
14085     
14086     onTickableTriggerClick : function(e)
14087     {
14088         if(this.disabled){
14089             return;
14090         }
14091         
14092         this.page = 0;
14093         this.loadNext = false;
14094         this.hasFocus = true;
14095         
14096         if(this.triggerAction == 'all') {
14097             this.doQuery(this.allQuery, true);
14098         } else {
14099             this.doQuery(this.getRawValue());
14100         }
14101     },
14102     
14103     onSearchFieldClick : function(e)
14104     {
14105         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14106             this.onTickableFooterButtonClick(e, false, false);
14107             return;
14108         }
14109         
14110         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14111             return;
14112         }
14113         
14114         this.page = 0;
14115         this.loadNext = false;
14116         this.hasFocus = true;
14117         
14118         if(this.triggerAction == 'all') {
14119             this.doQuery(this.allQuery, true);
14120         } else {
14121             this.doQuery(this.getRawValue());
14122         }
14123     },
14124     
14125     listKeyPress : function(e)
14126     {
14127         //Roo.log('listkeypress');
14128         // scroll to first matching element based on key pres..
14129         if (e.isSpecialKey()) {
14130             return false;
14131         }
14132         var k = String.fromCharCode(e.getKey()).toUpperCase();
14133         //Roo.log(k);
14134         var match  = false;
14135         var csel = this.view.getSelectedNodes();
14136         var cselitem = false;
14137         if (csel.length) {
14138             var ix = this.view.indexOf(csel[0]);
14139             cselitem  = this.store.getAt(ix);
14140             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14141                 cselitem = false;
14142             }
14143             
14144         }
14145         
14146         this.store.each(function(v) { 
14147             if (cselitem) {
14148                 // start at existing selection.
14149                 if (cselitem.id == v.id) {
14150                     cselitem = false;
14151                 }
14152                 return true;
14153             }
14154                 
14155             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14156                 match = this.store.indexOf(v);
14157                 return false;
14158             }
14159             return true;
14160         }, this);
14161         
14162         if (match === false) {
14163             return true; // no more action?
14164         }
14165         // scroll to?
14166         this.view.select(match);
14167         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14168         sn.scrollIntoView(sn.dom.parentNode, false);
14169     },
14170     
14171     onViewScroll : function(e, t){
14172         
14173         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){
14174             return;
14175         }
14176         
14177         this.hasQuery = true;
14178         
14179         this.loading = this.list.select('.loading', true).first();
14180         
14181         if(this.loading === null){
14182             this.list.createChild({
14183                 tag: 'div',
14184                 cls: 'loading roo-select2-more-results roo-select2-active',
14185                 html: 'Loading more results...'
14186             });
14187             
14188             this.loading = this.list.select('.loading', true).first();
14189             
14190             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14191             
14192             this.loading.hide();
14193         }
14194         
14195         this.loading.show();
14196         
14197         var _combo = this;
14198         
14199         this.page++;
14200         this.loadNext = true;
14201         
14202         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14203         
14204         return;
14205     },
14206     
14207     addItem : function(o)
14208     {   
14209         var dv = ''; // display value
14210         
14211         if (this.displayField) {
14212             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14213         } else {
14214             // this is an error condition!!!
14215             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14216         }
14217         
14218         if(!dv.length){
14219             return;
14220         }
14221         
14222         var choice = this.choices.createChild({
14223             tag: 'li',
14224             cls: 'roo-select2-search-choice',
14225             cn: [
14226                 {
14227                     tag: 'div',
14228                     html: dv
14229                 },
14230                 {
14231                     tag: 'a',
14232                     href: '#',
14233                     cls: 'roo-select2-search-choice-close',
14234                     tabindex: '-1'
14235                 }
14236             ]
14237             
14238         }, this.searchField);
14239         
14240         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14241         
14242         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14243         
14244         this.item.push(o);
14245         
14246         this.lastData = o;
14247         
14248         this.syncValue();
14249         
14250         this.inputEl().dom.value = '';
14251         
14252         this.validate();
14253     },
14254     
14255     onRemoveItem : function(e, _self, o)
14256     {
14257         e.preventDefault();
14258         
14259         this.lastItem = Roo.apply([], this.item);
14260         
14261         var index = this.item.indexOf(o.data) * 1;
14262         
14263         if( index < 0){
14264             Roo.log('not this item?!');
14265             return;
14266         }
14267         
14268         this.item.splice(index, 1);
14269         o.item.remove();
14270         
14271         this.syncValue();
14272         
14273         this.fireEvent('remove', this, e);
14274         
14275         this.validate();
14276         
14277     },
14278     
14279     syncValue : function()
14280     {
14281         if(!this.item.length){
14282             this.clearValue();
14283             return;
14284         }
14285             
14286         var value = [];
14287         var _this = this;
14288         Roo.each(this.item, function(i){
14289             if(_this.valueField){
14290                 value.push(i[_this.valueField]);
14291                 return;
14292             }
14293
14294             value.push(i);
14295         });
14296
14297         this.value = value.join(',');
14298
14299         if(this.hiddenField){
14300             this.hiddenField.dom.value = this.value;
14301         }
14302         
14303         this.store.fireEvent("datachanged", this.store);
14304         
14305         this.validate();
14306     },
14307     
14308     clearItem : function()
14309     {
14310         if(!this.multiple){
14311             return;
14312         }
14313         
14314         this.item = [];
14315         
14316         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14317            c.remove();
14318         });
14319         
14320         this.syncValue();
14321         
14322         this.validate();
14323         
14324         if(this.tickable && !Roo.isTouch){
14325             this.view.refresh();
14326         }
14327     },
14328     
14329     inputEl: function ()
14330     {
14331         if(Roo.isIOS && this.useNativeIOS){
14332             return this.el.select('select.roo-ios-select', true).first();
14333         }
14334         
14335         if(Roo.isTouch && this.mobileTouchView){
14336             return this.el.select('input.form-control',true).first();
14337         }
14338         
14339         if(this.tickable){
14340             return this.searchField;
14341         }
14342         
14343         return this.el.select('input.form-control',true).first();
14344     },
14345     
14346     onTickableFooterButtonClick : function(e, btn, el)
14347     {
14348         e.preventDefault();
14349         
14350         this.lastItem = Roo.apply([], this.item);
14351         
14352         if(btn && btn.name == 'cancel'){
14353             this.tickItems = Roo.apply([], this.item);
14354             this.collapse();
14355             return;
14356         }
14357         
14358         this.clearItem();
14359         
14360         var _this = this;
14361         
14362         Roo.each(this.tickItems, function(o){
14363             _this.addItem(o);
14364         });
14365         
14366         this.collapse();
14367         
14368     },
14369     
14370     validate : function()
14371     {
14372         var v = this.getRawValue();
14373         
14374         if(this.multiple){
14375             v = this.getValue();
14376         }
14377         
14378         if(this.disabled || this.allowBlank || v.length){
14379             this.markValid();
14380             return true;
14381         }
14382         
14383         this.markInvalid();
14384         return false;
14385     },
14386     
14387     tickableInputEl : function()
14388     {
14389         if(!this.tickable || !this.editable){
14390             return this.inputEl();
14391         }
14392         
14393         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14394     },
14395     
14396     
14397     getAutoCreateTouchView : function()
14398     {
14399         var id = Roo.id();
14400         
14401         var cfg = {
14402             cls: 'form-group' //input-group
14403         };
14404         
14405         var input =  {
14406             tag: 'input',
14407             id : id,
14408             type : this.inputType,
14409             cls : 'form-control x-combo-noedit',
14410             autocomplete: 'new-password',
14411             placeholder : this.placeholder || '',
14412             readonly : true
14413         };
14414         
14415         if (this.name) {
14416             input.name = this.name;
14417         }
14418         
14419         if (this.size) {
14420             input.cls += ' input-' + this.size;
14421         }
14422         
14423         if (this.disabled) {
14424             input.disabled = true;
14425         }
14426         
14427         var inputblock = {
14428             cls : '',
14429             cn : [
14430                 input
14431             ]
14432         };
14433         
14434         if(this.before){
14435             inputblock.cls += ' input-group';
14436             
14437             inputblock.cn.unshift({
14438                 tag :'span',
14439                 cls : 'input-group-addon',
14440                 html : this.before
14441             });
14442         }
14443         
14444         if(this.removable && !this.multiple){
14445             inputblock.cls += ' roo-removable';
14446             
14447             inputblock.cn.push({
14448                 tag: 'button',
14449                 html : 'x',
14450                 cls : 'roo-combo-removable-btn close'
14451             });
14452         }
14453
14454         if(this.hasFeedback && !this.allowBlank){
14455             
14456             inputblock.cls += ' has-feedback';
14457             
14458             inputblock.cn.push({
14459                 tag: 'span',
14460                 cls: 'glyphicon form-control-feedback'
14461             });
14462             
14463         }
14464         
14465         if (this.after) {
14466             
14467             inputblock.cls += (this.before) ? '' : ' input-group';
14468             
14469             inputblock.cn.push({
14470                 tag :'span',
14471                 cls : 'input-group-addon',
14472                 html : this.after
14473             });
14474         }
14475
14476         var box = {
14477             tag: 'div',
14478             cn: [
14479                 {
14480                     tag: 'input',
14481                     type : 'hidden',
14482                     cls: 'form-hidden-field'
14483                 },
14484                 inputblock
14485             ]
14486             
14487         };
14488         
14489         if(this.multiple){
14490             box = {
14491                 tag: 'div',
14492                 cn: [
14493                     {
14494                         tag: 'input',
14495                         type : 'hidden',
14496                         cls: 'form-hidden-field'
14497                     },
14498                     {
14499                         tag: 'ul',
14500                         cls: 'roo-select2-choices',
14501                         cn:[
14502                             {
14503                                 tag: 'li',
14504                                 cls: 'roo-select2-search-field',
14505                                 cn: [
14506
14507                                     inputblock
14508                                 ]
14509                             }
14510                         ]
14511                     }
14512                 ]
14513             }
14514         };
14515         
14516         var combobox = {
14517             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14518             cn: [
14519                 box
14520             ]
14521         };
14522         
14523         if(!this.multiple && this.showToggleBtn){
14524             
14525             var caret = {
14526                         tag: 'span',
14527                         cls: 'caret'
14528             };
14529             
14530             if (this.caret != false) {
14531                 caret = {
14532                      tag: 'i',
14533                      cls: 'fa fa-' + this.caret
14534                 };
14535                 
14536             }
14537             
14538             combobox.cn.push({
14539                 tag :'span',
14540                 cls : 'input-group-addon btn dropdown-toggle',
14541                 cn : [
14542                     caret,
14543                     {
14544                         tag: 'span',
14545                         cls: 'combobox-clear',
14546                         cn  : [
14547                             {
14548                                 tag : 'i',
14549                                 cls: 'icon-remove'
14550                             }
14551                         ]
14552                     }
14553                 ]
14554
14555             })
14556         }
14557         
14558         if(this.multiple){
14559             combobox.cls += ' roo-select2-container-multi';
14560         }
14561         
14562         var align = this.labelAlign || this.parentLabelAlign();
14563         
14564         if (align ==='left' && this.fieldLabel.length) {
14565
14566             cfg.cn = [
14567                 {
14568                    tag : 'i',
14569                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14570                    tooltip : 'This field is required'
14571                 },
14572                 {
14573                     tag: 'label',
14574                     cls : 'control-label',
14575                     html : this.fieldLabel
14576
14577                 },
14578                 {
14579                     cls : '', 
14580                     cn: [
14581                         combobox
14582                     ]
14583                 }
14584             ];
14585             
14586             var labelCfg = cfg.cn[1];
14587             var contentCfg = cfg.cn[2];
14588             
14589
14590             if(this.indicatorpos == 'right'){
14591                 cfg.cn = [
14592                     {
14593                         tag: 'label',
14594                         cls : 'control-label',
14595                         html : this.fieldLabel
14596
14597                     },
14598                     {
14599                        tag : 'i',
14600                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14601                        tooltip : 'This field is required'
14602                     },
14603                     {
14604                         cls : '', 
14605                         cn: [
14606                             combobox
14607                         ]
14608                     }
14609                 ];
14610             }
14611             
14612             labelCfg = cfg.cn[0];
14613             contentCfg = cfg.cn[2];
14614             
14615             if(this.labelWidth > 12){
14616                 labelCfg.style = "width: " + this.labelWidth + 'px';
14617             }
14618             
14619             if(this.labelWidth < 13 && this.labelmd == 0){
14620                 this.labelmd = this.labelWidth;
14621             }
14622             
14623             if(this.labellg > 0){
14624                 labelCfg.cls += ' col-lg-' + this.labellg;
14625                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14626             }
14627             
14628             if(this.labelmd > 0){
14629                 labelCfg.cls += ' col-md-' + this.labelmd;
14630                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14631             }
14632             
14633             if(this.labelsm > 0){
14634                 labelCfg.cls += ' col-sm-' + this.labelsm;
14635                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14636             }
14637             
14638             if(this.labelxs > 0){
14639                 labelCfg.cls += ' col-xs-' + this.labelxs;
14640                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14641             }
14642                 
14643                 
14644         } else if ( this.fieldLabel.length) {
14645             cfg.cn = [
14646                 {
14647                    tag : 'i',
14648                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14649                    tooltip : 'This field is required'
14650                 },
14651                 {
14652                     tag: 'label',
14653                     cls : 'control-label',
14654                     html : this.fieldLabel
14655
14656                 },
14657                 {
14658                     cls : '', 
14659                     cn: [
14660                         combobox
14661                     ]
14662                 }
14663             ];
14664             
14665             if(this.indicatorpos == 'right'){
14666                 cfg.cn = [
14667                     {
14668                         tag: 'label',
14669                         cls : 'control-label',
14670                         html : this.fieldLabel
14671
14672                     },
14673                     {
14674                        tag : 'i',
14675                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14676                        tooltip : 'This field is required'
14677                     },
14678                     {
14679                         cls : '', 
14680                         cn: [
14681                             combobox
14682                         ]
14683                     }
14684                 ];
14685             }
14686         } else {
14687             cfg.cn = combobox;    
14688         }
14689         
14690         
14691         var settings = this;
14692         
14693         ['xs','sm','md','lg'].map(function(size){
14694             if (settings[size]) {
14695                 cfg.cls += ' col-' + size + '-' + settings[size];
14696             }
14697         });
14698         
14699         return cfg;
14700     },
14701     
14702     initTouchView : function()
14703     {
14704         this.renderTouchView();
14705         
14706         this.touchViewEl.on('scroll', function(){
14707             this.el.dom.scrollTop = 0;
14708         }, this);
14709         
14710         this.originalValue = this.getValue();
14711         
14712         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14713         
14714         this.inputEl().on("click", this.showTouchView, this);
14715         if (this.triggerEl) {
14716             this.triggerEl.on("click", this.showTouchView, this);
14717         }
14718         
14719         
14720         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14721         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14722         
14723         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14724         
14725         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14726         this.store.on('load', this.onTouchViewLoad, this);
14727         this.store.on('loadexception', this.onTouchViewLoadException, this);
14728         
14729         if(this.hiddenName){
14730             
14731             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14732             
14733             this.hiddenField.dom.value =
14734                 this.hiddenValue !== undefined ? this.hiddenValue :
14735                 this.value !== undefined ? this.value : '';
14736         
14737             this.el.dom.removeAttribute('name');
14738             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14739         }
14740         
14741         if(this.multiple){
14742             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14743             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14744         }
14745         
14746         if(this.removable && !this.multiple){
14747             var close = this.closeTriggerEl();
14748             if(close){
14749                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14750                 close.on('click', this.removeBtnClick, this, close);
14751             }
14752         }
14753         /*
14754          * fix the bug in Safari iOS8
14755          */
14756         this.inputEl().on("focus", function(e){
14757             document.activeElement.blur();
14758         }, this);
14759         
14760         return;
14761         
14762         
14763     },
14764     
14765     renderTouchView : function()
14766     {
14767         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14768         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14769         
14770         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14771         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14772         
14773         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14774         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14775         this.touchViewBodyEl.setStyle('overflow', 'auto');
14776         
14777         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14778         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14779         
14780         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14781         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14782         
14783     },
14784     
14785     showTouchView : function()
14786     {
14787         if(this.disabled){
14788             return;
14789         }
14790         
14791         this.touchViewHeaderEl.hide();
14792
14793         if(this.modalTitle.length){
14794             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14795             this.touchViewHeaderEl.show();
14796         }
14797
14798         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14799         this.touchViewEl.show();
14800
14801         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14802         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14803                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14804
14805         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14806
14807         if(this.modalTitle.length){
14808             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14809         }
14810         
14811         this.touchViewBodyEl.setHeight(bodyHeight);
14812
14813         if(this.animate){
14814             var _this = this;
14815             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14816         }else{
14817             this.touchViewEl.addClass('in');
14818         }
14819
14820         this.doTouchViewQuery();
14821         
14822     },
14823     
14824     hideTouchView : function()
14825     {
14826         this.touchViewEl.removeClass('in');
14827
14828         if(this.animate){
14829             var _this = this;
14830             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14831         }else{
14832             this.touchViewEl.setStyle('display', 'none');
14833         }
14834         
14835     },
14836     
14837     setTouchViewValue : function()
14838     {
14839         if(this.multiple){
14840             this.clearItem();
14841         
14842             var _this = this;
14843
14844             Roo.each(this.tickItems, function(o){
14845                 this.addItem(o);
14846             }, this);
14847         }
14848         
14849         this.hideTouchView();
14850     },
14851     
14852     doTouchViewQuery : function()
14853     {
14854         var qe = {
14855             query: '',
14856             forceAll: true,
14857             combo: this,
14858             cancel:false
14859         };
14860         
14861         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14862             return false;
14863         }
14864         
14865         if(!this.alwaysQuery || this.mode == 'local'){
14866             this.onTouchViewLoad();
14867             return;
14868         }
14869         
14870         this.store.load();
14871     },
14872     
14873     onTouchViewBeforeLoad : function(combo,opts)
14874     {
14875         return;
14876     },
14877
14878     // private
14879     onTouchViewLoad : function()
14880     {
14881         if(this.store.getCount() < 1){
14882             this.onTouchViewEmptyResults();
14883             return;
14884         }
14885         
14886         this.clearTouchView();
14887         
14888         var rawValue = this.getRawValue();
14889         
14890         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14891         
14892         this.tickItems = [];
14893         
14894         this.store.data.each(function(d, rowIndex){
14895             var row = this.touchViewListGroup.createChild(template);
14896             
14897             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14898                 row.addClass(d.data.cls);
14899             }
14900             
14901             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14902                 var cfg = {
14903                     data : d.data,
14904                     html : d.data[this.displayField]
14905                 };
14906                 
14907                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14908                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14909                 }
14910             }
14911             row.removeClass('selected');
14912             if(!this.multiple && this.valueField &&
14913                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14914             {
14915                 // radio buttons..
14916                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14917                 row.addClass('selected');
14918             }
14919             
14920             if(this.multiple && this.valueField &&
14921                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14922             {
14923                 
14924                 // checkboxes...
14925                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14926                 this.tickItems.push(d.data);
14927             }
14928             
14929             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14930             
14931         }, this);
14932         
14933         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14934         
14935         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14936
14937         if(this.modalTitle.length){
14938             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14939         }
14940
14941         var listHeight = this.touchViewListGroup.getHeight();
14942         
14943         var _this = this;
14944         
14945         if(firstChecked && listHeight > bodyHeight){
14946             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14947         }
14948         
14949     },
14950     
14951     onTouchViewLoadException : function()
14952     {
14953         this.hideTouchView();
14954     },
14955     
14956     onTouchViewEmptyResults : function()
14957     {
14958         this.clearTouchView();
14959         
14960         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14961         
14962         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14963         
14964     },
14965     
14966     clearTouchView : function()
14967     {
14968         this.touchViewListGroup.dom.innerHTML = '';
14969     },
14970     
14971     onTouchViewClick : function(e, el, o)
14972     {
14973         e.preventDefault();
14974         
14975         var row = o.row;
14976         var rowIndex = o.rowIndex;
14977         
14978         var r = this.store.getAt(rowIndex);
14979         
14980         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14981             
14982             if(!this.multiple){
14983                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14984                     c.dom.removeAttribute('checked');
14985                 }, this);
14986
14987                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14988
14989                 this.setFromData(r.data);
14990
14991                 var close = this.closeTriggerEl();
14992
14993                 if(close){
14994                     close.show();
14995                 }
14996
14997                 this.hideTouchView();
14998
14999                 this.fireEvent('select', this, r, rowIndex);
15000
15001                 return;
15002             }
15003
15004             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15005                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15006                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15007                 return;
15008             }
15009
15010             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15011             this.addItem(r.data);
15012             this.tickItems.push(r.data);
15013         }
15014     },
15015     
15016     getAutoCreateNativeIOS : function()
15017     {
15018         var cfg = {
15019             cls: 'form-group' //input-group,
15020         };
15021         
15022         var combobox =  {
15023             tag: 'select',
15024             cls : 'roo-ios-select'
15025         };
15026         
15027         if (this.name) {
15028             combobox.name = this.name;
15029         }
15030         
15031         if (this.disabled) {
15032             combobox.disabled = true;
15033         }
15034         
15035         var settings = this;
15036         
15037         ['xs','sm','md','lg'].map(function(size){
15038             if (settings[size]) {
15039                 cfg.cls += ' col-' + size + '-' + settings[size];
15040             }
15041         });
15042         
15043         cfg.cn = combobox;
15044         
15045         return cfg;
15046         
15047     },
15048     
15049     initIOSView : function()
15050     {
15051         this.store.on('load', this.onIOSViewLoad, this);
15052         
15053         return;
15054     },
15055     
15056     onIOSViewLoad : function()
15057     {
15058         if(this.store.getCount() < 1){
15059             return;
15060         }
15061         
15062         this.clearIOSView();
15063         
15064         if(this.allowBlank) {
15065             
15066             var default_text = '-- SELECT --';
15067             
15068             var opt = this.inputEl().createChild({
15069                 tag: 'option',
15070                 value : 0,
15071                 html : default_text
15072             });
15073             
15074             var o = {};
15075             o[this.valueField] = 0;
15076             o[this.displayField] = default_text;
15077             
15078             this.ios_options.push({
15079                 data : o,
15080                 el : opt
15081             });
15082             
15083         }
15084         
15085         this.store.data.each(function(d, rowIndex){
15086             
15087             var html = '';
15088             
15089             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15090                 html = d.data[this.displayField];
15091             }
15092             
15093             var value = '';
15094             
15095             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15096                 value = d.data[this.valueField];
15097             }
15098             
15099             var option = {
15100                 tag: 'option',
15101                 value : value,
15102                 html : html
15103             };
15104             
15105             if(this.value == d.data[this.valueField]){
15106                 option['selected'] = true;
15107             }
15108             
15109             var opt = this.inputEl().createChild(option);
15110             
15111             this.ios_options.push({
15112                 data : d.data,
15113                 el : opt
15114             });
15115             
15116         }, this);
15117         
15118         this.inputEl().on('change', function(){
15119            this.fireEvent('select', this);
15120         }, this);
15121         
15122     },
15123     
15124     clearIOSView: function()
15125     {
15126         this.inputEl().dom.innerHTML = '';
15127         
15128         this.ios_options = [];
15129     },
15130     
15131     setIOSValue: function(v)
15132     {
15133         this.value = v;
15134         
15135         if(!this.ios_options){
15136             return;
15137         }
15138         
15139         Roo.each(this.ios_options, function(opts){
15140            
15141            opts.el.dom.removeAttribute('selected');
15142            
15143            if(opts.data[this.valueField] != v){
15144                return;
15145            }
15146            
15147            opts.el.dom.setAttribute('selected', true);
15148            
15149         }, this);
15150     }
15151
15152     /** 
15153     * @cfg {Boolean} grow 
15154     * @hide 
15155     */
15156     /** 
15157     * @cfg {Number} growMin 
15158     * @hide 
15159     */
15160     /** 
15161     * @cfg {Number} growMax 
15162     * @hide 
15163     */
15164     /**
15165      * @hide
15166      * @method autoSize
15167      */
15168 });
15169
15170 Roo.apply(Roo.bootstrap.ComboBox,  {
15171     
15172     header : {
15173         tag: 'div',
15174         cls: 'modal-header',
15175         cn: [
15176             {
15177                 tag: 'h4',
15178                 cls: 'modal-title'
15179             }
15180         ]
15181     },
15182     
15183     body : {
15184         tag: 'div',
15185         cls: 'modal-body',
15186         cn: [
15187             {
15188                 tag: 'ul',
15189                 cls: 'list-group'
15190             }
15191         ]
15192     },
15193     
15194     listItemRadio : {
15195         tag: 'li',
15196         cls: 'list-group-item',
15197         cn: [
15198             {
15199                 tag: 'span',
15200                 cls: 'roo-combobox-list-group-item-value'
15201             },
15202             {
15203                 tag: 'div',
15204                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15205                 cn: [
15206                     {
15207                         tag: 'input',
15208                         type: 'radio'
15209                     },
15210                     {
15211                         tag: 'label'
15212                     }
15213                 ]
15214             }
15215         ]
15216     },
15217     
15218     listItemCheckbox : {
15219         tag: 'li',
15220         cls: 'list-group-item',
15221         cn: [
15222             {
15223                 tag: 'span',
15224                 cls: 'roo-combobox-list-group-item-value'
15225             },
15226             {
15227                 tag: 'div',
15228                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15229                 cn: [
15230                     {
15231                         tag: 'input',
15232                         type: 'checkbox'
15233                     },
15234                     {
15235                         tag: 'label'
15236                     }
15237                 ]
15238             }
15239         ]
15240     },
15241     
15242     emptyResult : {
15243         tag: 'div',
15244         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15245     },
15246     
15247     footer : {
15248         tag: 'div',
15249         cls: 'modal-footer',
15250         cn: [
15251             {
15252                 tag: 'div',
15253                 cls: 'row',
15254                 cn: [
15255                     {
15256                         tag: 'div',
15257                         cls: 'col-xs-6 text-left',
15258                         cn: {
15259                             tag: 'button',
15260                             cls: 'btn btn-danger roo-touch-view-cancel',
15261                             html: 'Cancel'
15262                         }
15263                     },
15264                     {
15265                         tag: 'div',
15266                         cls: 'col-xs-6 text-right',
15267                         cn: {
15268                             tag: 'button',
15269                             cls: 'btn btn-success roo-touch-view-ok',
15270                             html: 'OK'
15271                         }
15272                     }
15273                 ]
15274             }
15275         ]
15276         
15277     }
15278 });
15279
15280 Roo.apply(Roo.bootstrap.ComboBox,  {
15281     
15282     touchViewTemplate : {
15283         tag: 'div',
15284         cls: 'modal fade roo-combobox-touch-view',
15285         cn: [
15286             {
15287                 tag: 'div',
15288                 cls: 'modal-dialog',
15289                 style : 'position:fixed', // we have to fix position....
15290                 cn: [
15291                     {
15292                         tag: 'div',
15293                         cls: 'modal-content',
15294                         cn: [
15295                             Roo.bootstrap.ComboBox.header,
15296                             Roo.bootstrap.ComboBox.body,
15297                             Roo.bootstrap.ComboBox.footer
15298                         ]
15299                     }
15300                 ]
15301             }
15302         ]
15303     }
15304 });/*
15305  * Based on:
15306  * Ext JS Library 1.1.1
15307  * Copyright(c) 2006-2007, Ext JS, LLC.
15308  *
15309  * Originally Released Under LGPL - original licence link has changed is not relivant.
15310  *
15311  * Fork - LGPL
15312  * <script type="text/javascript">
15313  */
15314
15315 /**
15316  * @class Roo.View
15317  * @extends Roo.util.Observable
15318  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15319  * This class also supports single and multi selection modes. <br>
15320  * Create a data model bound view:
15321  <pre><code>
15322  var store = new Roo.data.Store(...);
15323
15324  var view = new Roo.View({
15325     el : "my-element",
15326     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15327  
15328     singleSelect: true,
15329     selectedClass: "ydataview-selected",
15330     store: store
15331  });
15332
15333  // listen for node click?
15334  view.on("click", function(vw, index, node, e){
15335  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15336  });
15337
15338  // load XML data
15339  dataModel.load("foobar.xml");
15340  </code></pre>
15341  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15342  * <br><br>
15343  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15344  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15345  * 
15346  * Note: old style constructor is still suported (container, template, config)
15347  * 
15348  * @constructor
15349  * Create a new View
15350  * @param {Object} config The config object
15351  * 
15352  */
15353 Roo.View = function(config, depreciated_tpl, depreciated_config){
15354     
15355     this.parent = false;
15356     
15357     if (typeof(depreciated_tpl) == 'undefined') {
15358         // new way.. - universal constructor.
15359         Roo.apply(this, config);
15360         this.el  = Roo.get(this.el);
15361     } else {
15362         // old format..
15363         this.el  = Roo.get(config);
15364         this.tpl = depreciated_tpl;
15365         Roo.apply(this, depreciated_config);
15366     }
15367     this.wrapEl  = this.el.wrap().wrap();
15368     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15369     
15370     
15371     if(typeof(this.tpl) == "string"){
15372         this.tpl = new Roo.Template(this.tpl);
15373     } else {
15374         // support xtype ctors..
15375         this.tpl = new Roo.factory(this.tpl, Roo);
15376     }
15377     
15378     
15379     this.tpl.compile();
15380     
15381     /** @private */
15382     this.addEvents({
15383         /**
15384          * @event beforeclick
15385          * Fires before a click is processed. Returns false to cancel the default action.
15386          * @param {Roo.View} this
15387          * @param {Number} index The index of the target node
15388          * @param {HTMLElement} node The target node
15389          * @param {Roo.EventObject} e The raw event object
15390          */
15391             "beforeclick" : true,
15392         /**
15393          * @event click
15394          * Fires when a template node is clicked.
15395          * @param {Roo.View} this
15396          * @param {Number} index The index of the target node
15397          * @param {HTMLElement} node The target node
15398          * @param {Roo.EventObject} e The raw event object
15399          */
15400             "click" : true,
15401         /**
15402          * @event dblclick
15403          * Fires when a template node is double clicked.
15404          * @param {Roo.View} this
15405          * @param {Number} index The index of the target node
15406          * @param {HTMLElement} node The target node
15407          * @param {Roo.EventObject} e The raw event object
15408          */
15409             "dblclick" : true,
15410         /**
15411          * @event contextmenu
15412          * Fires when a template node is right clicked.
15413          * @param {Roo.View} this
15414          * @param {Number} index The index of the target node
15415          * @param {HTMLElement} node The target node
15416          * @param {Roo.EventObject} e The raw event object
15417          */
15418             "contextmenu" : true,
15419         /**
15420          * @event selectionchange
15421          * Fires when the selected nodes change.
15422          * @param {Roo.View} this
15423          * @param {Array} selections Array of the selected nodes
15424          */
15425             "selectionchange" : true,
15426     
15427         /**
15428          * @event beforeselect
15429          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15430          * @param {Roo.View} this
15431          * @param {HTMLElement} node The node to be selected
15432          * @param {Array} selections Array of currently selected nodes
15433          */
15434             "beforeselect" : true,
15435         /**
15436          * @event preparedata
15437          * Fires on every row to render, to allow you to change the data.
15438          * @param {Roo.View} this
15439          * @param {Object} data to be rendered (change this)
15440          */
15441           "preparedata" : true
15442           
15443           
15444         });
15445
15446
15447
15448     this.el.on({
15449         "click": this.onClick,
15450         "dblclick": this.onDblClick,
15451         "contextmenu": this.onContextMenu,
15452         scope:this
15453     });
15454
15455     this.selections = [];
15456     this.nodes = [];
15457     this.cmp = new Roo.CompositeElementLite([]);
15458     if(this.store){
15459         this.store = Roo.factory(this.store, Roo.data);
15460         this.setStore(this.store, true);
15461     }
15462     
15463     if ( this.footer && this.footer.xtype) {
15464            
15465          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15466         
15467         this.footer.dataSource = this.store;
15468         this.footer.container = fctr;
15469         this.footer = Roo.factory(this.footer, Roo);
15470         fctr.insertFirst(this.el);
15471         
15472         // this is a bit insane - as the paging toolbar seems to detach the el..
15473 //        dom.parentNode.parentNode.parentNode
15474          // they get detached?
15475     }
15476     
15477     
15478     Roo.View.superclass.constructor.call(this);
15479     
15480     
15481 };
15482
15483 Roo.extend(Roo.View, Roo.util.Observable, {
15484     
15485      /**
15486      * @cfg {Roo.data.Store} store Data store to load data from.
15487      */
15488     store : false,
15489     
15490     /**
15491      * @cfg {String|Roo.Element} el The container element.
15492      */
15493     el : '',
15494     
15495     /**
15496      * @cfg {String|Roo.Template} tpl The template used by this View 
15497      */
15498     tpl : false,
15499     /**
15500      * @cfg {String} dataName the named area of the template to use as the data area
15501      *                          Works with domtemplates roo-name="name"
15502      */
15503     dataName: false,
15504     /**
15505      * @cfg {String} selectedClass The css class to add to selected nodes
15506      */
15507     selectedClass : "x-view-selected",
15508      /**
15509      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15510      */
15511     emptyText : "",
15512     
15513     /**
15514      * @cfg {String} text to display on mask (default Loading)
15515      */
15516     mask : false,
15517     /**
15518      * @cfg {Boolean} multiSelect Allow multiple selection
15519      */
15520     multiSelect : false,
15521     /**
15522      * @cfg {Boolean} singleSelect Allow single selection
15523      */
15524     singleSelect:  false,
15525     
15526     /**
15527      * @cfg {Boolean} toggleSelect - selecting 
15528      */
15529     toggleSelect : false,
15530     
15531     /**
15532      * @cfg {Boolean} tickable - selecting 
15533      */
15534     tickable : false,
15535     
15536     /**
15537      * Returns the element this view is bound to.
15538      * @return {Roo.Element}
15539      */
15540     getEl : function(){
15541         return this.wrapEl;
15542     },
15543     
15544     
15545
15546     /**
15547      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15548      */
15549     refresh : function(){
15550         //Roo.log('refresh');
15551         var t = this.tpl;
15552         
15553         // if we are using something like 'domtemplate', then
15554         // the what gets used is:
15555         // t.applySubtemplate(NAME, data, wrapping data..)
15556         // the outer template then get' applied with
15557         //     the store 'extra data'
15558         // and the body get's added to the
15559         //      roo-name="data" node?
15560         //      <span class='roo-tpl-{name}'></span> ?????
15561         
15562         
15563         
15564         this.clearSelections();
15565         this.el.update("");
15566         var html = [];
15567         var records = this.store.getRange();
15568         if(records.length < 1) {
15569             
15570             // is this valid??  = should it render a template??
15571             
15572             this.el.update(this.emptyText);
15573             return;
15574         }
15575         var el = this.el;
15576         if (this.dataName) {
15577             this.el.update(t.apply(this.store.meta)); //????
15578             el = this.el.child('.roo-tpl-' + this.dataName);
15579         }
15580         
15581         for(var i = 0, len = records.length; i < len; i++){
15582             var data = this.prepareData(records[i].data, i, records[i]);
15583             this.fireEvent("preparedata", this, data, i, records[i]);
15584             
15585             var d = Roo.apply({}, data);
15586             
15587             if(this.tickable){
15588                 Roo.apply(d, {'roo-id' : Roo.id()});
15589                 
15590                 var _this = this;
15591             
15592                 Roo.each(this.parent.item, function(item){
15593                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15594                         return;
15595                     }
15596                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15597                 });
15598             }
15599             
15600             html[html.length] = Roo.util.Format.trim(
15601                 this.dataName ?
15602                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15603                     t.apply(d)
15604             );
15605         }
15606         
15607         
15608         
15609         el.update(html.join(""));
15610         this.nodes = el.dom.childNodes;
15611         this.updateIndexes(0);
15612     },
15613     
15614
15615     /**
15616      * Function to override to reformat the data that is sent to
15617      * the template for each node.
15618      * DEPRICATED - use the preparedata event handler.
15619      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15620      * a JSON object for an UpdateManager bound view).
15621      */
15622     prepareData : function(data, index, record)
15623     {
15624         this.fireEvent("preparedata", this, data, index, record);
15625         return data;
15626     },
15627
15628     onUpdate : function(ds, record){
15629         // Roo.log('on update');   
15630         this.clearSelections();
15631         var index = this.store.indexOf(record);
15632         var n = this.nodes[index];
15633         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15634         n.parentNode.removeChild(n);
15635         this.updateIndexes(index, index);
15636     },
15637
15638     
15639     
15640 // --------- FIXME     
15641     onAdd : function(ds, records, index)
15642     {
15643         //Roo.log(['on Add', ds, records, index] );        
15644         this.clearSelections();
15645         if(this.nodes.length == 0){
15646             this.refresh();
15647             return;
15648         }
15649         var n = this.nodes[index];
15650         for(var i = 0, len = records.length; i < len; i++){
15651             var d = this.prepareData(records[i].data, i, records[i]);
15652             if(n){
15653                 this.tpl.insertBefore(n, d);
15654             }else{
15655                 
15656                 this.tpl.append(this.el, d);
15657             }
15658         }
15659         this.updateIndexes(index);
15660     },
15661
15662     onRemove : function(ds, record, index){
15663        // Roo.log('onRemove');
15664         this.clearSelections();
15665         var el = this.dataName  ?
15666             this.el.child('.roo-tpl-' + this.dataName) :
15667             this.el; 
15668         
15669         el.dom.removeChild(this.nodes[index]);
15670         this.updateIndexes(index);
15671     },
15672
15673     /**
15674      * Refresh an individual node.
15675      * @param {Number} index
15676      */
15677     refreshNode : function(index){
15678         this.onUpdate(this.store, this.store.getAt(index));
15679     },
15680
15681     updateIndexes : function(startIndex, endIndex){
15682         var ns = this.nodes;
15683         startIndex = startIndex || 0;
15684         endIndex = endIndex || ns.length - 1;
15685         for(var i = startIndex; i <= endIndex; i++){
15686             ns[i].nodeIndex = i;
15687         }
15688     },
15689
15690     /**
15691      * Changes the data store this view uses and refresh the view.
15692      * @param {Store} store
15693      */
15694     setStore : function(store, initial){
15695         if(!initial && this.store){
15696             this.store.un("datachanged", this.refresh);
15697             this.store.un("add", this.onAdd);
15698             this.store.un("remove", this.onRemove);
15699             this.store.un("update", this.onUpdate);
15700             this.store.un("clear", this.refresh);
15701             this.store.un("beforeload", this.onBeforeLoad);
15702             this.store.un("load", this.onLoad);
15703             this.store.un("loadexception", this.onLoad);
15704         }
15705         if(store){
15706           
15707             store.on("datachanged", this.refresh, this);
15708             store.on("add", this.onAdd, this);
15709             store.on("remove", this.onRemove, this);
15710             store.on("update", this.onUpdate, this);
15711             store.on("clear", this.refresh, this);
15712             store.on("beforeload", this.onBeforeLoad, this);
15713             store.on("load", this.onLoad, this);
15714             store.on("loadexception", this.onLoad, this);
15715         }
15716         
15717         if(store){
15718             this.refresh();
15719         }
15720     },
15721     /**
15722      * onbeforeLoad - masks the loading area.
15723      *
15724      */
15725     onBeforeLoad : function(store,opts)
15726     {
15727          //Roo.log('onBeforeLoad');   
15728         if (!opts.add) {
15729             this.el.update("");
15730         }
15731         this.el.mask(this.mask ? this.mask : "Loading" ); 
15732     },
15733     onLoad : function ()
15734     {
15735         this.el.unmask();
15736     },
15737     
15738
15739     /**
15740      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15741      * @param {HTMLElement} node
15742      * @return {HTMLElement} The template node
15743      */
15744     findItemFromChild : function(node){
15745         var el = this.dataName  ?
15746             this.el.child('.roo-tpl-' + this.dataName,true) :
15747             this.el.dom; 
15748         
15749         if(!node || node.parentNode == el){
15750                     return node;
15751             }
15752             var p = node.parentNode;
15753             while(p && p != el){
15754             if(p.parentNode == el){
15755                 return p;
15756             }
15757             p = p.parentNode;
15758         }
15759             return null;
15760     },
15761
15762     /** @ignore */
15763     onClick : function(e){
15764         var item = this.findItemFromChild(e.getTarget());
15765         if(item){
15766             var index = this.indexOf(item);
15767             if(this.onItemClick(item, index, e) !== false){
15768                 this.fireEvent("click", this, index, item, e);
15769             }
15770         }else{
15771             this.clearSelections();
15772         }
15773     },
15774
15775     /** @ignore */
15776     onContextMenu : function(e){
15777         var item = this.findItemFromChild(e.getTarget());
15778         if(item){
15779             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15780         }
15781     },
15782
15783     /** @ignore */
15784     onDblClick : function(e){
15785         var item = this.findItemFromChild(e.getTarget());
15786         if(item){
15787             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15788         }
15789     },
15790
15791     onItemClick : function(item, index, e)
15792     {
15793         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15794             return false;
15795         }
15796         if (this.toggleSelect) {
15797             var m = this.isSelected(item) ? 'unselect' : 'select';
15798             //Roo.log(m);
15799             var _t = this;
15800             _t[m](item, true, false);
15801             return true;
15802         }
15803         if(this.multiSelect || this.singleSelect){
15804             if(this.multiSelect && e.shiftKey && this.lastSelection){
15805                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15806             }else{
15807                 this.select(item, this.multiSelect && e.ctrlKey);
15808                 this.lastSelection = item;
15809             }
15810             
15811             if(!this.tickable){
15812                 e.preventDefault();
15813             }
15814             
15815         }
15816         return true;
15817     },
15818
15819     /**
15820      * Get the number of selected nodes.
15821      * @return {Number}
15822      */
15823     getSelectionCount : function(){
15824         return this.selections.length;
15825     },
15826
15827     /**
15828      * Get the currently selected nodes.
15829      * @return {Array} An array of HTMLElements
15830      */
15831     getSelectedNodes : function(){
15832         return this.selections;
15833     },
15834
15835     /**
15836      * Get the indexes of the selected nodes.
15837      * @return {Array}
15838      */
15839     getSelectedIndexes : function(){
15840         var indexes = [], s = this.selections;
15841         for(var i = 0, len = s.length; i < len; i++){
15842             indexes.push(s[i].nodeIndex);
15843         }
15844         return indexes;
15845     },
15846
15847     /**
15848      * Clear all selections
15849      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15850      */
15851     clearSelections : function(suppressEvent){
15852         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15853             this.cmp.elements = this.selections;
15854             this.cmp.removeClass(this.selectedClass);
15855             this.selections = [];
15856             if(!suppressEvent){
15857                 this.fireEvent("selectionchange", this, this.selections);
15858             }
15859         }
15860     },
15861
15862     /**
15863      * Returns true if the passed node is selected
15864      * @param {HTMLElement/Number} node The node or node index
15865      * @return {Boolean}
15866      */
15867     isSelected : function(node){
15868         var s = this.selections;
15869         if(s.length < 1){
15870             return false;
15871         }
15872         node = this.getNode(node);
15873         return s.indexOf(node) !== -1;
15874     },
15875
15876     /**
15877      * Selects nodes.
15878      * @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
15879      * @param {Boolean} keepExisting (optional) true to keep existing selections
15880      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15881      */
15882     select : function(nodeInfo, keepExisting, suppressEvent){
15883         if(nodeInfo instanceof Array){
15884             if(!keepExisting){
15885                 this.clearSelections(true);
15886             }
15887             for(var i = 0, len = nodeInfo.length; i < len; i++){
15888                 this.select(nodeInfo[i], true, true);
15889             }
15890             return;
15891         } 
15892         var node = this.getNode(nodeInfo);
15893         if(!node || this.isSelected(node)){
15894             return; // already selected.
15895         }
15896         if(!keepExisting){
15897             this.clearSelections(true);
15898         }
15899         
15900         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15901             Roo.fly(node).addClass(this.selectedClass);
15902             this.selections.push(node);
15903             if(!suppressEvent){
15904                 this.fireEvent("selectionchange", this, this.selections);
15905             }
15906         }
15907         
15908         
15909     },
15910       /**
15911      * Unselects nodes.
15912      * @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
15913      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15914      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15915      */
15916     unselect : function(nodeInfo, keepExisting, suppressEvent)
15917     {
15918         if(nodeInfo instanceof Array){
15919             Roo.each(this.selections, function(s) {
15920                 this.unselect(s, nodeInfo);
15921             }, this);
15922             return;
15923         }
15924         var node = this.getNode(nodeInfo);
15925         if(!node || !this.isSelected(node)){
15926             //Roo.log("not selected");
15927             return; // not selected.
15928         }
15929         // fireevent???
15930         var ns = [];
15931         Roo.each(this.selections, function(s) {
15932             if (s == node ) {
15933                 Roo.fly(node).removeClass(this.selectedClass);
15934
15935                 return;
15936             }
15937             ns.push(s);
15938         },this);
15939         
15940         this.selections= ns;
15941         this.fireEvent("selectionchange", this, this.selections);
15942     },
15943
15944     /**
15945      * Gets a template node.
15946      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15947      * @return {HTMLElement} The node or null if it wasn't found
15948      */
15949     getNode : function(nodeInfo){
15950         if(typeof nodeInfo == "string"){
15951             return document.getElementById(nodeInfo);
15952         }else if(typeof nodeInfo == "number"){
15953             return this.nodes[nodeInfo];
15954         }
15955         return nodeInfo;
15956     },
15957
15958     /**
15959      * Gets a range template nodes.
15960      * @param {Number} startIndex
15961      * @param {Number} endIndex
15962      * @return {Array} An array of nodes
15963      */
15964     getNodes : function(start, end){
15965         var ns = this.nodes;
15966         start = start || 0;
15967         end = typeof end == "undefined" ? ns.length - 1 : end;
15968         var nodes = [];
15969         if(start <= end){
15970             for(var i = start; i <= end; i++){
15971                 nodes.push(ns[i]);
15972             }
15973         } else{
15974             for(var i = start; i >= end; i--){
15975                 nodes.push(ns[i]);
15976             }
15977         }
15978         return nodes;
15979     },
15980
15981     /**
15982      * Finds the index of the passed node
15983      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15984      * @return {Number} The index of the node or -1
15985      */
15986     indexOf : function(node){
15987         node = this.getNode(node);
15988         if(typeof node.nodeIndex == "number"){
15989             return node.nodeIndex;
15990         }
15991         var ns = this.nodes;
15992         for(var i = 0, len = ns.length; i < len; i++){
15993             if(ns[i] == node){
15994                 return i;
15995             }
15996         }
15997         return -1;
15998     }
15999 });
16000 /*
16001  * - LGPL
16002  *
16003  * based on jquery fullcalendar
16004  * 
16005  */
16006
16007 Roo.bootstrap = Roo.bootstrap || {};
16008 /**
16009  * @class Roo.bootstrap.Calendar
16010  * @extends Roo.bootstrap.Component
16011  * Bootstrap Calendar class
16012  * @cfg {Boolean} loadMask (true|false) default false
16013  * @cfg {Object} header generate the user specific header of the calendar, default false
16014
16015  * @constructor
16016  * Create a new Container
16017  * @param {Object} config The config object
16018  */
16019
16020
16021
16022 Roo.bootstrap.Calendar = function(config){
16023     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16024      this.addEvents({
16025         /**
16026              * @event select
16027              * Fires when a date is selected
16028              * @param {DatePicker} this
16029              * @param {Date} date The selected date
16030              */
16031         'select': true,
16032         /**
16033              * @event monthchange
16034              * Fires when the displayed month changes 
16035              * @param {DatePicker} this
16036              * @param {Date} date The selected month
16037              */
16038         'monthchange': true,
16039         /**
16040              * @event evententer
16041              * Fires when mouse over an event
16042              * @param {Calendar} this
16043              * @param {event} Event
16044              */
16045         'evententer': true,
16046         /**
16047              * @event eventleave
16048              * Fires when the mouse leaves an
16049              * @param {Calendar} this
16050              * @param {event}
16051              */
16052         'eventleave': true,
16053         /**
16054              * @event eventclick
16055              * Fires when the mouse click an
16056              * @param {Calendar} this
16057              * @param {event}
16058              */
16059         'eventclick': true
16060         
16061     });
16062
16063 };
16064
16065 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16066     
16067      /**
16068      * @cfg {Number} startDay
16069      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16070      */
16071     startDay : 0,
16072     
16073     loadMask : false,
16074     
16075     header : false,
16076       
16077     getAutoCreate : function(){
16078         
16079         
16080         var fc_button = function(name, corner, style, content ) {
16081             return Roo.apply({},{
16082                 tag : 'span',
16083                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16084                          (corner.length ?
16085                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16086                             ''
16087                         ),
16088                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16089                 unselectable: 'on'
16090             });
16091         };
16092         
16093         var header = {};
16094         
16095         if(!this.header){
16096             header = {
16097                 tag : 'table',
16098                 cls : 'fc-header',
16099                 style : 'width:100%',
16100                 cn : [
16101                     {
16102                         tag: 'tr',
16103                         cn : [
16104                             {
16105                                 tag : 'td',
16106                                 cls : 'fc-header-left',
16107                                 cn : [
16108                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16109                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16110                                     { tag: 'span', cls: 'fc-header-space' },
16111                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16112
16113
16114                                 ]
16115                             },
16116
16117                             {
16118                                 tag : 'td',
16119                                 cls : 'fc-header-center',
16120                                 cn : [
16121                                     {
16122                                         tag: 'span',
16123                                         cls: 'fc-header-title',
16124                                         cn : {
16125                                             tag: 'H2',
16126                                             html : 'month / year'
16127                                         }
16128                                     }
16129
16130                                 ]
16131                             },
16132                             {
16133                                 tag : 'td',
16134                                 cls : 'fc-header-right',
16135                                 cn : [
16136                               /*      fc_button('month', 'left', '', 'month' ),
16137                                     fc_button('week', '', '', 'week' ),
16138                                     fc_button('day', 'right', '', 'day' )
16139                                 */    
16140
16141                                 ]
16142                             }
16143
16144                         ]
16145                     }
16146                 ]
16147             };
16148         }
16149         
16150         header = this.header;
16151         
16152        
16153         var cal_heads = function() {
16154             var ret = [];
16155             // fixme - handle this.
16156             
16157             for (var i =0; i < Date.dayNames.length; i++) {
16158                 var d = Date.dayNames[i];
16159                 ret.push({
16160                     tag: 'th',
16161                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16162                     html : d.substring(0,3)
16163                 });
16164                 
16165             }
16166             ret[0].cls += ' fc-first';
16167             ret[6].cls += ' fc-last';
16168             return ret;
16169         };
16170         var cal_cell = function(n) {
16171             return  {
16172                 tag: 'td',
16173                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16174                 cn : [
16175                     {
16176                         cn : [
16177                             {
16178                                 cls: 'fc-day-number',
16179                                 html: 'D'
16180                             },
16181                             {
16182                                 cls: 'fc-day-content',
16183                              
16184                                 cn : [
16185                                      {
16186                                         style: 'position: relative;' // height: 17px;
16187                                     }
16188                                 ]
16189                             }
16190                             
16191                             
16192                         ]
16193                     }
16194                 ]
16195                 
16196             }
16197         };
16198         var cal_rows = function() {
16199             
16200             var ret = [];
16201             for (var r = 0; r < 6; r++) {
16202                 var row= {
16203                     tag : 'tr',
16204                     cls : 'fc-week',
16205                     cn : []
16206                 };
16207                 
16208                 for (var i =0; i < Date.dayNames.length; i++) {
16209                     var d = Date.dayNames[i];
16210                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16211
16212                 }
16213                 row.cn[0].cls+=' fc-first';
16214                 row.cn[0].cn[0].style = 'min-height:90px';
16215                 row.cn[6].cls+=' fc-last';
16216                 ret.push(row);
16217                 
16218             }
16219             ret[0].cls += ' fc-first';
16220             ret[4].cls += ' fc-prev-last';
16221             ret[5].cls += ' fc-last';
16222             return ret;
16223             
16224         };
16225         
16226         var cal_table = {
16227             tag: 'table',
16228             cls: 'fc-border-separate',
16229             style : 'width:100%',
16230             cellspacing  : 0,
16231             cn : [
16232                 { 
16233                     tag: 'thead',
16234                     cn : [
16235                         { 
16236                             tag: 'tr',
16237                             cls : 'fc-first fc-last',
16238                             cn : cal_heads()
16239                         }
16240                     ]
16241                 },
16242                 { 
16243                     tag: 'tbody',
16244                     cn : cal_rows()
16245                 }
16246                   
16247             ]
16248         };
16249          
16250          var cfg = {
16251             cls : 'fc fc-ltr',
16252             cn : [
16253                 header,
16254                 {
16255                     cls : 'fc-content',
16256                     style : "position: relative;",
16257                     cn : [
16258                         {
16259                             cls : 'fc-view fc-view-month fc-grid',
16260                             style : 'position: relative',
16261                             unselectable : 'on',
16262                             cn : [
16263                                 {
16264                                     cls : 'fc-event-container',
16265                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16266                                 },
16267                                 cal_table
16268                             ]
16269                         }
16270                     ]
16271     
16272                 }
16273            ] 
16274             
16275         };
16276         
16277          
16278         
16279         return cfg;
16280     },
16281     
16282     
16283     initEvents : function()
16284     {
16285         if(!this.store){
16286             throw "can not find store for calendar";
16287         }
16288         
16289         var mark = {
16290             tag: "div",
16291             cls:"x-dlg-mask",
16292             style: "text-align:center",
16293             cn: [
16294                 {
16295                     tag: "div",
16296                     style: "background-color:white;width:50%;margin:250 auto",
16297                     cn: [
16298                         {
16299                             tag: "img",
16300                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16301                         },
16302                         {
16303                             tag: "span",
16304                             html: "Loading"
16305                         }
16306                         
16307                     ]
16308                 }
16309             ]
16310         };
16311         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16312         
16313         var size = this.el.select('.fc-content', true).first().getSize();
16314         this.maskEl.setSize(size.width, size.height);
16315         this.maskEl.enableDisplayMode("block");
16316         if(!this.loadMask){
16317             this.maskEl.hide();
16318         }
16319         
16320         this.store = Roo.factory(this.store, Roo.data);
16321         this.store.on('load', this.onLoad, this);
16322         this.store.on('beforeload', this.onBeforeLoad, this);
16323         
16324         this.resize();
16325         
16326         this.cells = this.el.select('.fc-day',true);
16327         //Roo.log(this.cells);
16328         this.textNodes = this.el.query('.fc-day-number');
16329         this.cells.addClassOnOver('fc-state-hover');
16330         
16331         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16332         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16333         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16334         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16335         
16336         this.on('monthchange', this.onMonthChange, this);
16337         
16338         this.update(new Date().clearTime());
16339     },
16340     
16341     resize : function() {
16342         var sz  = this.el.getSize();
16343         
16344         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16345         this.el.select('.fc-day-content div',true).setHeight(34);
16346     },
16347     
16348     
16349     // private
16350     showPrevMonth : function(e){
16351         this.update(this.activeDate.add("mo", -1));
16352     },
16353     showToday : function(e){
16354         this.update(new Date().clearTime());
16355     },
16356     // private
16357     showNextMonth : function(e){
16358         this.update(this.activeDate.add("mo", 1));
16359     },
16360
16361     // private
16362     showPrevYear : function(){
16363         this.update(this.activeDate.add("y", -1));
16364     },
16365
16366     // private
16367     showNextYear : function(){
16368         this.update(this.activeDate.add("y", 1));
16369     },
16370
16371     
16372    // private
16373     update : function(date)
16374     {
16375         var vd = this.activeDate;
16376         this.activeDate = date;
16377 //        if(vd && this.el){
16378 //            var t = date.getTime();
16379 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16380 //                Roo.log('using add remove');
16381 //                
16382 //                this.fireEvent('monthchange', this, date);
16383 //                
16384 //                this.cells.removeClass("fc-state-highlight");
16385 //                this.cells.each(function(c){
16386 //                   if(c.dateValue == t){
16387 //                       c.addClass("fc-state-highlight");
16388 //                       setTimeout(function(){
16389 //                            try{c.dom.firstChild.focus();}catch(e){}
16390 //                       }, 50);
16391 //                       return false;
16392 //                   }
16393 //                   return true;
16394 //                });
16395 //                return;
16396 //            }
16397 //        }
16398         
16399         var days = date.getDaysInMonth();
16400         
16401         var firstOfMonth = date.getFirstDateOfMonth();
16402         var startingPos = firstOfMonth.getDay()-this.startDay;
16403         
16404         if(startingPos < this.startDay){
16405             startingPos += 7;
16406         }
16407         
16408         var pm = date.add(Date.MONTH, -1);
16409         var prevStart = pm.getDaysInMonth()-startingPos;
16410 //        
16411         this.cells = this.el.select('.fc-day',true);
16412         this.textNodes = this.el.query('.fc-day-number');
16413         this.cells.addClassOnOver('fc-state-hover');
16414         
16415         var cells = this.cells.elements;
16416         var textEls = this.textNodes;
16417         
16418         Roo.each(cells, function(cell){
16419             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16420         });
16421         
16422         days += startingPos;
16423
16424         // convert everything to numbers so it's fast
16425         var day = 86400000;
16426         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16427         //Roo.log(d);
16428         //Roo.log(pm);
16429         //Roo.log(prevStart);
16430         
16431         var today = new Date().clearTime().getTime();
16432         var sel = date.clearTime().getTime();
16433         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16434         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16435         var ddMatch = this.disabledDatesRE;
16436         var ddText = this.disabledDatesText;
16437         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16438         var ddaysText = this.disabledDaysText;
16439         var format = this.format;
16440         
16441         var setCellClass = function(cal, cell){
16442             cell.row = 0;
16443             cell.events = [];
16444             cell.more = [];
16445             //Roo.log('set Cell Class');
16446             cell.title = "";
16447             var t = d.getTime();
16448             
16449             //Roo.log(d);
16450             
16451             cell.dateValue = t;
16452             if(t == today){
16453                 cell.className += " fc-today";
16454                 cell.className += " fc-state-highlight";
16455                 cell.title = cal.todayText;
16456             }
16457             if(t == sel){
16458                 // disable highlight in other month..
16459                 //cell.className += " fc-state-highlight";
16460                 
16461             }
16462             // disabling
16463             if(t < min) {
16464                 cell.className = " fc-state-disabled";
16465                 cell.title = cal.minText;
16466                 return;
16467             }
16468             if(t > max) {
16469                 cell.className = " fc-state-disabled";
16470                 cell.title = cal.maxText;
16471                 return;
16472             }
16473             if(ddays){
16474                 if(ddays.indexOf(d.getDay()) != -1){
16475                     cell.title = ddaysText;
16476                     cell.className = " fc-state-disabled";
16477                 }
16478             }
16479             if(ddMatch && format){
16480                 var fvalue = d.dateFormat(format);
16481                 if(ddMatch.test(fvalue)){
16482                     cell.title = ddText.replace("%0", fvalue);
16483                     cell.className = " fc-state-disabled";
16484                 }
16485             }
16486             
16487             if (!cell.initialClassName) {
16488                 cell.initialClassName = cell.dom.className;
16489             }
16490             
16491             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16492         };
16493
16494         var i = 0;
16495         
16496         for(; i < startingPos; i++) {
16497             textEls[i].innerHTML = (++prevStart);
16498             d.setDate(d.getDate()+1);
16499             
16500             cells[i].className = "fc-past fc-other-month";
16501             setCellClass(this, cells[i]);
16502         }
16503         
16504         var intDay = 0;
16505         
16506         for(; i < days; i++){
16507             intDay = i - startingPos + 1;
16508             textEls[i].innerHTML = (intDay);
16509             d.setDate(d.getDate()+1);
16510             
16511             cells[i].className = ''; // "x-date-active";
16512             setCellClass(this, cells[i]);
16513         }
16514         var extraDays = 0;
16515         
16516         for(; i < 42; i++) {
16517             textEls[i].innerHTML = (++extraDays);
16518             d.setDate(d.getDate()+1);
16519             
16520             cells[i].className = "fc-future fc-other-month";
16521             setCellClass(this, cells[i]);
16522         }
16523         
16524         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16525         
16526         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16527         
16528         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16529         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16530         
16531         if(totalRows != 6){
16532             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16533             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16534         }
16535         
16536         this.fireEvent('monthchange', this, date);
16537         
16538         
16539         /*
16540         if(!this.internalRender){
16541             var main = this.el.dom.firstChild;
16542             var w = main.offsetWidth;
16543             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16544             Roo.fly(main).setWidth(w);
16545             this.internalRender = true;
16546             // opera does not respect the auto grow header center column
16547             // then, after it gets a width opera refuses to recalculate
16548             // without a second pass
16549             if(Roo.isOpera && !this.secondPass){
16550                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16551                 this.secondPass = true;
16552                 this.update.defer(10, this, [date]);
16553             }
16554         }
16555         */
16556         
16557     },
16558     
16559     findCell : function(dt) {
16560         dt = dt.clearTime().getTime();
16561         var ret = false;
16562         this.cells.each(function(c){
16563             //Roo.log("check " +c.dateValue + '?=' + dt);
16564             if(c.dateValue == dt){
16565                 ret = c;
16566                 return false;
16567             }
16568             return true;
16569         });
16570         
16571         return ret;
16572     },
16573     
16574     findCells : function(ev) {
16575         var s = ev.start.clone().clearTime().getTime();
16576        // Roo.log(s);
16577         var e= ev.end.clone().clearTime().getTime();
16578        // Roo.log(e);
16579         var ret = [];
16580         this.cells.each(function(c){
16581              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16582             
16583             if(c.dateValue > e){
16584                 return ;
16585             }
16586             if(c.dateValue < s){
16587                 return ;
16588             }
16589             ret.push(c);
16590         });
16591         
16592         return ret;    
16593     },
16594     
16595 //    findBestRow: function(cells)
16596 //    {
16597 //        var ret = 0;
16598 //        
16599 //        for (var i =0 ; i < cells.length;i++) {
16600 //            ret  = Math.max(cells[i].rows || 0,ret);
16601 //        }
16602 //        return ret;
16603 //        
16604 //    },
16605     
16606     
16607     addItem : function(ev)
16608     {
16609         // look for vertical location slot in
16610         var cells = this.findCells(ev);
16611         
16612 //        ev.row = this.findBestRow(cells);
16613         
16614         // work out the location.
16615         
16616         var crow = false;
16617         var rows = [];
16618         for(var i =0; i < cells.length; i++) {
16619             
16620             cells[i].row = cells[0].row;
16621             
16622             if(i == 0){
16623                 cells[i].row = cells[i].row + 1;
16624             }
16625             
16626             if (!crow) {
16627                 crow = {
16628                     start : cells[i],
16629                     end :  cells[i]
16630                 };
16631                 continue;
16632             }
16633             if (crow.start.getY() == cells[i].getY()) {
16634                 // on same row.
16635                 crow.end = cells[i];
16636                 continue;
16637             }
16638             // different row.
16639             rows.push(crow);
16640             crow = {
16641                 start: cells[i],
16642                 end : cells[i]
16643             };
16644             
16645         }
16646         
16647         rows.push(crow);
16648         ev.els = [];
16649         ev.rows = rows;
16650         ev.cells = cells;
16651         
16652         cells[0].events.push(ev);
16653         
16654         this.calevents.push(ev);
16655     },
16656     
16657     clearEvents: function() {
16658         
16659         if(!this.calevents){
16660             return;
16661         }
16662         
16663         Roo.each(this.cells.elements, function(c){
16664             c.row = 0;
16665             c.events = [];
16666             c.more = [];
16667         });
16668         
16669         Roo.each(this.calevents, function(e) {
16670             Roo.each(e.els, function(el) {
16671                 el.un('mouseenter' ,this.onEventEnter, this);
16672                 el.un('mouseleave' ,this.onEventLeave, this);
16673                 el.remove();
16674             },this);
16675         },this);
16676         
16677         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16678             e.remove();
16679         });
16680         
16681     },
16682     
16683     renderEvents: function()
16684     {   
16685         var _this = this;
16686         
16687         this.cells.each(function(c) {
16688             
16689             if(c.row < 5){
16690                 return;
16691             }
16692             
16693             var ev = c.events;
16694             
16695             var r = 4;
16696             if(c.row != c.events.length){
16697                 r = 4 - (4 - (c.row - c.events.length));
16698             }
16699             
16700             c.events = ev.slice(0, r);
16701             c.more = ev.slice(r);
16702             
16703             if(c.more.length && c.more.length == 1){
16704                 c.events.push(c.more.pop());
16705             }
16706             
16707             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16708             
16709         });
16710             
16711         this.cells.each(function(c) {
16712             
16713             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16714             
16715             
16716             for (var e = 0; e < c.events.length; e++){
16717                 var ev = c.events[e];
16718                 var rows = ev.rows;
16719                 
16720                 for(var i = 0; i < rows.length; i++) {
16721                 
16722                     // how many rows should it span..
16723
16724                     var  cfg = {
16725                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16726                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16727
16728                         unselectable : "on",
16729                         cn : [
16730                             {
16731                                 cls: 'fc-event-inner',
16732                                 cn : [
16733     //                                {
16734     //                                  tag:'span',
16735     //                                  cls: 'fc-event-time',
16736     //                                  html : cells.length > 1 ? '' : ev.time
16737     //                                },
16738                                     {
16739                                       tag:'span',
16740                                       cls: 'fc-event-title',
16741                                       html : String.format('{0}', ev.title)
16742                                     }
16743
16744
16745                                 ]
16746                             },
16747                             {
16748                                 cls: 'ui-resizable-handle ui-resizable-e',
16749                                 html : '&nbsp;&nbsp;&nbsp'
16750                             }
16751
16752                         ]
16753                     };
16754
16755                     if (i == 0) {
16756                         cfg.cls += ' fc-event-start';
16757                     }
16758                     if ((i+1) == rows.length) {
16759                         cfg.cls += ' fc-event-end';
16760                     }
16761
16762                     var ctr = _this.el.select('.fc-event-container',true).first();
16763                     var cg = ctr.createChild(cfg);
16764
16765                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16766                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16767
16768                     var r = (c.more.length) ? 1 : 0;
16769                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16770                     cg.setWidth(ebox.right - sbox.x -2);
16771
16772                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16773                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16774                     cg.on('click', _this.onEventClick, _this, ev);
16775
16776                     ev.els.push(cg);
16777                     
16778                 }
16779                 
16780             }
16781             
16782             
16783             if(c.more.length){
16784                 var  cfg = {
16785                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16786                     style : 'position: absolute',
16787                     unselectable : "on",
16788                     cn : [
16789                         {
16790                             cls: 'fc-event-inner',
16791                             cn : [
16792                                 {
16793                                   tag:'span',
16794                                   cls: 'fc-event-title',
16795                                   html : 'More'
16796                                 }
16797
16798
16799                             ]
16800                         },
16801                         {
16802                             cls: 'ui-resizable-handle ui-resizable-e',
16803                             html : '&nbsp;&nbsp;&nbsp'
16804                         }
16805
16806                     ]
16807                 };
16808
16809                 var ctr = _this.el.select('.fc-event-container',true).first();
16810                 var cg = ctr.createChild(cfg);
16811
16812                 var sbox = c.select('.fc-day-content',true).first().getBox();
16813                 var ebox = c.select('.fc-day-content',true).first().getBox();
16814                 //Roo.log(cg);
16815                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16816                 cg.setWidth(ebox.right - sbox.x -2);
16817
16818                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16819                 
16820             }
16821             
16822         });
16823         
16824         
16825         
16826     },
16827     
16828     onEventEnter: function (e, el,event,d) {
16829         this.fireEvent('evententer', this, el, event);
16830     },
16831     
16832     onEventLeave: function (e, el,event,d) {
16833         this.fireEvent('eventleave', this, el, event);
16834     },
16835     
16836     onEventClick: function (e, el,event,d) {
16837         this.fireEvent('eventclick', this, el, event);
16838     },
16839     
16840     onMonthChange: function () {
16841         this.store.load();
16842     },
16843     
16844     onMoreEventClick: function(e, el, more)
16845     {
16846         var _this = this;
16847         
16848         this.calpopover.placement = 'right';
16849         this.calpopover.setTitle('More');
16850         
16851         this.calpopover.setContent('');
16852         
16853         var ctr = this.calpopover.el.select('.popover-content', true).first();
16854         
16855         Roo.each(more, function(m){
16856             var cfg = {
16857                 cls : 'fc-event-hori fc-event-draggable',
16858                 html : m.title
16859             };
16860             var cg = ctr.createChild(cfg);
16861             
16862             cg.on('click', _this.onEventClick, _this, m);
16863         });
16864         
16865         this.calpopover.show(el);
16866         
16867         
16868     },
16869     
16870     onLoad: function () 
16871     {   
16872         this.calevents = [];
16873         var cal = this;
16874         
16875         if(this.store.getCount() > 0){
16876             this.store.data.each(function(d){
16877                cal.addItem({
16878                     id : d.data.id,
16879                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16880                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16881                     time : d.data.start_time,
16882                     title : d.data.title,
16883                     description : d.data.description,
16884                     venue : d.data.venue
16885                 });
16886             });
16887         }
16888         
16889         this.renderEvents();
16890         
16891         if(this.calevents.length && this.loadMask){
16892             this.maskEl.hide();
16893         }
16894     },
16895     
16896     onBeforeLoad: function()
16897     {
16898         this.clearEvents();
16899         if(this.loadMask){
16900             this.maskEl.show();
16901         }
16902     }
16903 });
16904
16905  
16906  /*
16907  * - LGPL
16908  *
16909  * element
16910  * 
16911  */
16912
16913 /**
16914  * @class Roo.bootstrap.Popover
16915  * @extends Roo.bootstrap.Component
16916  * Bootstrap Popover class
16917  * @cfg {String} html contents of the popover   (or false to use children..)
16918  * @cfg {String} title of popover (or false to hide)
16919  * @cfg {String} placement how it is placed
16920  * @cfg {String} trigger click || hover (or false to trigger manually)
16921  * @cfg {String} over what (parent or false to trigger manually.)
16922  * @cfg {Number} delay - delay before showing
16923  
16924  * @constructor
16925  * Create a new Popover
16926  * @param {Object} config The config object
16927  */
16928
16929 Roo.bootstrap.Popover = function(config){
16930     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16931     
16932     this.addEvents({
16933         // raw events
16934          /**
16935          * @event show
16936          * After the popover show
16937          * 
16938          * @param {Roo.bootstrap.Popover} this
16939          */
16940         "show" : true,
16941         /**
16942          * @event hide
16943          * After the popover hide
16944          * 
16945          * @param {Roo.bootstrap.Popover} this
16946          */
16947         "hide" : true
16948     });
16949 };
16950
16951 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16952     
16953     title: 'Fill in a title',
16954     html: false,
16955     
16956     placement : 'right',
16957     trigger : 'hover', // hover
16958     
16959     delay : 0,
16960     
16961     over: 'parent',
16962     
16963     can_build_overlaid : false,
16964     
16965     getChildContainer : function()
16966     {
16967         return this.el.select('.popover-content',true).first();
16968     },
16969     
16970     getAutoCreate : function(){
16971          
16972         var cfg = {
16973            cls : 'popover roo-dynamic',
16974            style: 'display:block',
16975            cn : [
16976                 {
16977                     cls : 'arrow'
16978                 },
16979                 {
16980                     cls : 'popover-inner',
16981                     cn : [
16982                         {
16983                             tag: 'h3',
16984                             cls: 'popover-title',
16985                             html : this.title
16986                         },
16987                         {
16988                             cls : 'popover-content',
16989                             html : this.html
16990                         }
16991                     ]
16992                     
16993                 }
16994            ]
16995         };
16996         
16997         return cfg;
16998     },
16999     setTitle: function(str)
17000     {
17001         this.title = str;
17002         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17003     },
17004     setContent: function(str)
17005     {
17006         this.html = str;
17007         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17008     },
17009     // as it get's added to the bottom of the page.
17010     onRender : function(ct, position)
17011     {
17012         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17013         if(!this.el){
17014             var cfg = Roo.apply({},  this.getAutoCreate());
17015             cfg.id = Roo.id();
17016             
17017             if (this.cls) {
17018                 cfg.cls += ' ' + this.cls;
17019             }
17020             if (this.style) {
17021                 cfg.style = this.style;
17022             }
17023             //Roo.log("adding to ");
17024             this.el = Roo.get(document.body).createChild(cfg, position);
17025 //            Roo.log(this.el);
17026         }
17027         this.initEvents();
17028     },
17029     
17030     initEvents : function()
17031     {
17032         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17033         this.el.enableDisplayMode('block');
17034         this.el.hide();
17035         if (this.over === false) {
17036             return; 
17037         }
17038         if (this.triggers === false) {
17039             return;
17040         }
17041         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17042         var triggers = this.trigger ? this.trigger.split(' ') : [];
17043         Roo.each(triggers, function(trigger) {
17044         
17045             if (trigger == 'click') {
17046                 on_el.on('click', this.toggle, this);
17047             } else if (trigger != 'manual') {
17048                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17049                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17050       
17051                 on_el.on(eventIn  ,this.enter, this);
17052                 on_el.on(eventOut, this.leave, this);
17053             }
17054         }, this);
17055         
17056     },
17057     
17058     
17059     // private
17060     timeout : null,
17061     hoverState : null,
17062     
17063     toggle : function () {
17064         this.hoverState == 'in' ? this.leave() : this.enter();
17065     },
17066     
17067     enter : function () {
17068         
17069         clearTimeout(this.timeout);
17070     
17071         this.hoverState = 'in';
17072     
17073         if (!this.delay || !this.delay.show) {
17074             this.show();
17075             return;
17076         }
17077         var _t = this;
17078         this.timeout = setTimeout(function () {
17079             if (_t.hoverState == 'in') {
17080                 _t.show();
17081             }
17082         }, this.delay.show)
17083     },
17084     
17085     leave : function() {
17086         clearTimeout(this.timeout);
17087     
17088         this.hoverState = 'out';
17089     
17090         if (!this.delay || !this.delay.hide) {
17091             this.hide();
17092             return;
17093         }
17094         var _t = this;
17095         this.timeout = setTimeout(function () {
17096             if (_t.hoverState == 'out') {
17097                 _t.hide();
17098             }
17099         }, this.delay.hide)
17100     },
17101     
17102     show : function (on_el)
17103     {
17104         if (!on_el) {
17105             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17106         }
17107         
17108         // set content.
17109         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17110         if (this.html !== false) {
17111             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17112         }
17113         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17114         if (!this.title.length) {
17115             this.el.select('.popover-title',true).hide();
17116         }
17117         
17118         var placement = typeof this.placement == 'function' ?
17119             this.placement.call(this, this.el, on_el) :
17120             this.placement;
17121             
17122         var autoToken = /\s?auto?\s?/i;
17123         var autoPlace = autoToken.test(placement);
17124         if (autoPlace) {
17125             placement = placement.replace(autoToken, '') || 'top';
17126         }
17127         
17128         //this.el.detach()
17129         //this.el.setXY([0,0]);
17130         this.el.show();
17131         this.el.dom.style.display='block';
17132         this.el.addClass(placement);
17133         
17134         //this.el.appendTo(on_el);
17135         
17136         var p = this.getPosition();
17137         var box = this.el.getBox();
17138         
17139         if (autoPlace) {
17140             // fixme..
17141         }
17142         var align = Roo.bootstrap.Popover.alignment[placement];
17143         this.el.alignTo(on_el, align[0],align[1]);
17144         //var arrow = this.el.select('.arrow',true).first();
17145         //arrow.set(align[2], 
17146         
17147         this.el.addClass('in');
17148         
17149         
17150         if (this.el.hasClass('fade')) {
17151             // fade it?
17152         }
17153         
17154         this.hoverState = 'in';
17155         
17156         this.fireEvent('show', this);
17157         
17158     },
17159     hide : function()
17160     {
17161         this.el.setXY([0,0]);
17162         this.el.removeClass('in');
17163         this.el.hide();
17164         this.hoverState = null;
17165         
17166         this.fireEvent('hide', this);
17167     }
17168     
17169 });
17170
17171 Roo.bootstrap.Popover.alignment = {
17172     'left' : ['r-l', [-10,0], 'right'],
17173     'right' : ['l-r', [10,0], 'left'],
17174     'bottom' : ['t-b', [0,10], 'top'],
17175     'top' : [ 'b-t', [0,-10], 'bottom']
17176 };
17177
17178  /*
17179  * - LGPL
17180  *
17181  * Progress
17182  * 
17183  */
17184
17185 /**
17186  * @class Roo.bootstrap.Progress
17187  * @extends Roo.bootstrap.Component
17188  * Bootstrap Progress class
17189  * @cfg {Boolean} striped striped of the progress bar
17190  * @cfg {Boolean} active animated of the progress bar
17191  * 
17192  * 
17193  * @constructor
17194  * Create a new Progress
17195  * @param {Object} config The config object
17196  */
17197
17198 Roo.bootstrap.Progress = function(config){
17199     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17200 };
17201
17202 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17203     
17204     striped : false,
17205     active: false,
17206     
17207     getAutoCreate : function(){
17208         var cfg = {
17209             tag: 'div',
17210             cls: 'progress'
17211         };
17212         
17213         
17214         if(this.striped){
17215             cfg.cls += ' progress-striped';
17216         }
17217       
17218         if(this.active){
17219             cfg.cls += ' active';
17220         }
17221         
17222         
17223         return cfg;
17224     }
17225    
17226 });
17227
17228  
17229
17230  /*
17231  * - LGPL
17232  *
17233  * ProgressBar
17234  * 
17235  */
17236
17237 /**
17238  * @class Roo.bootstrap.ProgressBar
17239  * @extends Roo.bootstrap.Component
17240  * Bootstrap ProgressBar class
17241  * @cfg {Number} aria_valuenow aria-value now
17242  * @cfg {Number} aria_valuemin aria-value min
17243  * @cfg {Number} aria_valuemax aria-value max
17244  * @cfg {String} label label for the progress bar
17245  * @cfg {String} panel (success | info | warning | danger )
17246  * @cfg {String} role role of the progress bar
17247  * @cfg {String} sr_only text
17248  * 
17249  * 
17250  * @constructor
17251  * Create a new ProgressBar
17252  * @param {Object} config The config object
17253  */
17254
17255 Roo.bootstrap.ProgressBar = function(config){
17256     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17257 };
17258
17259 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17260     
17261     aria_valuenow : 0,
17262     aria_valuemin : 0,
17263     aria_valuemax : 100,
17264     label : false,
17265     panel : false,
17266     role : false,
17267     sr_only: false,
17268     
17269     getAutoCreate : function()
17270     {
17271         
17272         var cfg = {
17273             tag: 'div',
17274             cls: 'progress-bar',
17275             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17276         };
17277         
17278         if(this.sr_only){
17279             cfg.cn = {
17280                 tag: 'span',
17281                 cls: 'sr-only',
17282                 html: this.sr_only
17283             }
17284         }
17285         
17286         if(this.role){
17287             cfg.role = this.role;
17288         }
17289         
17290         if(this.aria_valuenow){
17291             cfg['aria-valuenow'] = this.aria_valuenow;
17292         }
17293         
17294         if(this.aria_valuemin){
17295             cfg['aria-valuemin'] = this.aria_valuemin;
17296         }
17297         
17298         if(this.aria_valuemax){
17299             cfg['aria-valuemax'] = this.aria_valuemax;
17300         }
17301         
17302         if(this.label && !this.sr_only){
17303             cfg.html = this.label;
17304         }
17305         
17306         if(this.panel){
17307             cfg.cls += ' progress-bar-' + this.panel;
17308         }
17309         
17310         return cfg;
17311     },
17312     
17313     update : function(aria_valuenow)
17314     {
17315         this.aria_valuenow = aria_valuenow;
17316         
17317         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17318     }
17319    
17320 });
17321
17322  
17323
17324  /*
17325  * - LGPL
17326  *
17327  * column
17328  * 
17329  */
17330
17331 /**
17332  * @class Roo.bootstrap.TabGroup
17333  * @extends Roo.bootstrap.Column
17334  * Bootstrap Column class
17335  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17336  * @cfg {Boolean} carousel true to make the group behave like a carousel
17337  * @cfg {Boolean} bullets show bullets for the panels
17338  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17339  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17340  * @cfg {Boolean} showarrow (true|false) show arrow default true
17341  * 
17342  * @constructor
17343  * Create a new TabGroup
17344  * @param {Object} config The config object
17345  */
17346
17347 Roo.bootstrap.TabGroup = function(config){
17348     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17349     if (!this.navId) {
17350         this.navId = Roo.id();
17351     }
17352     this.tabs = [];
17353     Roo.bootstrap.TabGroup.register(this);
17354     
17355 };
17356
17357 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17358     
17359     carousel : false,
17360     transition : false,
17361     bullets : 0,
17362     timer : 0,
17363     autoslide : false,
17364     slideFn : false,
17365     slideOnTouch : false,
17366     showarrow : true,
17367     
17368     getAutoCreate : function()
17369     {
17370         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17371         
17372         cfg.cls += ' tab-content';
17373         
17374         if (this.carousel) {
17375             cfg.cls += ' carousel slide';
17376             
17377             cfg.cn = [{
17378                cls : 'carousel-inner',
17379                cn : []
17380             }];
17381         
17382             if(this.bullets  && !Roo.isTouch){
17383                 
17384                 var bullets = {
17385                     cls : 'carousel-bullets',
17386                     cn : []
17387                 };
17388                
17389                 if(this.bullets_cls){
17390                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17391                 }
17392                 
17393                 bullets.cn.push({
17394                     cls : 'clear'
17395                 });
17396                 
17397                 cfg.cn[0].cn.push(bullets);
17398             }
17399             
17400             if(this.showarrow){
17401                 cfg.cn[0].cn.push({
17402                     tag : 'div',
17403                     class : 'carousel-arrow',
17404                     cn : [
17405                         {
17406                             tag : 'div',
17407                             class : 'carousel-prev',
17408                             cn : [
17409                                 {
17410                                     tag : 'i',
17411                                     class : 'fa fa-chevron-left'
17412                                 }
17413                             ]
17414                         },
17415                         {
17416                             tag : 'div',
17417                             class : 'carousel-next',
17418                             cn : [
17419                                 {
17420                                     tag : 'i',
17421                                     class : 'fa fa-chevron-right'
17422                                 }
17423                             ]
17424                         }
17425                     ]
17426                 });
17427             }
17428             
17429         }
17430         
17431         return cfg;
17432     },
17433     
17434     initEvents:  function()
17435     {
17436 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17437 //            this.el.on("touchstart", this.onTouchStart, this);
17438 //        }
17439         
17440         if(this.autoslide){
17441             var _this = this;
17442             
17443             this.slideFn = window.setInterval(function() {
17444                 _this.showPanelNext();
17445             }, this.timer);
17446         }
17447         
17448         if(this.showarrow){
17449             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17450             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17451         }
17452         
17453         
17454     },
17455     
17456 //    onTouchStart : function(e, el, o)
17457 //    {
17458 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17459 //            return;
17460 //        }
17461 //        
17462 //        this.showPanelNext();
17463 //    },
17464     
17465     
17466     getChildContainer : function()
17467     {
17468         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17469     },
17470     
17471     /**
17472     * register a Navigation item
17473     * @param {Roo.bootstrap.NavItem} the navitem to add
17474     */
17475     register : function(item)
17476     {
17477         this.tabs.push( item);
17478         item.navId = this.navId; // not really needed..
17479         this.addBullet();
17480     
17481     },
17482     
17483     getActivePanel : function()
17484     {
17485         var r = false;
17486         Roo.each(this.tabs, function(t) {
17487             if (t.active) {
17488                 r = t;
17489                 return false;
17490             }
17491             return null;
17492         });
17493         return r;
17494         
17495     },
17496     getPanelByName : function(n)
17497     {
17498         var r = false;
17499         Roo.each(this.tabs, function(t) {
17500             if (t.tabId == n) {
17501                 r = t;
17502                 return false;
17503             }
17504             return null;
17505         });
17506         return r;
17507     },
17508     indexOfPanel : function(p)
17509     {
17510         var r = false;
17511         Roo.each(this.tabs, function(t,i) {
17512             if (t.tabId == p.tabId) {
17513                 r = i;
17514                 return false;
17515             }
17516             return null;
17517         });
17518         return r;
17519     },
17520     /**
17521      * show a specific panel
17522      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17523      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17524      */
17525     showPanel : function (pan)
17526     {
17527         if(this.transition || typeof(pan) == 'undefined'){
17528             Roo.log("waiting for the transitionend");
17529             return;
17530         }
17531         
17532         if (typeof(pan) == 'number') {
17533             pan = this.tabs[pan];
17534         }
17535         
17536         if (typeof(pan) == 'string') {
17537             pan = this.getPanelByName(pan);
17538         }
17539         
17540         var cur = this.getActivePanel();
17541         
17542         if(!pan || !cur){
17543             Roo.log('pan or acitve pan is undefined');
17544             return false;
17545         }
17546         
17547         if (pan.tabId == this.getActivePanel().tabId) {
17548             return true;
17549         }
17550         
17551         if (false === cur.fireEvent('beforedeactivate')) {
17552             return false;
17553         }
17554         
17555         if(this.bullets > 0 && !Roo.isTouch){
17556             this.setActiveBullet(this.indexOfPanel(pan));
17557         }
17558         
17559         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17560             
17561             this.transition = true;
17562             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17563             var lr = dir == 'next' ? 'left' : 'right';
17564             pan.el.addClass(dir); // or prev
17565             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17566             cur.el.addClass(lr); // or right
17567             pan.el.addClass(lr);
17568             
17569             var _this = this;
17570             cur.el.on('transitionend', function() {
17571                 Roo.log("trans end?");
17572                 
17573                 pan.el.removeClass([lr,dir]);
17574                 pan.setActive(true);
17575                 
17576                 cur.el.removeClass([lr]);
17577                 cur.setActive(false);
17578                 
17579                 _this.transition = false;
17580                 
17581             }, this, { single:  true } );
17582             
17583             return true;
17584         }
17585         
17586         cur.setActive(false);
17587         pan.setActive(true);
17588         
17589         return true;
17590         
17591     },
17592     showPanelNext : function()
17593     {
17594         var i = this.indexOfPanel(this.getActivePanel());
17595         
17596         if (i >= this.tabs.length - 1 && !this.autoslide) {
17597             return;
17598         }
17599         
17600         if (i >= this.tabs.length - 1 && this.autoslide) {
17601             i = -1;
17602         }
17603         
17604         this.showPanel(this.tabs[i+1]);
17605     },
17606     
17607     showPanelPrev : function()
17608     {
17609         var i = this.indexOfPanel(this.getActivePanel());
17610         
17611         if (i  < 1 && !this.autoslide) {
17612             return;
17613         }
17614         
17615         if (i < 1 && this.autoslide) {
17616             i = this.tabs.length;
17617         }
17618         
17619         this.showPanel(this.tabs[i-1]);
17620     },
17621     
17622     
17623     addBullet: function()
17624     {
17625         if(!this.bullets || Roo.isTouch){
17626             return;
17627         }
17628         var ctr = this.el.select('.carousel-bullets',true).first();
17629         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17630         var bullet = ctr.createChild({
17631             cls : 'bullet bullet-' + i
17632         },ctr.dom.lastChild);
17633         
17634         
17635         var _this = this;
17636         
17637         bullet.on('click', (function(e, el, o, ii, t){
17638
17639             e.preventDefault();
17640
17641             this.showPanel(ii);
17642
17643             if(this.autoslide && this.slideFn){
17644                 clearInterval(this.slideFn);
17645                 this.slideFn = window.setInterval(function() {
17646                     _this.showPanelNext();
17647                 }, this.timer);
17648             }
17649
17650         }).createDelegate(this, [i, bullet], true));
17651                 
17652         
17653     },
17654      
17655     setActiveBullet : function(i)
17656     {
17657         if(Roo.isTouch){
17658             return;
17659         }
17660         
17661         Roo.each(this.el.select('.bullet', true).elements, function(el){
17662             el.removeClass('selected');
17663         });
17664
17665         var bullet = this.el.select('.bullet-' + i, true).first();
17666         
17667         if(!bullet){
17668             return;
17669         }
17670         
17671         bullet.addClass('selected');
17672     }
17673     
17674     
17675   
17676 });
17677
17678  
17679
17680  
17681  
17682 Roo.apply(Roo.bootstrap.TabGroup, {
17683     
17684     groups: {},
17685      /**
17686     * register a Navigation Group
17687     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17688     */
17689     register : function(navgrp)
17690     {
17691         this.groups[navgrp.navId] = navgrp;
17692         
17693     },
17694     /**
17695     * fetch a Navigation Group based on the navigation ID
17696     * if one does not exist , it will get created.
17697     * @param {string} the navgroup to add
17698     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17699     */
17700     get: function(navId) {
17701         if (typeof(this.groups[navId]) == 'undefined') {
17702             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17703         }
17704         return this.groups[navId] ;
17705     }
17706     
17707     
17708     
17709 });
17710
17711  /*
17712  * - LGPL
17713  *
17714  * TabPanel
17715  * 
17716  */
17717
17718 /**
17719  * @class Roo.bootstrap.TabPanel
17720  * @extends Roo.bootstrap.Component
17721  * Bootstrap TabPanel class
17722  * @cfg {Boolean} active panel active
17723  * @cfg {String} html panel content
17724  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17725  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17726  * @cfg {String} href click to link..
17727  * 
17728  * 
17729  * @constructor
17730  * Create a new TabPanel
17731  * @param {Object} config The config object
17732  */
17733
17734 Roo.bootstrap.TabPanel = function(config){
17735     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17736     this.addEvents({
17737         /**
17738              * @event changed
17739              * Fires when the active status changes
17740              * @param {Roo.bootstrap.TabPanel} this
17741              * @param {Boolean} state the new state
17742             
17743          */
17744         'changed': true,
17745         /**
17746              * @event beforedeactivate
17747              * Fires before a tab is de-activated - can be used to do validation on a form.
17748              * @param {Roo.bootstrap.TabPanel} this
17749              * @return {Boolean} false if there is an error
17750             
17751          */
17752         'beforedeactivate': true
17753      });
17754     
17755     this.tabId = this.tabId || Roo.id();
17756   
17757 };
17758
17759 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17760     
17761     active: false,
17762     html: false,
17763     tabId: false,
17764     navId : false,
17765     href : '',
17766     
17767     getAutoCreate : function(){
17768         var cfg = {
17769             tag: 'div',
17770             // item is needed for carousel - not sure if it has any effect otherwise
17771             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17772             html: this.html || ''
17773         };
17774         
17775         if(this.active){
17776             cfg.cls += ' active';
17777         }
17778         
17779         if(this.tabId){
17780             cfg.tabId = this.tabId;
17781         }
17782         
17783         
17784         return cfg;
17785     },
17786     
17787     initEvents:  function()
17788     {
17789         var p = this.parent();
17790         
17791         this.navId = this.navId || p.navId;
17792         
17793         if (typeof(this.navId) != 'undefined') {
17794             // not really needed.. but just in case.. parent should be a NavGroup.
17795             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17796             
17797             tg.register(this);
17798             
17799             var i = tg.tabs.length - 1;
17800             
17801             if(this.active && tg.bullets > 0 && i < tg.bullets){
17802                 tg.setActiveBullet(i);
17803             }
17804         }
17805         
17806         this.el.on('click', this.onClick, this);
17807         
17808         if(Roo.isTouch){
17809             this.el.on("touchstart", this.onTouchStart, this);
17810             this.el.on("touchmove", this.onTouchMove, this);
17811             this.el.on("touchend", this.onTouchEnd, this);
17812         }
17813         
17814     },
17815     
17816     onRender : function(ct, position)
17817     {
17818         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17819     },
17820     
17821     setActive : function(state)
17822     {
17823         Roo.log("panel - set active " + this.tabId + "=" + state);
17824         
17825         this.active = state;
17826         if (!state) {
17827             this.el.removeClass('active');
17828             
17829         } else  if (!this.el.hasClass('active')) {
17830             this.el.addClass('active');
17831         }
17832         
17833         this.fireEvent('changed', this, state);
17834     },
17835     
17836     onClick : function(e)
17837     {
17838         e.preventDefault();
17839         
17840         if(!this.href.length){
17841             return;
17842         }
17843         
17844         window.location.href = this.href;
17845     },
17846     
17847     startX : 0,
17848     startY : 0,
17849     endX : 0,
17850     endY : 0,
17851     swiping : false,
17852     
17853     onTouchStart : function(e)
17854     {
17855         this.swiping = false;
17856         
17857         this.startX = e.browserEvent.touches[0].clientX;
17858         this.startY = e.browserEvent.touches[0].clientY;
17859     },
17860     
17861     onTouchMove : function(e)
17862     {
17863         this.swiping = true;
17864         
17865         this.endX = e.browserEvent.touches[0].clientX;
17866         this.endY = e.browserEvent.touches[0].clientY;
17867     },
17868     
17869     onTouchEnd : function(e)
17870     {
17871         if(!this.swiping){
17872             this.onClick(e);
17873             return;
17874         }
17875         
17876         var tabGroup = this.parent();
17877         
17878         if(this.endX > this.startX){ // swiping right
17879             tabGroup.showPanelPrev();
17880             return;
17881         }
17882         
17883         if(this.startX > this.endX){ // swiping left
17884             tabGroup.showPanelNext();
17885             return;
17886         }
17887     }
17888     
17889     
17890 });
17891  
17892
17893  
17894
17895  /*
17896  * - LGPL
17897  *
17898  * DateField
17899  * 
17900  */
17901
17902 /**
17903  * @class Roo.bootstrap.DateField
17904  * @extends Roo.bootstrap.Input
17905  * Bootstrap DateField class
17906  * @cfg {Number} weekStart default 0
17907  * @cfg {String} viewMode default empty, (months|years)
17908  * @cfg {String} minViewMode default empty, (months|years)
17909  * @cfg {Number} startDate default -Infinity
17910  * @cfg {Number} endDate default Infinity
17911  * @cfg {Boolean} todayHighlight default false
17912  * @cfg {Boolean} todayBtn default false
17913  * @cfg {Boolean} calendarWeeks default false
17914  * @cfg {Object} daysOfWeekDisabled default empty
17915  * @cfg {Boolean} singleMode default false (true | false)
17916  * 
17917  * @cfg {Boolean} keyboardNavigation default true
17918  * @cfg {String} language default en
17919  * 
17920  * @constructor
17921  * Create a new DateField
17922  * @param {Object} config The config object
17923  */
17924
17925 Roo.bootstrap.DateField = function(config){
17926     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17927      this.addEvents({
17928             /**
17929              * @event show
17930              * Fires when this field show.
17931              * @param {Roo.bootstrap.DateField} this
17932              * @param {Mixed} date The date value
17933              */
17934             show : true,
17935             /**
17936              * @event show
17937              * Fires when this field hide.
17938              * @param {Roo.bootstrap.DateField} this
17939              * @param {Mixed} date The date value
17940              */
17941             hide : true,
17942             /**
17943              * @event select
17944              * Fires when select a date.
17945              * @param {Roo.bootstrap.DateField} this
17946              * @param {Mixed} date The date value
17947              */
17948             select : true,
17949             /**
17950              * @event beforeselect
17951              * Fires when before select a date.
17952              * @param {Roo.bootstrap.DateField} this
17953              * @param {Mixed} date The date value
17954              */
17955             beforeselect : true
17956         });
17957 };
17958
17959 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17960     
17961     /**
17962      * @cfg {String} format
17963      * The default date format string which can be overriden for localization support.  The format must be
17964      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17965      */
17966     format : "m/d/y",
17967     /**
17968      * @cfg {String} altFormats
17969      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17970      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17971      */
17972     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17973     
17974     weekStart : 0,
17975     
17976     viewMode : '',
17977     
17978     minViewMode : '',
17979     
17980     todayHighlight : false,
17981     
17982     todayBtn: false,
17983     
17984     language: 'en',
17985     
17986     keyboardNavigation: true,
17987     
17988     calendarWeeks: false,
17989     
17990     startDate: -Infinity,
17991     
17992     endDate: Infinity,
17993     
17994     daysOfWeekDisabled: [],
17995     
17996     _events: [],
17997     
17998     singleMode : false,
17999     
18000     UTCDate: function()
18001     {
18002         return new Date(Date.UTC.apply(Date, arguments));
18003     },
18004     
18005     UTCToday: function()
18006     {
18007         var today = new Date();
18008         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18009     },
18010     
18011     getDate: function() {
18012             var d = this.getUTCDate();
18013             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18014     },
18015     
18016     getUTCDate: function() {
18017             return this.date;
18018     },
18019     
18020     setDate: function(d) {
18021             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18022     },
18023     
18024     setUTCDate: function(d) {
18025             this.date = d;
18026             this.setValue(this.formatDate(this.date));
18027     },
18028         
18029     onRender: function(ct, position)
18030     {
18031         
18032         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18033         
18034         this.language = this.language || 'en';
18035         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18036         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18037         
18038         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18039         this.format = this.format || 'm/d/y';
18040         this.isInline = false;
18041         this.isInput = true;
18042         this.component = this.el.select('.add-on', true).first() || false;
18043         this.component = (this.component && this.component.length === 0) ? false : this.component;
18044         this.hasInput = this.component && this.inputEl().length;
18045         
18046         if (typeof(this.minViewMode === 'string')) {
18047             switch (this.minViewMode) {
18048                 case 'months':
18049                     this.minViewMode = 1;
18050                     break;
18051                 case 'years':
18052                     this.minViewMode = 2;
18053                     break;
18054                 default:
18055                     this.minViewMode = 0;
18056                     break;
18057             }
18058         }
18059         
18060         if (typeof(this.viewMode === 'string')) {
18061             switch (this.viewMode) {
18062                 case 'months':
18063                     this.viewMode = 1;
18064                     break;
18065                 case 'years':
18066                     this.viewMode = 2;
18067                     break;
18068                 default:
18069                     this.viewMode = 0;
18070                     break;
18071             }
18072         }
18073                 
18074         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18075         
18076 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18077         
18078         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18079         
18080         this.picker().on('mousedown', this.onMousedown, this);
18081         this.picker().on('click', this.onClick, this);
18082         
18083         this.picker().addClass('datepicker-dropdown');
18084         
18085         this.startViewMode = this.viewMode;
18086         
18087         if(this.singleMode){
18088             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18089                 v.setVisibilityMode(Roo.Element.DISPLAY);
18090                 v.hide();
18091             });
18092             
18093             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18094                 v.setStyle('width', '189px');
18095             });
18096         }
18097         
18098         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18099             if(!this.calendarWeeks){
18100                 v.remove();
18101                 return;
18102             }
18103             
18104             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18105             v.attr('colspan', function(i, val){
18106                 return parseInt(val) + 1;
18107             });
18108         });
18109                         
18110         
18111         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18112         
18113         this.setStartDate(this.startDate);
18114         this.setEndDate(this.endDate);
18115         
18116         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18117         
18118         this.fillDow();
18119         this.fillMonths();
18120         this.update();
18121         this.showMode();
18122         
18123         if(this.isInline) {
18124             this.show();
18125         }
18126     },
18127     
18128     picker : function()
18129     {
18130         return this.pickerEl;
18131 //        return this.el.select('.datepicker', true).first();
18132     },
18133     
18134     fillDow: function()
18135     {
18136         var dowCnt = this.weekStart;
18137         
18138         var dow = {
18139             tag: 'tr',
18140             cn: [
18141                 
18142             ]
18143         };
18144         
18145         if(this.calendarWeeks){
18146             dow.cn.push({
18147                 tag: 'th',
18148                 cls: 'cw',
18149                 html: '&nbsp;'
18150             })
18151         }
18152         
18153         while (dowCnt < this.weekStart + 7) {
18154             dow.cn.push({
18155                 tag: 'th',
18156                 cls: 'dow',
18157                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18158             });
18159         }
18160         
18161         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18162     },
18163     
18164     fillMonths: function()
18165     {    
18166         var i = 0;
18167         var months = this.picker().select('>.datepicker-months td', true).first();
18168         
18169         months.dom.innerHTML = '';
18170         
18171         while (i < 12) {
18172             var month = {
18173                 tag: 'span',
18174                 cls: 'month',
18175                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18176             };
18177             
18178             months.createChild(month);
18179         }
18180         
18181     },
18182     
18183     update: function()
18184     {
18185         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;
18186         
18187         if (this.date < this.startDate) {
18188             this.viewDate = new Date(this.startDate);
18189         } else if (this.date > this.endDate) {
18190             this.viewDate = new Date(this.endDate);
18191         } else {
18192             this.viewDate = new Date(this.date);
18193         }
18194         
18195         this.fill();
18196     },
18197     
18198     fill: function() 
18199     {
18200         var d = new Date(this.viewDate),
18201                 year = d.getUTCFullYear(),
18202                 month = d.getUTCMonth(),
18203                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18204                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18205                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18206                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18207                 currentDate = this.date && this.date.valueOf(),
18208                 today = this.UTCToday();
18209         
18210         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18211         
18212 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18213         
18214 //        this.picker.select('>tfoot th.today').
18215 //                                              .text(dates[this.language].today)
18216 //                                              .toggle(this.todayBtn !== false);
18217     
18218         this.updateNavArrows();
18219         this.fillMonths();
18220                                                 
18221         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18222         
18223         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18224          
18225         prevMonth.setUTCDate(day);
18226         
18227         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18228         
18229         var nextMonth = new Date(prevMonth);
18230         
18231         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18232         
18233         nextMonth = nextMonth.valueOf();
18234         
18235         var fillMonths = false;
18236         
18237         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18238         
18239         while(prevMonth.valueOf() < nextMonth) {
18240             var clsName = '';
18241             
18242             if (prevMonth.getUTCDay() === this.weekStart) {
18243                 if(fillMonths){
18244                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18245                 }
18246                     
18247                 fillMonths = {
18248                     tag: 'tr',
18249                     cn: []
18250                 };
18251                 
18252                 if(this.calendarWeeks){
18253                     // ISO 8601: First week contains first thursday.
18254                     // ISO also states week starts on Monday, but we can be more abstract here.
18255                     var
18256                     // Start of current week: based on weekstart/current date
18257                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18258                     // Thursday of this week
18259                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18260                     // First Thursday of year, year from thursday
18261                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18262                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18263                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18264                     
18265                     fillMonths.cn.push({
18266                         tag: 'td',
18267                         cls: 'cw',
18268                         html: calWeek
18269                     });
18270                 }
18271             }
18272             
18273             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18274                 clsName += ' old';
18275             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18276                 clsName += ' new';
18277             }
18278             if (this.todayHighlight &&
18279                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18280                 prevMonth.getUTCMonth() == today.getMonth() &&
18281                 prevMonth.getUTCDate() == today.getDate()) {
18282                 clsName += ' today';
18283             }
18284             
18285             if (currentDate && prevMonth.valueOf() === currentDate) {
18286                 clsName += ' active';
18287             }
18288             
18289             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18290                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18291                     clsName += ' disabled';
18292             }
18293             
18294             fillMonths.cn.push({
18295                 tag: 'td',
18296                 cls: 'day ' + clsName,
18297                 html: prevMonth.getDate()
18298             });
18299             
18300             prevMonth.setDate(prevMonth.getDate()+1);
18301         }
18302           
18303         var currentYear = this.date && this.date.getUTCFullYear();
18304         var currentMonth = this.date && this.date.getUTCMonth();
18305         
18306         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18307         
18308         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18309             v.removeClass('active');
18310             
18311             if(currentYear === year && k === currentMonth){
18312                 v.addClass('active');
18313             }
18314             
18315             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18316                 v.addClass('disabled');
18317             }
18318             
18319         });
18320         
18321         
18322         year = parseInt(year/10, 10) * 10;
18323         
18324         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18325         
18326         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18327         
18328         year -= 1;
18329         for (var i = -1; i < 11; i++) {
18330             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18331                 tag: 'span',
18332                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18333                 html: year
18334             });
18335             
18336             year += 1;
18337         }
18338     },
18339     
18340     showMode: function(dir) 
18341     {
18342         if (dir) {
18343             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18344         }
18345         
18346         Roo.each(this.picker().select('>div',true).elements, function(v){
18347             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18348             v.hide();
18349         });
18350         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18351     },
18352     
18353     place: function()
18354     {
18355         if(this.isInline) {
18356             return;
18357         }
18358         
18359         this.picker().removeClass(['bottom', 'top']);
18360         
18361         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18362             /*
18363              * place to the top of element!
18364              *
18365              */
18366             
18367             this.picker().addClass('top');
18368             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18369             
18370             return;
18371         }
18372         
18373         this.picker().addClass('bottom');
18374         
18375         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18376     },
18377     
18378     parseDate : function(value)
18379     {
18380         if(!value || value instanceof Date){
18381             return value;
18382         }
18383         var v = Date.parseDate(value, this.format);
18384         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18385             v = Date.parseDate(value, 'Y-m-d');
18386         }
18387         if(!v && this.altFormats){
18388             if(!this.altFormatsArray){
18389                 this.altFormatsArray = this.altFormats.split("|");
18390             }
18391             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18392                 v = Date.parseDate(value, this.altFormatsArray[i]);
18393             }
18394         }
18395         return v;
18396     },
18397     
18398     formatDate : function(date, fmt)
18399     {   
18400         return (!date || !(date instanceof Date)) ?
18401         date : date.dateFormat(fmt || this.format);
18402     },
18403     
18404     onFocus : function()
18405     {
18406         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18407         this.show();
18408     },
18409     
18410     onBlur : function()
18411     {
18412         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18413         
18414         var d = this.inputEl().getValue();
18415         
18416         this.setValue(d);
18417                 
18418         this.hide();
18419     },
18420     
18421     show : function()
18422     {
18423         this.picker().show();
18424         this.update();
18425         this.place();
18426         
18427         this.fireEvent('show', this, this.date);
18428     },
18429     
18430     hide : function()
18431     {
18432         if(this.isInline) {
18433             return;
18434         }
18435         this.picker().hide();
18436         this.viewMode = this.startViewMode;
18437         this.showMode();
18438         
18439         this.fireEvent('hide', this, this.date);
18440         
18441     },
18442     
18443     onMousedown: function(e)
18444     {
18445         e.stopPropagation();
18446         e.preventDefault();
18447     },
18448     
18449     keyup: function(e)
18450     {
18451         Roo.bootstrap.DateField.superclass.keyup.call(this);
18452         this.update();
18453     },
18454
18455     setValue: function(v)
18456     {
18457         if(this.fireEvent('beforeselect', this, v) !== false){
18458             var d = new Date(this.parseDate(v) ).clearTime();
18459         
18460             if(isNaN(d.getTime())){
18461                 this.date = this.viewDate = '';
18462                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18463                 return;
18464             }
18465
18466             v = this.formatDate(d);
18467
18468             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18469
18470             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18471
18472             this.update();
18473
18474             this.fireEvent('select', this, this.date);
18475         }
18476     },
18477     
18478     getValue: function()
18479     {
18480         return this.formatDate(this.date);
18481     },
18482     
18483     fireKey: function(e)
18484     {
18485         if (!this.picker().isVisible()){
18486             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18487                 this.show();
18488             }
18489             return;
18490         }
18491         
18492         var dateChanged = false,
18493         dir, day, month,
18494         newDate, newViewDate;
18495         
18496         switch(e.keyCode){
18497             case 27: // escape
18498                 this.hide();
18499                 e.preventDefault();
18500                 break;
18501             case 37: // left
18502             case 39: // right
18503                 if (!this.keyboardNavigation) {
18504                     break;
18505                 }
18506                 dir = e.keyCode == 37 ? -1 : 1;
18507                 
18508                 if (e.ctrlKey){
18509                     newDate = this.moveYear(this.date, dir);
18510                     newViewDate = this.moveYear(this.viewDate, dir);
18511                 } else if (e.shiftKey){
18512                     newDate = this.moveMonth(this.date, dir);
18513                     newViewDate = this.moveMonth(this.viewDate, dir);
18514                 } else {
18515                     newDate = new Date(this.date);
18516                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18517                     newViewDate = new Date(this.viewDate);
18518                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18519                 }
18520                 if (this.dateWithinRange(newDate)){
18521                     this.date = newDate;
18522                     this.viewDate = newViewDate;
18523                     this.setValue(this.formatDate(this.date));
18524 //                    this.update();
18525                     e.preventDefault();
18526                     dateChanged = true;
18527                 }
18528                 break;
18529             case 38: // up
18530             case 40: // down
18531                 if (!this.keyboardNavigation) {
18532                     break;
18533                 }
18534                 dir = e.keyCode == 38 ? -1 : 1;
18535                 if (e.ctrlKey){
18536                     newDate = this.moveYear(this.date, dir);
18537                     newViewDate = this.moveYear(this.viewDate, dir);
18538                 } else if (e.shiftKey){
18539                     newDate = this.moveMonth(this.date, dir);
18540                     newViewDate = this.moveMonth(this.viewDate, dir);
18541                 } else {
18542                     newDate = new Date(this.date);
18543                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18544                     newViewDate = new Date(this.viewDate);
18545                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18546                 }
18547                 if (this.dateWithinRange(newDate)){
18548                     this.date = newDate;
18549                     this.viewDate = newViewDate;
18550                     this.setValue(this.formatDate(this.date));
18551 //                    this.update();
18552                     e.preventDefault();
18553                     dateChanged = true;
18554                 }
18555                 break;
18556             case 13: // enter
18557                 this.setValue(this.formatDate(this.date));
18558                 this.hide();
18559                 e.preventDefault();
18560                 break;
18561             case 9: // tab
18562                 this.setValue(this.formatDate(this.date));
18563                 this.hide();
18564                 break;
18565             case 16: // shift
18566             case 17: // ctrl
18567             case 18: // alt
18568                 break;
18569             default :
18570                 this.hide();
18571                 
18572         }
18573     },
18574     
18575     
18576     onClick: function(e) 
18577     {
18578         e.stopPropagation();
18579         e.preventDefault();
18580         
18581         var target = e.getTarget();
18582         
18583         if(target.nodeName.toLowerCase() === 'i'){
18584             target = Roo.get(target).dom.parentNode;
18585         }
18586         
18587         var nodeName = target.nodeName;
18588         var className = target.className;
18589         var html = target.innerHTML;
18590         //Roo.log(nodeName);
18591         
18592         switch(nodeName.toLowerCase()) {
18593             case 'th':
18594                 switch(className) {
18595                     case 'switch':
18596                         this.showMode(1);
18597                         break;
18598                     case 'prev':
18599                     case 'next':
18600                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18601                         switch(this.viewMode){
18602                                 case 0:
18603                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18604                                         break;
18605                                 case 1:
18606                                 case 2:
18607                                         this.viewDate = this.moveYear(this.viewDate, dir);
18608                                         break;
18609                         }
18610                         this.fill();
18611                         break;
18612                     case 'today':
18613                         var date = new Date();
18614                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18615 //                        this.fill()
18616                         this.setValue(this.formatDate(this.date));
18617                         
18618                         this.hide();
18619                         break;
18620                 }
18621                 break;
18622             case 'span':
18623                 if (className.indexOf('disabled') < 0) {
18624                     this.viewDate.setUTCDate(1);
18625                     if (className.indexOf('month') > -1) {
18626                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18627                     } else {
18628                         var year = parseInt(html, 10) || 0;
18629                         this.viewDate.setUTCFullYear(year);
18630                         
18631                     }
18632                     
18633                     if(this.singleMode){
18634                         this.setValue(this.formatDate(this.viewDate));
18635                         this.hide();
18636                         return;
18637                     }
18638                     
18639                     this.showMode(-1);
18640                     this.fill();
18641                 }
18642                 break;
18643                 
18644             case 'td':
18645                 //Roo.log(className);
18646                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18647                     var day = parseInt(html, 10) || 1;
18648                     var year = this.viewDate.getUTCFullYear(),
18649                         month = this.viewDate.getUTCMonth();
18650
18651                     if (className.indexOf('old') > -1) {
18652                         if(month === 0 ){
18653                             month = 11;
18654                             year -= 1;
18655                         }else{
18656                             month -= 1;
18657                         }
18658                     } else if (className.indexOf('new') > -1) {
18659                         if (month == 11) {
18660                             month = 0;
18661                             year += 1;
18662                         } else {
18663                             month += 1;
18664                         }
18665                     }
18666                     //Roo.log([year,month,day]);
18667                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18668                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18669 //                    this.fill();
18670                     //Roo.log(this.formatDate(this.date));
18671                     this.setValue(this.formatDate(this.date));
18672                     this.hide();
18673                 }
18674                 break;
18675         }
18676     },
18677     
18678     setStartDate: function(startDate)
18679     {
18680         this.startDate = startDate || -Infinity;
18681         if (this.startDate !== -Infinity) {
18682             this.startDate = this.parseDate(this.startDate);
18683         }
18684         this.update();
18685         this.updateNavArrows();
18686     },
18687
18688     setEndDate: function(endDate)
18689     {
18690         this.endDate = endDate || Infinity;
18691         if (this.endDate !== Infinity) {
18692             this.endDate = this.parseDate(this.endDate);
18693         }
18694         this.update();
18695         this.updateNavArrows();
18696     },
18697     
18698     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18699     {
18700         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18701         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18702             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18703         }
18704         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18705             return parseInt(d, 10);
18706         });
18707         this.update();
18708         this.updateNavArrows();
18709     },
18710     
18711     updateNavArrows: function() 
18712     {
18713         if(this.singleMode){
18714             return;
18715         }
18716         
18717         var d = new Date(this.viewDate),
18718         year = d.getUTCFullYear(),
18719         month = d.getUTCMonth();
18720         
18721         Roo.each(this.picker().select('.prev', true).elements, function(v){
18722             v.show();
18723             switch (this.viewMode) {
18724                 case 0:
18725
18726                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18727                         v.hide();
18728                     }
18729                     break;
18730                 case 1:
18731                 case 2:
18732                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18733                         v.hide();
18734                     }
18735                     break;
18736             }
18737         });
18738         
18739         Roo.each(this.picker().select('.next', true).elements, function(v){
18740             v.show();
18741             switch (this.viewMode) {
18742                 case 0:
18743
18744                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18745                         v.hide();
18746                     }
18747                     break;
18748                 case 1:
18749                 case 2:
18750                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18751                         v.hide();
18752                     }
18753                     break;
18754             }
18755         })
18756     },
18757     
18758     moveMonth: function(date, dir)
18759     {
18760         if (!dir) {
18761             return date;
18762         }
18763         var new_date = new Date(date.valueOf()),
18764         day = new_date.getUTCDate(),
18765         month = new_date.getUTCMonth(),
18766         mag = Math.abs(dir),
18767         new_month, test;
18768         dir = dir > 0 ? 1 : -1;
18769         if (mag == 1){
18770             test = dir == -1
18771             // If going back one month, make sure month is not current month
18772             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18773             ? function(){
18774                 return new_date.getUTCMonth() == month;
18775             }
18776             // If going forward one month, make sure month is as expected
18777             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18778             : function(){
18779                 return new_date.getUTCMonth() != new_month;
18780             };
18781             new_month = month + dir;
18782             new_date.setUTCMonth(new_month);
18783             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18784             if (new_month < 0 || new_month > 11) {
18785                 new_month = (new_month + 12) % 12;
18786             }
18787         } else {
18788             // For magnitudes >1, move one month at a time...
18789             for (var i=0; i<mag; i++) {
18790                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18791                 new_date = this.moveMonth(new_date, dir);
18792             }
18793             // ...then reset the day, keeping it in the new month
18794             new_month = new_date.getUTCMonth();
18795             new_date.setUTCDate(day);
18796             test = function(){
18797                 return new_month != new_date.getUTCMonth();
18798             };
18799         }
18800         // Common date-resetting loop -- if date is beyond end of month, make it
18801         // end of month
18802         while (test()){
18803             new_date.setUTCDate(--day);
18804             new_date.setUTCMonth(new_month);
18805         }
18806         return new_date;
18807     },
18808
18809     moveYear: function(date, dir)
18810     {
18811         return this.moveMonth(date, dir*12);
18812     },
18813
18814     dateWithinRange: function(date)
18815     {
18816         return date >= this.startDate && date <= this.endDate;
18817     },
18818
18819     
18820     remove: function() 
18821     {
18822         this.picker().remove();
18823     },
18824     
18825     validateValue : function(value)
18826     {
18827         if(value.length < 1)  {
18828             if(this.allowBlank){
18829                 return true;
18830             }
18831             return false;
18832         }
18833         
18834         if(value.length < this.minLength){
18835             return false;
18836         }
18837         if(value.length > this.maxLength){
18838             return false;
18839         }
18840         if(this.vtype){
18841             var vt = Roo.form.VTypes;
18842             if(!vt[this.vtype](value, this)){
18843                 return false;
18844             }
18845         }
18846         if(typeof this.validator == "function"){
18847             var msg = this.validator(value);
18848             if(msg !== true){
18849                 return false;
18850             }
18851         }
18852         
18853         if(this.regex && !this.regex.test(value)){
18854             return false;
18855         }
18856         
18857         if(typeof(this.parseDate(value)) == 'undefined'){
18858             return false;
18859         }
18860         
18861         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18862             return false;
18863         }      
18864         
18865         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18866             return false;
18867         } 
18868         
18869         
18870         return true;
18871     }
18872    
18873 });
18874
18875 Roo.apply(Roo.bootstrap.DateField,  {
18876     
18877     head : {
18878         tag: 'thead',
18879         cn: [
18880         {
18881             tag: 'tr',
18882             cn: [
18883             {
18884                 tag: 'th',
18885                 cls: 'prev',
18886                 html: '<i class="fa fa-arrow-left"/>'
18887             },
18888             {
18889                 tag: 'th',
18890                 cls: 'switch',
18891                 colspan: '5'
18892             },
18893             {
18894                 tag: 'th',
18895                 cls: 'next',
18896                 html: '<i class="fa fa-arrow-right"/>'
18897             }
18898
18899             ]
18900         }
18901         ]
18902     },
18903     
18904     content : {
18905         tag: 'tbody',
18906         cn: [
18907         {
18908             tag: 'tr',
18909             cn: [
18910             {
18911                 tag: 'td',
18912                 colspan: '7'
18913             }
18914             ]
18915         }
18916         ]
18917     },
18918     
18919     footer : {
18920         tag: 'tfoot',
18921         cn: [
18922         {
18923             tag: 'tr',
18924             cn: [
18925             {
18926                 tag: 'th',
18927                 colspan: '7',
18928                 cls: 'today'
18929             }
18930                     
18931             ]
18932         }
18933         ]
18934     },
18935     
18936     dates:{
18937         en: {
18938             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18939             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18940             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18941             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18942             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18943             today: "Today"
18944         }
18945     },
18946     
18947     modes: [
18948     {
18949         clsName: 'days',
18950         navFnc: 'Month',
18951         navStep: 1
18952     },
18953     {
18954         clsName: 'months',
18955         navFnc: 'FullYear',
18956         navStep: 1
18957     },
18958     {
18959         clsName: 'years',
18960         navFnc: 'FullYear',
18961         navStep: 10
18962     }]
18963 });
18964
18965 Roo.apply(Roo.bootstrap.DateField,  {
18966   
18967     template : {
18968         tag: 'div',
18969         cls: 'datepicker dropdown-menu roo-dynamic',
18970         cn: [
18971         {
18972             tag: 'div',
18973             cls: 'datepicker-days',
18974             cn: [
18975             {
18976                 tag: 'table',
18977                 cls: 'table-condensed',
18978                 cn:[
18979                 Roo.bootstrap.DateField.head,
18980                 {
18981                     tag: 'tbody'
18982                 },
18983                 Roo.bootstrap.DateField.footer
18984                 ]
18985             }
18986             ]
18987         },
18988         {
18989             tag: 'div',
18990             cls: 'datepicker-months',
18991             cn: [
18992             {
18993                 tag: 'table',
18994                 cls: 'table-condensed',
18995                 cn:[
18996                 Roo.bootstrap.DateField.head,
18997                 Roo.bootstrap.DateField.content,
18998                 Roo.bootstrap.DateField.footer
18999                 ]
19000             }
19001             ]
19002         },
19003         {
19004             tag: 'div',
19005             cls: 'datepicker-years',
19006             cn: [
19007             {
19008                 tag: 'table',
19009                 cls: 'table-condensed',
19010                 cn:[
19011                 Roo.bootstrap.DateField.head,
19012                 Roo.bootstrap.DateField.content,
19013                 Roo.bootstrap.DateField.footer
19014                 ]
19015             }
19016             ]
19017         }
19018         ]
19019     }
19020 });
19021
19022  
19023
19024  /*
19025  * - LGPL
19026  *
19027  * TimeField
19028  * 
19029  */
19030
19031 /**
19032  * @class Roo.bootstrap.TimeField
19033  * @extends Roo.bootstrap.Input
19034  * Bootstrap DateField class
19035  * 
19036  * 
19037  * @constructor
19038  * Create a new TimeField
19039  * @param {Object} config The config object
19040  */
19041
19042 Roo.bootstrap.TimeField = function(config){
19043     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19044     this.addEvents({
19045             /**
19046              * @event show
19047              * Fires when this field show.
19048              * @param {Roo.bootstrap.DateField} thisthis
19049              * @param {Mixed} date The date value
19050              */
19051             show : true,
19052             /**
19053              * @event show
19054              * Fires when this field hide.
19055              * @param {Roo.bootstrap.DateField} this
19056              * @param {Mixed} date The date value
19057              */
19058             hide : true,
19059             /**
19060              * @event select
19061              * Fires when select a date.
19062              * @param {Roo.bootstrap.DateField} this
19063              * @param {Mixed} date The date value
19064              */
19065             select : true
19066         });
19067 };
19068
19069 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19070     
19071     /**
19072      * @cfg {String} format
19073      * The default time format string which can be overriden for localization support.  The format must be
19074      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19075      */
19076     format : "H:i",
19077        
19078     onRender: function(ct, position)
19079     {
19080         
19081         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19082                 
19083         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19084         
19085         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19086         
19087         this.pop = this.picker().select('>.datepicker-time',true).first();
19088         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19089         
19090         this.picker().on('mousedown', this.onMousedown, this);
19091         this.picker().on('click', this.onClick, this);
19092         
19093         this.picker().addClass('datepicker-dropdown');
19094     
19095         this.fillTime();
19096         this.update();
19097             
19098         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19099         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19100         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19101         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19102         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19103         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19104
19105     },
19106     
19107     fireKey: function(e){
19108         if (!this.picker().isVisible()){
19109             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19110                 this.show();
19111             }
19112             return;
19113         }
19114
19115         e.preventDefault();
19116         
19117         switch(e.keyCode){
19118             case 27: // escape
19119                 this.hide();
19120                 break;
19121             case 37: // left
19122             case 39: // right
19123                 this.onTogglePeriod();
19124                 break;
19125             case 38: // up
19126                 this.onIncrementMinutes();
19127                 break;
19128             case 40: // down
19129                 this.onDecrementMinutes();
19130                 break;
19131             case 13: // enter
19132             case 9: // tab
19133                 this.setTime();
19134                 break;
19135         }
19136     },
19137     
19138     onClick: function(e) {
19139         e.stopPropagation();
19140         e.preventDefault();
19141     },
19142     
19143     picker : function()
19144     {
19145         return this.el.select('.datepicker', true).first();
19146     },
19147     
19148     fillTime: function()
19149     {    
19150         var time = this.pop.select('tbody', true).first();
19151         
19152         time.dom.innerHTML = '';
19153         
19154         time.createChild({
19155             tag: 'tr',
19156             cn: [
19157                 {
19158                     tag: 'td',
19159                     cn: [
19160                         {
19161                             tag: 'a',
19162                             href: '#',
19163                             cls: 'btn',
19164                             cn: [
19165                                 {
19166                                     tag: 'span',
19167                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19168                                 }
19169                             ]
19170                         } 
19171                     ]
19172                 },
19173                 {
19174                     tag: 'td',
19175                     cls: 'separator'
19176                 },
19177                 {
19178                     tag: 'td',
19179                     cn: [
19180                         {
19181                             tag: 'a',
19182                             href: '#',
19183                             cls: 'btn',
19184                             cn: [
19185                                 {
19186                                     tag: 'span',
19187                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19188                                 }
19189                             ]
19190                         }
19191                     ]
19192                 },
19193                 {
19194                     tag: 'td',
19195                     cls: 'separator'
19196                 }
19197             ]
19198         });
19199         
19200         time.createChild({
19201             tag: 'tr',
19202             cn: [
19203                 {
19204                     tag: 'td',
19205                     cn: [
19206                         {
19207                             tag: 'span',
19208                             cls: 'timepicker-hour',
19209                             html: '00'
19210                         }  
19211                     ]
19212                 },
19213                 {
19214                     tag: 'td',
19215                     cls: 'separator',
19216                     html: ':'
19217                 },
19218                 {
19219                     tag: 'td',
19220                     cn: [
19221                         {
19222                             tag: 'span',
19223                             cls: 'timepicker-minute',
19224                             html: '00'
19225                         }  
19226                     ]
19227                 },
19228                 {
19229                     tag: 'td',
19230                     cls: 'separator'
19231                 },
19232                 {
19233                     tag: 'td',
19234                     cn: [
19235                         {
19236                             tag: 'button',
19237                             type: 'button',
19238                             cls: 'btn btn-primary period',
19239                             html: 'AM'
19240                             
19241                         }
19242                     ]
19243                 }
19244             ]
19245         });
19246         
19247         time.createChild({
19248             tag: 'tr',
19249             cn: [
19250                 {
19251                     tag: 'td',
19252                     cn: [
19253                         {
19254                             tag: 'a',
19255                             href: '#',
19256                             cls: 'btn',
19257                             cn: [
19258                                 {
19259                                     tag: 'span',
19260                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19261                                 }
19262                             ]
19263                         }
19264                     ]
19265                 },
19266                 {
19267                     tag: 'td',
19268                     cls: 'separator'
19269                 },
19270                 {
19271                     tag: 'td',
19272                     cn: [
19273                         {
19274                             tag: 'a',
19275                             href: '#',
19276                             cls: 'btn',
19277                             cn: [
19278                                 {
19279                                     tag: 'span',
19280                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19281                                 }
19282                             ]
19283                         }
19284                     ]
19285                 },
19286                 {
19287                     tag: 'td',
19288                     cls: 'separator'
19289                 }
19290             ]
19291         });
19292         
19293     },
19294     
19295     update: function()
19296     {
19297         
19298         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19299         
19300         this.fill();
19301     },
19302     
19303     fill: function() 
19304     {
19305         var hours = this.time.getHours();
19306         var minutes = this.time.getMinutes();
19307         var period = 'AM';
19308         
19309         if(hours > 11){
19310             period = 'PM';
19311         }
19312         
19313         if(hours == 0){
19314             hours = 12;
19315         }
19316         
19317         
19318         if(hours > 12){
19319             hours = hours - 12;
19320         }
19321         
19322         if(hours < 10){
19323             hours = '0' + hours;
19324         }
19325         
19326         if(minutes < 10){
19327             minutes = '0' + minutes;
19328         }
19329         
19330         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19331         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19332         this.pop.select('button', true).first().dom.innerHTML = period;
19333         
19334     },
19335     
19336     place: function()
19337     {   
19338         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19339         
19340         var cls = ['bottom'];
19341         
19342         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19343             cls.pop();
19344             cls.push('top');
19345         }
19346         
19347         cls.push('right');
19348         
19349         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19350             cls.pop();
19351             cls.push('left');
19352         }
19353         
19354         this.picker().addClass(cls.join('-'));
19355         
19356         var _this = this;
19357         
19358         Roo.each(cls, function(c){
19359             if(c == 'bottom'){
19360                 _this.picker().setTop(_this.inputEl().getHeight());
19361                 return;
19362             }
19363             if(c == 'top'){
19364                 _this.picker().setTop(0 - _this.picker().getHeight());
19365                 return;
19366             }
19367             
19368             if(c == 'left'){
19369                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19370                 return;
19371             }
19372             if(c == 'right'){
19373                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19374                 return;
19375             }
19376         });
19377         
19378     },
19379   
19380     onFocus : function()
19381     {
19382         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19383         this.show();
19384     },
19385     
19386     onBlur : function()
19387     {
19388         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19389         this.hide();
19390     },
19391     
19392     show : function()
19393     {
19394         this.picker().show();
19395         this.pop.show();
19396         this.update();
19397         this.place();
19398         
19399         this.fireEvent('show', this, this.date);
19400     },
19401     
19402     hide : function()
19403     {
19404         this.picker().hide();
19405         this.pop.hide();
19406         
19407         this.fireEvent('hide', this, this.date);
19408     },
19409     
19410     setTime : function()
19411     {
19412         this.hide();
19413         this.setValue(this.time.format(this.format));
19414         
19415         this.fireEvent('select', this, this.date);
19416         
19417         
19418     },
19419     
19420     onMousedown: function(e){
19421         e.stopPropagation();
19422         e.preventDefault();
19423     },
19424     
19425     onIncrementHours: function()
19426     {
19427         Roo.log('onIncrementHours');
19428         this.time = this.time.add(Date.HOUR, 1);
19429         this.update();
19430         
19431     },
19432     
19433     onDecrementHours: function()
19434     {
19435         Roo.log('onDecrementHours');
19436         this.time = this.time.add(Date.HOUR, -1);
19437         this.update();
19438     },
19439     
19440     onIncrementMinutes: function()
19441     {
19442         Roo.log('onIncrementMinutes');
19443         this.time = this.time.add(Date.MINUTE, 1);
19444         this.update();
19445     },
19446     
19447     onDecrementMinutes: function()
19448     {
19449         Roo.log('onDecrementMinutes');
19450         this.time = this.time.add(Date.MINUTE, -1);
19451         this.update();
19452     },
19453     
19454     onTogglePeriod: function()
19455     {
19456         Roo.log('onTogglePeriod');
19457         this.time = this.time.add(Date.HOUR, 12);
19458         this.update();
19459     }
19460     
19461    
19462 });
19463
19464 Roo.apply(Roo.bootstrap.TimeField,  {
19465     
19466     content : {
19467         tag: 'tbody',
19468         cn: [
19469             {
19470                 tag: 'tr',
19471                 cn: [
19472                 {
19473                     tag: 'td',
19474                     colspan: '7'
19475                 }
19476                 ]
19477             }
19478         ]
19479     },
19480     
19481     footer : {
19482         tag: 'tfoot',
19483         cn: [
19484             {
19485                 tag: 'tr',
19486                 cn: [
19487                 {
19488                     tag: 'th',
19489                     colspan: '7',
19490                     cls: '',
19491                     cn: [
19492                         {
19493                             tag: 'button',
19494                             cls: 'btn btn-info ok',
19495                             html: 'OK'
19496                         }
19497                     ]
19498                 }
19499
19500                 ]
19501             }
19502         ]
19503     }
19504 });
19505
19506 Roo.apply(Roo.bootstrap.TimeField,  {
19507   
19508     template : {
19509         tag: 'div',
19510         cls: 'datepicker dropdown-menu',
19511         cn: [
19512             {
19513                 tag: 'div',
19514                 cls: 'datepicker-time',
19515                 cn: [
19516                 {
19517                     tag: 'table',
19518                     cls: 'table-condensed',
19519                     cn:[
19520                     Roo.bootstrap.TimeField.content,
19521                     Roo.bootstrap.TimeField.footer
19522                     ]
19523                 }
19524                 ]
19525             }
19526         ]
19527     }
19528 });
19529
19530  
19531
19532  /*
19533  * - LGPL
19534  *
19535  * MonthField
19536  * 
19537  */
19538
19539 /**
19540  * @class Roo.bootstrap.MonthField
19541  * @extends Roo.bootstrap.Input
19542  * Bootstrap MonthField class
19543  * 
19544  * @cfg {String} language default en
19545  * 
19546  * @constructor
19547  * Create a new MonthField
19548  * @param {Object} config The config object
19549  */
19550
19551 Roo.bootstrap.MonthField = function(config){
19552     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19553     
19554     this.addEvents({
19555         /**
19556          * @event show
19557          * Fires when this field show.
19558          * @param {Roo.bootstrap.MonthField} this
19559          * @param {Mixed} date The date value
19560          */
19561         show : true,
19562         /**
19563          * @event show
19564          * Fires when this field hide.
19565          * @param {Roo.bootstrap.MonthField} this
19566          * @param {Mixed} date The date value
19567          */
19568         hide : true,
19569         /**
19570          * @event select
19571          * Fires when select a date.
19572          * @param {Roo.bootstrap.MonthField} this
19573          * @param {String} oldvalue The old value
19574          * @param {String} newvalue The new value
19575          */
19576         select : true
19577     });
19578 };
19579
19580 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19581     
19582     onRender: function(ct, position)
19583     {
19584         
19585         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19586         
19587         this.language = this.language || 'en';
19588         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19589         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19590         
19591         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19592         this.isInline = false;
19593         this.isInput = true;
19594         this.component = this.el.select('.add-on', true).first() || false;
19595         this.component = (this.component && this.component.length === 0) ? false : this.component;
19596         this.hasInput = this.component && this.inputEL().length;
19597         
19598         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19599         
19600         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19601         
19602         this.picker().on('mousedown', this.onMousedown, this);
19603         this.picker().on('click', this.onClick, this);
19604         
19605         this.picker().addClass('datepicker-dropdown');
19606         
19607         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19608             v.setStyle('width', '189px');
19609         });
19610         
19611         this.fillMonths();
19612         
19613         this.update();
19614         
19615         if(this.isInline) {
19616             this.show();
19617         }
19618         
19619     },
19620     
19621     setValue: function(v, suppressEvent)
19622     {   
19623         var o = this.getValue();
19624         
19625         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19626         
19627         this.update();
19628
19629         if(suppressEvent !== true){
19630             this.fireEvent('select', this, o, v);
19631         }
19632         
19633     },
19634     
19635     getValue: function()
19636     {
19637         return this.value;
19638     },
19639     
19640     onClick: function(e) 
19641     {
19642         e.stopPropagation();
19643         e.preventDefault();
19644         
19645         var target = e.getTarget();
19646         
19647         if(target.nodeName.toLowerCase() === 'i'){
19648             target = Roo.get(target).dom.parentNode;
19649         }
19650         
19651         var nodeName = target.nodeName;
19652         var className = target.className;
19653         var html = target.innerHTML;
19654         
19655         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19656             return;
19657         }
19658         
19659         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19660         
19661         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19662         
19663         this.hide();
19664                         
19665     },
19666     
19667     picker : function()
19668     {
19669         return this.pickerEl;
19670     },
19671     
19672     fillMonths: function()
19673     {    
19674         var i = 0;
19675         var months = this.picker().select('>.datepicker-months td', true).first();
19676         
19677         months.dom.innerHTML = '';
19678         
19679         while (i < 12) {
19680             var month = {
19681                 tag: 'span',
19682                 cls: 'month',
19683                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19684             };
19685             
19686             months.createChild(month);
19687         }
19688         
19689     },
19690     
19691     update: function()
19692     {
19693         var _this = this;
19694         
19695         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19696             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19697         }
19698         
19699         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19700             e.removeClass('active');
19701             
19702             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19703                 e.addClass('active');
19704             }
19705         })
19706     },
19707     
19708     place: function()
19709     {
19710         if(this.isInline) {
19711             return;
19712         }
19713         
19714         this.picker().removeClass(['bottom', 'top']);
19715         
19716         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19717             /*
19718              * place to the top of element!
19719              *
19720              */
19721             
19722             this.picker().addClass('top');
19723             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19724             
19725             return;
19726         }
19727         
19728         this.picker().addClass('bottom');
19729         
19730         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19731     },
19732     
19733     onFocus : function()
19734     {
19735         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19736         this.show();
19737     },
19738     
19739     onBlur : function()
19740     {
19741         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19742         
19743         var d = this.inputEl().getValue();
19744         
19745         this.setValue(d);
19746                 
19747         this.hide();
19748     },
19749     
19750     show : function()
19751     {
19752         this.picker().show();
19753         this.picker().select('>.datepicker-months', true).first().show();
19754         this.update();
19755         this.place();
19756         
19757         this.fireEvent('show', this, this.date);
19758     },
19759     
19760     hide : function()
19761     {
19762         if(this.isInline) {
19763             return;
19764         }
19765         this.picker().hide();
19766         this.fireEvent('hide', this, this.date);
19767         
19768     },
19769     
19770     onMousedown: function(e)
19771     {
19772         e.stopPropagation();
19773         e.preventDefault();
19774     },
19775     
19776     keyup: function(e)
19777     {
19778         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19779         this.update();
19780     },
19781
19782     fireKey: function(e)
19783     {
19784         if (!this.picker().isVisible()){
19785             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19786                 this.show();
19787             }
19788             return;
19789         }
19790         
19791         var dir;
19792         
19793         switch(e.keyCode){
19794             case 27: // escape
19795                 this.hide();
19796                 e.preventDefault();
19797                 break;
19798             case 37: // left
19799             case 39: // right
19800                 dir = e.keyCode == 37 ? -1 : 1;
19801                 
19802                 this.vIndex = this.vIndex + dir;
19803                 
19804                 if(this.vIndex < 0){
19805                     this.vIndex = 0;
19806                 }
19807                 
19808                 if(this.vIndex > 11){
19809                     this.vIndex = 11;
19810                 }
19811                 
19812                 if(isNaN(this.vIndex)){
19813                     this.vIndex = 0;
19814                 }
19815                 
19816                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19817                 
19818                 break;
19819             case 38: // up
19820             case 40: // down
19821                 
19822                 dir = e.keyCode == 38 ? -1 : 1;
19823                 
19824                 this.vIndex = this.vIndex + dir * 4;
19825                 
19826                 if(this.vIndex < 0){
19827                     this.vIndex = 0;
19828                 }
19829                 
19830                 if(this.vIndex > 11){
19831                     this.vIndex = 11;
19832                 }
19833                 
19834                 if(isNaN(this.vIndex)){
19835                     this.vIndex = 0;
19836                 }
19837                 
19838                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19839                 break;
19840                 
19841             case 13: // enter
19842                 
19843                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19844                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19845                 }
19846                 
19847                 this.hide();
19848                 e.preventDefault();
19849                 break;
19850             case 9: // tab
19851                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19852                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19853                 }
19854                 this.hide();
19855                 break;
19856             case 16: // shift
19857             case 17: // ctrl
19858             case 18: // alt
19859                 break;
19860             default :
19861                 this.hide();
19862                 
19863         }
19864     },
19865     
19866     remove: function() 
19867     {
19868         this.picker().remove();
19869     }
19870    
19871 });
19872
19873 Roo.apply(Roo.bootstrap.MonthField,  {
19874     
19875     content : {
19876         tag: 'tbody',
19877         cn: [
19878         {
19879             tag: 'tr',
19880             cn: [
19881             {
19882                 tag: 'td',
19883                 colspan: '7'
19884             }
19885             ]
19886         }
19887         ]
19888     },
19889     
19890     dates:{
19891         en: {
19892             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19893             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19894         }
19895     }
19896 });
19897
19898 Roo.apply(Roo.bootstrap.MonthField,  {
19899   
19900     template : {
19901         tag: 'div',
19902         cls: 'datepicker dropdown-menu roo-dynamic',
19903         cn: [
19904             {
19905                 tag: 'div',
19906                 cls: 'datepicker-months',
19907                 cn: [
19908                 {
19909                     tag: 'table',
19910                     cls: 'table-condensed',
19911                     cn:[
19912                         Roo.bootstrap.DateField.content
19913                     ]
19914                 }
19915                 ]
19916             }
19917         ]
19918     }
19919 });
19920
19921  
19922
19923  
19924  /*
19925  * - LGPL
19926  *
19927  * CheckBox
19928  * 
19929  */
19930
19931 /**
19932  * @class Roo.bootstrap.CheckBox
19933  * @extends Roo.bootstrap.Input
19934  * Bootstrap CheckBox class
19935  * 
19936  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19937  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19938  * @cfg {String} boxLabel The text that appears beside the checkbox
19939  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19940  * @cfg {Boolean} checked initnal the element
19941  * @cfg {Boolean} inline inline the element (default false)
19942  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19943  * 
19944  * @constructor
19945  * Create a new CheckBox
19946  * @param {Object} config The config object
19947  */
19948
19949 Roo.bootstrap.CheckBox = function(config){
19950     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19951    
19952     this.addEvents({
19953         /**
19954         * @event check
19955         * Fires when the element is checked or unchecked.
19956         * @param {Roo.bootstrap.CheckBox} this This input
19957         * @param {Boolean} checked The new checked value
19958         */
19959        check : true
19960     });
19961     
19962 };
19963
19964 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19965   
19966     inputType: 'checkbox',
19967     inputValue: 1,
19968     valueOff: 0,
19969     boxLabel: false,
19970     checked: false,
19971     weight : false,
19972     inline: false,
19973     
19974     getAutoCreate : function()
19975     {
19976         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19977         
19978         var id = Roo.id();
19979         
19980         var cfg = {};
19981         
19982         cfg.cls = 'form-group ' + this.inputType; //input-group
19983         
19984         if(this.inline){
19985             cfg.cls += ' ' + this.inputType + '-inline';
19986         }
19987         
19988         var input =  {
19989             tag: 'input',
19990             id : id,
19991             type : this.inputType,
19992             value : this.inputValue,
19993             cls : 'roo-' + this.inputType, //'form-box',
19994             placeholder : this.placeholder || ''
19995             
19996         };
19997         
19998         if(this.inputType != 'radio'){
19999             var hidden =  {
20000                 tag: 'input',
20001                 type : 'hidden',
20002                 cls : 'roo-hidden-value',
20003                 value : this.checked ? this.valueOff : this.inputValue
20004             };
20005         }
20006         
20007             
20008         if (this.weight) { // Validity check?
20009             cfg.cls += " " + this.inputType + "-" + this.weight;
20010         }
20011         
20012         if (this.disabled) {
20013             input.disabled=true;
20014         }
20015         
20016         if(this.checked){
20017             input.checked = this.checked;
20018             
20019         }
20020         
20021         
20022         if (this.name) {
20023             
20024             input.name = this.name;
20025             
20026             if(this.inputType != 'radio'){
20027                 hidden.name = this.name;
20028                 input.name = '_hidden_' + this.name;
20029             }
20030         }
20031         
20032         if (this.size) {
20033             input.cls += ' input-' + this.size;
20034         }
20035         
20036         var settings=this;
20037         
20038         ['xs','sm','md','lg'].map(function(size){
20039             if (settings[size]) {
20040                 cfg.cls += ' col-' + size + '-' + settings[size];
20041             }
20042         });
20043         
20044         var inputblock = input;
20045          
20046         if (this.before || this.after) {
20047             
20048             inputblock = {
20049                 cls : 'input-group',
20050                 cn :  [] 
20051             };
20052             
20053             if (this.before) {
20054                 inputblock.cn.push({
20055                     tag :'span',
20056                     cls : 'input-group-addon',
20057                     html : this.before
20058                 });
20059             }
20060             
20061             inputblock.cn.push(input);
20062             
20063             if(this.inputType != 'radio'){
20064                 inputblock.cn.push(hidden);
20065             }
20066             
20067             if (this.after) {
20068                 inputblock.cn.push({
20069                     tag :'span',
20070                     cls : 'input-group-addon',
20071                     html : this.after
20072                 });
20073             }
20074             
20075         }
20076         
20077         if (align ==='left' && this.fieldLabel.length) {
20078 //                Roo.log("left and has label");
20079             cfg.cn = [
20080                 {
20081                     tag: 'label',
20082                     'for' :  id,
20083                     cls : 'control-label',
20084                     html : this.fieldLabel
20085
20086                 },
20087                 {
20088                     cls : "", 
20089                     cn: [
20090                         inputblock
20091                     ]
20092                 }
20093             ];
20094             
20095             if(this.labelWidth > 12){
20096                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20097             }
20098             
20099             if(this.labelWidth < 13 && this.labelmd == 0){
20100                 this.labelmd = this.labelWidth;
20101             }
20102             
20103             if(this.labellg > 0){
20104                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20105                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20106             }
20107             
20108             if(this.labelmd > 0){
20109                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20110                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20111             }
20112             
20113             if(this.labelsm > 0){
20114                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20115                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20116             }
20117             
20118             if(this.labelxs > 0){
20119                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20120                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20121             }
20122             
20123         } else if ( this.fieldLabel.length) {
20124 //                Roo.log(" label");
20125                 cfg.cn = [
20126                    
20127                     {
20128                         tag: this.boxLabel ? 'span' : 'label',
20129                         'for': id,
20130                         cls: 'control-label box-input-label',
20131                         //cls : 'input-group-addon',
20132                         html : this.fieldLabel
20133                         
20134                     },
20135                     
20136                     inputblock
20137                     
20138                 ];
20139
20140         } else {
20141             
20142 //                Roo.log(" no label && no align");
20143                 cfg.cn = [  inputblock ] ;
20144                 
20145                 
20146         }
20147         
20148         if(this.boxLabel){
20149              var boxLabelCfg = {
20150                 tag: 'label',
20151                 //'for': id, // box label is handled by onclick - so no for...
20152                 cls: 'box-label',
20153                 html: this.boxLabel
20154             };
20155             
20156             if(this.tooltip){
20157                 boxLabelCfg.tooltip = this.tooltip;
20158             }
20159              
20160             cfg.cn.push(boxLabelCfg);
20161         }
20162         
20163         if(this.inputType != 'radio'){
20164             cfg.cn.push(hidden);
20165         }
20166         
20167         return cfg;
20168         
20169     },
20170     
20171     /**
20172      * return the real input element.
20173      */
20174     inputEl: function ()
20175     {
20176         return this.el.select('input.roo-' + this.inputType,true).first();
20177     },
20178     hiddenEl: function ()
20179     {
20180         return this.el.select('input.roo-hidden-value',true).first();
20181     },
20182     
20183     labelEl: function()
20184     {
20185         return this.el.select('label.control-label',true).first();
20186     },
20187     /* depricated... */
20188     
20189     label: function()
20190     {
20191         return this.labelEl();
20192     },
20193     
20194     boxLabelEl: function()
20195     {
20196         return this.el.select('label.box-label',true).first();
20197     },
20198     
20199     initEvents : function()
20200     {
20201 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20202         
20203         this.inputEl().on('click', this.onClick,  this);
20204         
20205         if (this.boxLabel) { 
20206             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20207         }
20208         
20209         this.startValue = this.getValue();
20210         
20211         if(this.groupId){
20212             Roo.bootstrap.CheckBox.register(this);
20213         }
20214     },
20215     
20216     onClick : function()
20217     {   
20218         this.setChecked(!this.checked);
20219     },
20220     
20221     setChecked : function(state,suppressEvent)
20222     {
20223         this.startValue = this.getValue();
20224
20225         if(this.inputType == 'radio'){
20226             
20227             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20228                 e.dom.checked = false;
20229             });
20230             
20231             this.inputEl().dom.checked = true;
20232             
20233             this.inputEl().dom.value = this.inputValue;
20234             
20235             if(suppressEvent !== true){
20236                 this.fireEvent('check', this, true);
20237             }
20238             
20239             this.validate();
20240             
20241             return;
20242         }
20243         
20244         this.checked = state;
20245         
20246         this.inputEl().dom.checked = state;
20247         
20248         
20249         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20250         
20251         if(suppressEvent !== true){
20252             this.fireEvent('check', this, state);
20253         }
20254         
20255         this.validate();
20256     },
20257     
20258     getValue : function()
20259     {
20260         if(this.inputType == 'radio'){
20261             return this.getGroupValue();
20262         }
20263         
20264         return this.hiddenEl().dom.value;
20265         
20266     },
20267     
20268     getGroupValue : function()
20269     {
20270         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20271             return '';
20272         }
20273         
20274         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20275     },
20276     
20277     setValue : function(v,suppressEvent)
20278     {
20279         if(this.inputType == 'radio'){
20280             this.setGroupValue(v, suppressEvent);
20281             return;
20282         }
20283         
20284         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20285         
20286         this.validate();
20287     },
20288     
20289     setGroupValue : function(v, suppressEvent)
20290     {
20291         this.startValue = this.getValue();
20292         
20293         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20294             e.dom.checked = false;
20295             
20296             if(e.dom.value == v){
20297                 e.dom.checked = true;
20298             }
20299         });
20300         
20301         if(suppressEvent !== true){
20302             this.fireEvent('check', this, true);
20303         }
20304
20305         this.validate();
20306         
20307         return;
20308     },
20309     
20310     validate : function()
20311     {
20312         if(
20313                 this.disabled || 
20314                 (this.inputType == 'radio' && this.validateRadio()) ||
20315                 (this.inputType == 'checkbox' && this.validateCheckbox())
20316         ){
20317             this.markValid();
20318             return true;
20319         }
20320         
20321         this.markInvalid();
20322         return false;
20323     },
20324     
20325     validateRadio : function()
20326     {
20327         if(this.allowBlank){
20328             return true;
20329         }
20330         
20331         var valid = false;
20332         
20333         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20334             if(!e.dom.checked){
20335                 return;
20336             }
20337             
20338             valid = true;
20339             
20340             return false;
20341         });
20342         
20343         return valid;
20344     },
20345     
20346     validateCheckbox : function()
20347     {
20348         if(!this.groupId){
20349             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20350             //return (this.getValue() == this.inputValue) ? true : false;
20351         }
20352         
20353         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20354         
20355         if(!group){
20356             return false;
20357         }
20358         
20359         var r = false;
20360         
20361         for(var i in group){
20362             if(r){
20363                 break;
20364             }
20365             
20366             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20367         }
20368         
20369         return r;
20370     },
20371     
20372     /**
20373      * Mark this field as valid
20374      */
20375     markValid : function()
20376     {
20377         var _this = this;
20378         
20379         this.fireEvent('valid', this);
20380         
20381         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20382         
20383         if(this.groupId){
20384             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20385         }
20386         
20387         if(label){
20388             label.markValid();
20389         }
20390
20391         if(this.inputType == 'radio'){
20392             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20393                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20394                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20395             });
20396             
20397             return;
20398         }
20399
20400         if(!this.groupId){
20401             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20402             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20403             return;
20404         }
20405         
20406         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20407         
20408         if(!group){
20409             return;
20410         }
20411         
20412         for(var i in group){
20413             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20414             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20415         }
20416     },
20417     
20418      /**
20419      * Mark this field as invalid
20420      * @param {String} msg The validation message
20421      */
20422     markInvalid : function(msg)
20423     {
20424         if(this.allowBlank){
20425             return;
20426         }
20427         
20428         var _this = this;
20429         
20430         this.fireEvent('invalid', this, msg);
20431         
20432         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20433         
20434         if(this.groupId){
20435             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20436         }
20437         
20438         if(label){
20439             label.markInvalid();
20440         }
20441             
20442         if(this.inputType == 'radio'){
20443             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20444                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20445                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20446             });
20447             
20448             return;
20449         }
20450         
20451         if(!this.groupId){
20452             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20453             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20454             return;
20455         }
20456         
20457         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20458         
20459         if(!group){
20460             return;
20461         }
20462         
20463         for(var i in group){
20464             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20465             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20466         }
20467         
20468     },
20469     
20470     clearInvalid : function()
20471     {
20472         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20473         
20474         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20475         
20476         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20477         
20478         if (label) {
20479             label.iconEl.removeClass(label.validClass);
20480             label.iconEl.removeClass(label.invalidClass);
20481         }
20482     },
20483     
20484     disable : function()
20485     {
20486         if(this.inputType != 'radio'){
20487             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20488             return;
20489         }
20490         
20491         var _this = this;
20492         
20493         if(this.rendered){
20494             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20495                 _this.getActionEl().addClass(this.disabledClass);
20496                 e.dom.disabled = true;
20497             });
20498         }
20499         
20500         this.disabled = true;
20501         this.fireEvent("disable", this);
20502         return this;
20503     },
20504
20505     enable : function()
20506     {
20507         if(this.inputType != 'radio'){
20508             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20509             return;
20510         }
20511         
20512         var _this = this;
20513         
20514         if(this.rendered){
20515             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20516                 _this.getActionEl().removeClass(this.disabledClass);
20517                 e.dom.disabled = false;
20518             });
20519         }
20520         
20521         this.disabled = false;
20522         this.fireEvent("enable", this);
20523         return this;
20524     }
20525
20526 });
20527
20528 Roo.apply(Roo.bootstrap.CheckBox, {
20529     
20530     groups: {},
20531     
20532      /**
20533     * register a CheckBox Group
20534     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20535     */
20536     register : function(checkbox)
20537     {
20538         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20539             this.groups[checkbox.groupId] = {};
20540         }
20541         
20542         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20543             return;
20544         }
20545         
20546         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20547         
20548     },
20549     /**
20550     * fetch a CheckBox Group based on the group ID
20551     * @param {string} the group ID
20552     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20553     */
20554     get: function(groupId) {
20555         if (typeof(this.groups[groupId]) == 'undefined') {
20556             return false;
20557         }
20558         
20559         return this.groups[groupId] ;
20560     }
20561     
20562     
20563 });
20564 /*
20565  * - LGPL
20566  *
20567  * RadioItem
20568  * 
20569  */
20570
20571 /**
20572  * @class Roo.bootstrap.Radio
20573  * @extends Roo.bootstrap.Component
20574  * Bootstrap Radio class
20575  * @cfg {String} boxLabel - the label associated
20576  * @cfg {String} value - the value of radio
20577  * 
20578  * @constructor
20579  * Create a new Radio
20580  * @param {Object} config The config object
20581  */
20582 Roo.bootstrap.Radio = function(config){
20583     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20584     
20585 };
20586
20587 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20588     
20589     boxLabel : '',
20590     
20591     value : '',
20592     
20593     getAutoCreate : function()
20594     {
20595         var cfg = {
20596             tag : 'div',
20597             cls : 'form-group radio',
20598             cn : [
20599                 {
20600                     tag : 'label',
20601                     cls : 'box-label',
20602                     html : this.boxLabel
20603                 }
20604             ]
20605         };
20606         
20607         return cfg;
20608     },
20609     
20610     initEvents : function() 
20611     {
20612         this.parent().register(this);
20613         
20614         this.el.on('click', this.onClick, this);
20615         
20616     },
20617     
20618     onClick : function()
20619     {
20620         this.setChecked(true);
20621     },
20622     
20623     setChecked : function(state, suppressEvent)
20624     {
20625         this.parent().setValue(this.value, suppressEvent);
20626         
20627     }
20628     
20629 });
20630  
20631
20632  //<script type="text/javascript">
20633
20634 /*
20635  * Based  Ext JS Library 1.1.1
20636  * Copyright(c) 2006-2007, Ext JS, LLC.
20637  * LGPL
20638  *
20639  */
20640  
20641 /**
20642  * @class Roo.HtmlEditorCore
20643  * @extends Roo.Component
20644  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20645  *
20646  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20647  */
20648
20649 Roo.HtmlEditorCore = function(config){
20650     
20651     
20652     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20653     
20654     
20655     this.addEvents({
20656         /**
20657          * @event initialize
20658          * Fires when the editor is fully initialized (including the iframe)
20659          * @param {Roo.HtmlEditorCore} this
20660          */
20661         initialize: true,
20662         /**
20663          * @event activate
20664          * Fires when the editor is first receives the focus. Any insertion must wait
20665          * until after this event.
20666          * @param {Roo.HtmlEditorCore} this
20667          */
20668         activate: true,
20669          /**
20670          * @event beforesync
20671          * Fires before the textarea is updated with content from the editor iframe. Return false
20672          * to cancel the sync.
20673          * @param {Roo.HtmlEditorCore} this
20674          * @param {String} html
20675          */
20676         beforesync: true,
20677          /**
20678          * @event beforepush
20679          * Fires before the iframe editor is updated with content from the textarea. Return false
20680          * to cancel the push.
20681          * @param {Roo.HtmlEditorCore} this
20682          * @param {String} html
20683          */
20684         beforepush: true,
20685          /**
20686          * @event sync
20687          * Fires when the textarea is updated with content from the editor iframe.
20688          * @param {Roo.HtmlEditorCore} this
20689          * @param {String} html
20690          */
20691         sync: true,
20692          /**
20693          * @event push
20694          * Fires when the iframe editor is updated with content from the textarea.
20695          * @param {Roo.HtmlEditorCore} this
20696          * @param {String} html
20697          */
20698         push: true,
20699         
20700         /**
20701          * @event editorevent
20702          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20703          * @param {Roo.HtmlEditorCore} this
20704          */
20705         editorevent: true
20706         
20707     });
20708     
20709     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20710     
20711     // defaults : white / black...
20712     this.applyBlacklists();
20713     
20714     
20715     
20716 };
20717
20718
20719 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20720
20721
20722      /**
20723      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20724      */
20725     
20726     owner : false,
20727     
20728      /**
20729      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20730      *                        Roo.resizable.
20731      */
20732     resizable : false,
20733      /**
20734      * @cfg {Number} height (in pixels)
20735      */   
20736     height: 300,
20737    /**
20738      * @cfg {Number} width (in pixels)
20739      */   
20740     width: 500,
20741     
20742     /**
20743      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20744      * 
20745      */
20746     stylesheets: false,
20747     
20748     // id of frame..
20749     frameId: false,
20750     
20751     // private properties
20752     validationEvent : false,
20753     deferHeight: true,
20754     initialized : false,
20755     activated : false,
20756     sourceEditMode : false,
20757     onFocus : Roo.emptyFn,
20758     iframePad:3,
20759     hideMode:'offsets',
20760     
20761     clearUp: true,
20762     
20763     // blacklist + whitelisted elements..
20764     black: false,
20765     white: false,
20766      
20767     
20768
20769     /**
20770      * Protected method that will not generally be called directly. It
20771      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20772      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20773      */
20774     getDocMarkup : function(){
20775         // body styles..
20776         var st = '';
20777         
20778         // inherit styels from page...?? 
20779         if (this.stylesheets === false) {
20780             
20781             Roo.get(document.head).select('style').each(function(node) {
20782                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20783             });
20784             
20785             Roo.get(document.head).select('link').each(function(node) { 
20786                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20787             });
20788             
20789         } else if (!this.stylesheets.length) {
20790                 // simple..
20791                 st = '<style type="text/css">' +
20792                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20793                    '</style>';
20794         } else { 
20795             
20796         }
20797         
20798         st +=  '<style type="text/css">' +
20799             'IMG { cursor: pointer } ' +
20800         '</style>';
20801
20802         
20803         return '<html><head>' + st  +
20804             //<style type="text/css">' +
20805             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20806             //'</style>' +
20807             ' </head><body class="roo-htmleditor-body"></body></html>';
20808     },
20809
20810     // private
20811     onRender : function(ct, position)
20812     {
20813         var _t = this;
20814         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20815         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20816         
20817         
20818         this.el.dom.style.border = '0 none';
20819         this.el.dom.setAttribute('tabIndex', -1);
20820         this.el.addClass('x-hidden hide');
20821         
20822         
20823         
20824         if(Roo.isIE){ // fix IE 1px bogus margin
20825             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20826         }
20827        
20828         
20829         this.frameId = Roo.id();
20830         
20831          
20832         
20833         var iframe = this.owner.wrap.createChild({
20834             tag: 'iframe',
20835             cls: 'form-control', // bootstrap..
20836             id: this.frameId,
20837             name: this.frameId,
20838             frameBorder : 'no',
20839             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20840         }, this.el
20841         );
20842         
20843         
20844         this.iframe = iframe.dom;
20845
20846          this.assignDocWin();
20847         
20848         this.doc.designMode = 'on';
20849        
20850         this.doc.open();
20851         this.doc.write(this.getDocMarkup());
20852         this.doc.close();
20853
20854         
20855         var task = { // must defer to wait for browser to be ready
20856             run : function(){
20857                 //console.log("run task?" + this.doc.readyState);
20858                 this.assignDocWin();
20859                 if(this.doc.body || this.doc.readyState == 'complete'){
20860                     try {
20861                         this.doc.designMode="on";
20862                     } catch (e) {
20863                         return;
20864                     }
20865                     Roo.TaskMgr.stop(task);
20866                     this.initEditor.defer(10, this);
20867                 }
20868             },
20869             interval : 10,
20870             duration: 10000,
20871             scope: this
20872         };
20873         Roo.TaskMgr.start(task);
20874
20875     },
20876
20877     // private
20878     onResize : function(w, h)
20879     {
20880          Roo.log('resize: ' +w + ',' + h );
20881         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20882         if(!this.iframe){
20883             return;
20884         }
20885         if(typeof w == 'number'){
20886             
20887             this.iframe.style.width = w + 'px';
20888         }
20889         if(typeof h == 'number'){
20890             
20891             this.iframe.style.height = h + 'px';
20892             if(this.doc){
20893                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20894             }
20895         }
20896         
20897     },
20898
20899     /**
20900      * Toggles the editor between standard and source edit mode.
20901      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20902      */
20903     toggleSourceEdit : function(sourceEditMode){
20904         
20905         this.sourceEditMode = sourceEditMode === true;
20906         
20907         if(this.sourceEditMode){
20908  
20909             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20910             
20911         }else{
20912             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20913             //this.iframe.className = '';
20914             this.deferFocus();
20915         }
20916         //this.setSize(this.owner.wrap.getSize());
20917         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20918     },
20919
20920     
20921   
20922
20923     /**
20924      * Protected method that will not generally be called directly. If you need/want
20925      * custom HTML cleanup, this is the method you should override.
20926      * @param {String} html The HTML to be cleaned
20927      * return {String} The cleaned HTML
20928      */
20929     cleanHtml : function(html){
20930         html = String(html);
20931         if(html.length > 5){
20932             if(Roo.isSafari){ // strip safari nonsense
20933                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20934             }
20935         }
20936         if(html == '&nbsp;'){
20937             html = '';
20938         }
20939         return html;
20940     },
20941
20942     /**
20943      * HTML Editor -> Textarea
20944      * Protected method that will not generally be called directly. Syncs the contents
20945      * of the editor iframe with the textarea.
20946      */
20947     syncValue : function(){
20948         if(this.initialized){
20949             var bd = (this.doc.body || this.doc.documentElement);
20950             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20951             var html = bd.innerHTML;
20952             if(Roo.isSafari){
20953                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20954                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20955                 if(m && m[1]){
20956                     html = '<div style="'+m[0]+'">' + html + '</div>';
20957                 }
20958             }
20959             html = this.cleanHtml(html);
20960             // fix up the special chars.. normaly like back quotes in word...
20961             // however we do not want to do this with chinese..
20962             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20963                 var cc = b.charCodeAt();
20964                 if (
20965                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20966                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20967                     (cc >= 0xf900 && cc < 0xfb00 )
20968                 ) {
20969                         return b;
20970                 }
20971                 return "&#"+cc+";" 
20972             });
20973             if(this.owner.fireEvent('beforesync', this, html) !== false){
20974                 this.el.dom.value = html;
20975                 this.owner.fireEvent('sync', this, html);
20976             }
20977         }
20978     },
20979
20980     /**
20981      * Protected method that will not generally be called directly. Pushes the value of the textarea
20982      * into the iframe editor.
20983      */
20984     pushValue : function(){
20985         if(this.initialized){
20986             var v = this.el.dom.value.trim();
20987             
20988 //            if(v.length < 1){
20989 //                v = '&#160;';
20990 //            }
20991             
20992             if(this.owner.fireEvent('beforepush', this, v) !== false){
20993                 var d = (this.doc.body || this.doc.documentElement);
20994                 d.innerHTML = v;
20995                 this.cleanUpPaste();
20996                 this.el.dom.value = d.innerHTML;
20997                 this.owner.fireEvent('push', this, v);
20998             }
20999         }
21000     },
21001
21002     // private
21003     deferFocus : function(){
21004         this.focus.defer(10, this);
21005     },
21006
21007     // doc'ed in Field
21008     focus : function(){
21009         if(this.win && !this.sourceEditMode){
21010             this.win.focus();
21011         }else{
21012             this.el.focus();
21013         }
21014     },
21015     
21016     assignDocWin: function()
21017     {
21018         var iframe = this.iframe;
21019         
21020          if(Roo.isIE){
21021             this.doc = iframe.contentWindow.document;
21022             this.win = iframe.contentWindow;
21023         } else {
21024 //            if (!Roo.get(this.frameId)) {
21025 //                return;
21026 //            }
21027 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21028 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21029             
21030             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21031                 return;
21032             }
21033             
21034             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21035             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21036         }
21037     },
21038     
21039     // private
21040     initEditor : function(){
21041         //console.log("INIT EDITOR");
21042         this.assignDocWin();
21043         
21044         
21045         
21046         this.doc.designMode="on";
21047         this.doc.open();
21048         this.doc.write(this.getDocMarkup());
21049         this.doc.close();
21050         
21051         var dbody = (this.doc.body || this.doc.documentElement);
21052         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21053         // this copies styles from the containing element into thsi one..
21054         // not sure why we need all of this..
21055         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21056         
21057         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21058         //ss['background-attachment'] = 'fixed'; // w3c
21059         dbody.bgProperties = 'fixed'; // ie
21060         //Roo.DomHelper.applyStyles(dbody, ss);
21061         Roo.EventManager.on(this.doc, {
21062             //'mousedown': this.onEditorEvent,
21063             'mouseup': this.onEditorEvent,
21064             'dblclick': this.onEditorEvent,
21065             'click': this.onEditorEvent,
21066             'keyup': this.onEditorEvent,
21067             buffer:100,
21068             scope: this
21069         });
21070         if(Roo.isGecko){
21071             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21072         }
21073         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21074             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21075         }
21076         this.initialized = true;
21077
21078         this.owner.fireEvent('initialize', this);
21079         this.pushValue();
21080     },
21081
21082     // private
21083     onDestroy : function(){
21084         
21085         
21086         
21087         if(this.rendered){
21088             
21089             //for (var i =0; i < this.toolbars.length;i++) {
21090             //    // fixme - ask toolbars for heights?
21091             //    this.toolbars[i].onDestroy();
21092            // }
21093             
21094             //this.wrap.dom.innerHTML = '';
21095             //this.wrap.remove();
21096         }
21097     },
21098
21099     // private
21100     onFirstFocus : function(){
21101         
21102         this.assignDocWin();
21103         
21104         
21105         this.activated = true;
21106          
21107     
21108         if(Roo.isGecko){ // prevent silly gecko errors
21109             this.win.focus();
21110             var s = this.win.getSelection();
21111             if(!s.focusNode || s.focusNode.nodeType != 3){
21112                 var r = s.getRangeAt(0);
21113                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21114                 r.collapse(true);
21115                 this.deferFocus();
21116             }
21117             try{
21118                 this.execCmd('useCSS', true);
21119                 this.execCmd('styleWithCSS', false);
21120             }catch(e){}
21121         }
21122         this.owner.fireEvent('activate', this);
21123     },
21124
21125     // private
21126     adjustFont: function(btn){
21127         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21128         //if(Roo.isSafari){ // safari
21129         //    adjust *= 2;
21130        // }
21131         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21132         if(Roo.isSafari){ // safari
21133             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21134             v =  (v < 10) ? 10 : v;
21135             v =  (v > 48) ? 48 : v;
21136             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21137             
21138         }
21139         
21140         
21141         v = Math.max(1, v+adjust);
21142         
21143         this.execCmd('FontSize', v  );
21144     },
21145
21146     onEditorEvent : function(e)
21147     {
21148         this.owner.fireEvent('editorevent', this, e);
21149       //  this.updateToolbar();
21150         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21151     },
21152
21153     insertTag : function(tg)
21154     {
21155         // could be a bit smarter... -> wrap the current selected tRoo..
21156         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21157             
21158             range = this.createRange(this.getSelection());
21159             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21160             wrappingNode.appendChild(range.extractContents());
21161             range.insertNode(wrappingNode);
21162
21163             return;
21164             
21165             
21166             
21167         }
21168         this.execCmd("formatblock",   tg);
21169         
21170     },
21171     
21172     insertText : function(txt)
21173     {
21174         
21175         
21176         var range = this.createRange();
21177         range.deleteContents();
21178                //alert(Sender.getAttribute('label'));
21179                
21180         range.insertNode(this.doc.createTextNode(txt));
21181     } ,
21182     
21183      
21184
21185     /**
21186      * Executes a Midas editor command on the editor document and performs necessary focus and
21187      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21188      * @param {String} cmd The Midas command
21189      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21190      */
21191     relayCmd : function(cmd, value){
21192         this.win.focus();
21193         this.execCmd(cmd, value);
21194         this.owner.fireEvent('editorevent', this);
21195         //this.updateToolbar();
21196         this.owner.deferFocus();
21197     },
21198
21199     /**
21200      * Executes a Midas editor command directly on the editor document.
21201      * For visual commands, you should use {@link #relayCmd} instead.
21202      * <b>This should only be called after the editor is initialized.</b>
21203      * @param {String} cmd The Midas command
21204      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21205      */
21206     execCmd : function(cmd, value){
21207         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21208         this.syncValue();
21209     },
21210  
21211  
21212    
21213     /**
21214      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21215      * to insert tRoo.
21216      * @param {String} text | dom node.. 
21217      */
21218     insertAtCursor : function(text)
21219     {
21220         
21221         
21222         
21223         if(!this.activated){
21224             return;
21225         }
21226         /*
21227         if(Roo.isIE){
21228             this.win.focus();
21229             var r = this.doc.selection.createRange();
21230             if(r){
21231                 r.collapse(true);
21232                 r.pasteHTML(text);
21233                 this.syncValue();
21234                 this.deferFocus();
21235             
21236             }
21237             return;
21238         }
21239         */
21240         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21241             this.win.focus();
21242             
21243             
21244             // from jquery ui (MIT licenced)
21245             var range, node;
21246             var win = this.win;
21247             
21248             if (win.getSelection && win.getSelection().getRangeAt) {
21249                 range = win.getSelection().getRangeAt(0);
21250                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21251                 range.insertNode(node);
21252             } else if (win.document.selection && win.document.selection.createRange) {
21253                 // no firefox support
21254                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21255                 win.document.selection.createRange().pasteHTML(txt);
21256             } else {
21257                 // no firefox support
21258                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21259                 this.execCmd('InsertHTML', txt);
21260             } 
21261             
21262             this.syncValue();
21263             
21264             this.deferFocus();
21265         }
21266     },
21267  // private
21268     mozKeyPress : function(e){
21269         if(e.ctrlKey){
21270             var c = e.getCharCode(), cmd;
21271           
21272             if(c > 0){
21273                 c = String.fromCharCode(c).toLowerCase();
21274                 switch(c){
21275                     case 'b':
21276                         cmd = 'bold';
21277                         break;
21278                     case 'i':
21279                         cmd = 'italic';
21280                         break;
21281                     
21282                     case 'u':
21283                         cmd = 'underline';
21284                         break;
21285                     
21286                     case 'v':
21287                         this.cleanUpPaste.defer(100, this);
21288                         return;
21289                         
21290                 }
21291                 if(cmd){
21292                     this.win.focus();
21293                     this.execCmd(cmd);
21294                     this.deferFocus();
21295                     e.preventDefault();
21296                 }
21297                 
21298             }
21299         }
21300     },
21301
21302     // private
21303     fixKeys : function(){ // load time branching for fastest keydown performance
21304         if(Roo.isIE){
21305             return function(e){
21306                 var k = e.getKey(), r;
21307                 if(k == e.TAB){
21308                     e.stopEvent();
21309                     r = this.doc.selection.createRange();
21310                     if(r){
21311                         r.collapse(true);
21312                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21313                         this.deferFocus();
21314                     }
21315                     return;
21316                 }
21317                 
21318                 if(k == e.ENTER){
21319                     r = this.doc.selection.createRange();
21320                     if(r){
21321                         var target = r.parentElement();
21322                         if(!target || target.tagName.toLowerCase() != 'li'){
21323                             e.stopEvent();
21324                             r.pasteHTML('<br />');
21325                             r.collapse(false);
21326                             r.select();
21327                         }
21328                     }
21329                 }
21330                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21331                     this.cleanUpPaste.defer(100, this);
21332                     return;
21333                 }
21334                 
21335                 
21336             };
21337         }else if(Roo.isOpera){
21338             return function(e){
21339                 var k = e.getKey();
21340                 if(k == e.TAB){
21341                     e.stopEvent();
21342                     this.win.focus();
21343                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21344                     this.deferFocus();
21345                 }
21346                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21347                     this.cleanUpPaste.defer(100, this);
21348                     return;
21349                 }
21350                 
21351             };
21352         }else if(Roo.isSafari){
21353             return function(e){
21354                 var k = e.getKey();
21355                 
21356                 if(k == e.TAB){
21357                     e.stopEvent();
21358                     this.execCmd('InsertText','\t');
21359                     this.deferFocus();
21360                     return;
21361                 }
21362                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21363                     this.cleanUpPaste.defer(100, this);
21364                     return;
21365                 }
21366                 
21367              };
21368         }
21369     }(),
21370     
21371     getAllAncestors: function()
21372     {
21373         var p = this.getSelectedNode();
21374         var a = [];
21375         if (!p) {
21376             a.push(p); // push blank onto stack..
21377             p = this.getParentElement();
21378         }
21379         
21380         
21381         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21382             a.push(p);
21383             p = p.parentNode;
21384         }
21385         a.push(this.doc.body);
21386         return a;
21387     },
21388     lastSel : false,
21389     lastSelNode : false,
21390     
21391     
21392     getSelection : function() 
21393     {
21394         this.assignDocWin();
21395         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21396     },
21397     
21398     getSelectedNode: function() 
21399     {
21400         // this may only work on Gecko!!!
21401         
21402         // should we cache this!!!!
21403         
21404         
21405         
21406          
21407         var range = this.createRange(this.getSelection()).cloneRange();
21408         
21409         if (Roo.isIE) {
21410             var parent = range.parentElement();
21411             while (true) {
21412                 var testRange = range.duplicate();
21413                 testRange.moveToElementText(parent);
21414                 if (testRange.inRange(range)) {
21415                     break;
21416                 }
21417                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21418                     break;
21419                 }
21420                 parent = parent.parentElement;
21421             }
21422             return parent;
21423         }
21424         
21425         // is ancestor a text element.
21426         var ac =  range.commonAncestorContainer;
21427         if (ac.nodeType == 3) {
21428             ac = ac.parentNode;
21429         }
21430         
21431         var ar = ac.childNodes;
21432          
21433         var nodes = [];
21434         var other_nodes = [];
21435         var has_other_nodes = false;
21436         for (var i=0;i<ar.length;i++) {
21437             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21438                 continue;
21439             }
21440             // fullly contained node.
21441             
21442             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21443                 nodes.push(ar[i]);
21444                 continue;
21445             }
21446             
21447             // probably selected..
21448             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21449                 other_nodes.push(ar[i]);
21450                 continue;
21451             }
21452             // outer..
21453             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21454                 continue;
21455             }
21456             
21457             
21458             has_other_nodes = true;
21459         }
21460         if (!nodes.length && other_nodes.length) {
21461             nodes= other_nodes;
21462         }
21463         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21464             return false;
21465         }
21466         
21467         return nodes[0];
21468     },
21469     createRange: function(sel)
21470     {
21471         // this has strange effects when using with 
21472         // top toolbar - not sure if it's a great idea.
21473         //this.editor.contentWindow.focus();
21474         if (typeof sel != "undefined") {
21475             try {
21476                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21477             } catch(e) {
21478                 return this.doc.createRange();
21479             }
21480         } else {
21481             return this.doc.createRange();
21482         }
21483     },
21484     getParentElement: function()
21485     {
21486         
21487         this.assignDocWin();
21488         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21489         
21490         var range = this.createRange(sel);
21491          
21492         try {
21493             var p = range.commonAncestorContainer;
21494             while (p.nodeType == 3) { // text node
21495                 p = p.parentNode;
21496             }
21497             return p;
21498         } catch (e) {
21499             return null;
21500         }
21501     
21502     },
21503     /***
21504      *
21505      * Range intersection.. the hard stuff...
21506      *  '-1' = before
21507      *  '0' = hits..
21508      *  '1' = after.
21509      *         [ -- selected range --- ]
21510      *   [fail]                        [fail]
21511      *
21512      *    basically..
21513      *      if end is before start or  hits it. fail.
21514      *      if start is after end or hits it fail.
21515      *
21516      *   if either hits (but other is outside. - then it's not 
21517      *   
21518      *    
21519      **/
21520     
21521     
21522     // @see http://www.thismuchiknow.co.uk/?p=64.
21523     rangeIntersectsNode : function(range, node)
21524     {
21525         var nodeRange = node.ownerDocument.createRange();
21526         try {
21527             nodeRange.selectNode(node);
21528         } catch (e) {
21529             nodeRange.selectNodeContents(node);
21530         }
21531     
21532         var rangeStartRange = range.cloneRange();
21533         rangeStartRange.collapse(true);
21534     
21535         var rangeEndRange = range.cloneRange();
21536         rangeEndRange.collapse(false);
21537     
21538         var nodeStartRange = nodeRange.cloneRange();
21539         nodeStartRange.collapse(true);
21540     
21541         var nodeEndRange = nodeRange.cloneRange();
21542         nodeEndRange.collapse(false);
21543     
21544         return rangeStartRange.compareBoundaryPoints(
21545                  Range.START_TO_START, nodeEndRange) == -1 &&
21546                rangeEndRange.compareBoundaryPoints(
21547                  Range.START_TO_START, nodeStartRange) == 1;
21548         
21549          
21550     },
21551     rangeCompareNode : function(range, node)
21552     {
21553         var nodeRange = node.ownerDocument.createRange();
21554         try {
21555             nodeRange.selectNode(node);
21556         } catch (e) {
21557             nodeRange.selectNodeContents(node);
21558         }
21559         
21560         
21561         range.collapse(true);
21562     
21563         nodeRange.collapse(true);
21564      
21565         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21566         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21567          
21568         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21569         
21570         var nodeIsBefore   =  ss == 1;
21571         var nodeIsAfter    = ee == -1;
21572         
21573         if (nodeIsBefore && nodeIsAfter) {
21574             return 0; // outer
21575         }
21576         if (!nodeIsBefore && nodeIsAfter) {
21577             return 1; //right trailed.
21578         }
21579         
21580         if (nodeIsBefore && !nodeIsAfter) {
21581             return 2;  // left trailed.
21582         }
21583         // fully contined.
21584         return 3;
21585     },
21586
21587     // private? - in a new class?
21588     cleanUpPaste :  function()
21589     {
21590         // cleans up the whole document..
21591         Roo.log('cleanuppaste');
21592         
21593         this.cleanUpChildren(this.doc.body);
21594         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21595         if (clean != this.doc.body.innerHTML) {
21596             this.doc.body.innerHTML = clean;
21597         }
21598         
21599     },
21600     
21601     cleanWordChars : function(input) {// change the chars to hex code
21602         var he = Roo.HtmlEditorCore;
21603         
21604         var output = input;
21605         Roo.each(he.swapCodes, function(sw) { 
21606             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21607             
21608             output = output.replace(swapper, sw[1]);
21609         });
21610         
21611         return output;
21612     },
21613     
21614     
21615     cleanUpChildren : function (n)
21616     {
21617         if (!n.childNodes.length) {
21618             return;
21619         }
21620         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21621            this.cleanUpChild(n.childNodes[i]);
21622         }
21623     },
21624     
21625     
21626         
21627     
21628     cleanUpChild : function (node)
21629     {
21630         var ed = this;
21631         //console.log(node);
21632         if (node.nodeName == "#text") {
21633             // clean up silly Windows -- stuff?
21634             return; 
21635         }
21636         if (node.nodeName == "#comment") {
21637             node.parentNode.removeChild(node);
21638             // clean up silly Windows -- stuff?
21639             return; 
21640         }
21641         var lcname = node.tagName.toLowerCase();
21642         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21643         // whitelist of tags..
21644         
21645         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21646             // remove node.
21647             node.parentNode.removeChild(node);
21648             return;
21649             
21650         }
21651         
21652         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21653         
21654         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21655         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21656         
21657         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21658         //    remove_keep_children = true;
21659         //}
21660         
21661         if (remove_keep_children) {
21662             this.cleanUpChildren(node);
21663             // inserts everything just before this node...
21664             while (node.childNodes.length) {
21665                 var cn = node.childNodes[0];
21666                 node.removeChild(cn);
21667                 node.parentNode.insertBefore(cn, node);
21668             }
21669             node.parentNode.removeChild(node);
21670             return;
21671         }
21672         
21673         if (!node.attributes || !node.attributes.length) {
21674             this.cleanUpChildren(node);
21675             return;
21676         }
21677         
21678         function cleanAttr(n,v)
21679         {
21680             
21681             if (v.match(/^\./) || v.match(/^\//)) {
21682                 return;
21683             }
21684             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21685                 return;
21686             }
21687             if (v.match(/^#/)) {
21688                 return;
21689             }
21690 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21691             node.removeAttribute(n);
21692             
21693         }
21694         
21695         var cwhite = this.cwhite;
21696         var cblack = this.cblack;
21697             
21698         function cleanStyle(n,v)
21699         {
21700             if (v.match(/expression/)) { //XSS?? should we even bother..
21701                 node.removeAttribute(n);
21702                 return;
21703             }
21704             
21705             var parts = v.split(/;/);
21706             var clean = [];
21707             
21708             Roo.each(parts, function(p) {
21709                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21710                 if (!p.length) {
21711                     return true;
21712                 }
21713                 var l = p.split(':').shift().replace(/\s+/g,'');
21714                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21715                 
21716                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21717 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21718                     //node.removeAttribute(n);
21719                     return true;
21720                 }
21721                 //Roo.log()
21722                 // only allow 'c whitelisted system attributes'
21723                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21724 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21725                     //node.removeAttribute(n);
21726                     return true;
21727                 }
21728                 
21729                 
21730                  
21731                 
21732                 clean.push(p);
21733                 return true;
21734             });
21735             if (clean.length) { 
21736                 node.setAttribute(n, clean.join(';'));
21737             } else {
21738                 node.removeAttribute(n);
21739             }
21740             
21741         }
21742         
21743         
21744         for (var i = node.attributes.length-1; i > -1 ; i--) {
21745             var a = node.attributes[i];
21746             //console.log(a);
21747             
21748             if (a.name.toLowerCase().substr(0,2)=='on')  {
21749                 node.removeAttribute(a.name);
21750                 continue;
21751             }
21752             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21753                 node.removeAttribute(a.name);
21754                 continue;
21755             }
21756             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21757                 cleanAttr(a.name,a.value); // fixme..
21758                 continue;
21759             }
21760             if (a.name == 'style') {
21761                 cleanStyle(a.name,a.value);
21762                 continue;
21763             }
21764             /// clean up MS crap..
21765             // tecnically this should be a list of valid class'es..
21766             
21767             
21768             if (a.name == 'class') {
21769                 if (a.value.match(/^Mso/)) {
21770                     node.className = '';
21771                 }
21772                 
21773                 if (a.value.match(/body/)) {
21774                     node.className = '';
21775                 }
21776                 continue;
21777             }
21778             
21779             // style cleanup!?
21780             // class cleanup?
21781             
21782         }
21783         
21784         
21785         this.cleanUpChildren(node);
21786         
21787         
21788     },
21789     
21790     /**
21791      * Clean up MS wordisms...
21792      */
21793     cleanWord : function(node)
21794     {
21795         
21796         
21797         if (!node) {
21798             this.cleanWord(this.doc.body);
21799             return;
21800         }
21801         if (node.nodeName == "#text") {
21802             // clean up silly Windows -- stuff?
21803             return; 
21804         }
21805         if (node.nodeName == "#comment") {
21806             node.parentNode.removeChild(node);
21807             // clean up silly Windows -- stuff?
21808             return; 
21809         }
21810         
21811         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21812             node.parentNode.removeChild(node);
21813             return;
21814         }
21815         
21816         // remove - but keep children..
21817         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21818             while (node.childNodes.length) {
21819                 var cn = node.childNodes[0];
21820                 node.removeChild(cn);
21821                 node.parentNode.insertBefore(cn, node);
21822             }
21823             node.parentNode.removeChild(node);
21824             this.iterateChildren(node, this.cleanWord);
21825             return;
21826         }
21827         // clean styles
21828         if (node.className.length) {
21829             
21830             var cn = node.className.split(/\W+/);
21831             var cna = [];
21832             Roo.each(cn, function(cls) {
21833                 if (cls.match(/Mso[a-zA-Z]+/)) {
21834                     return;
21835                 }
21836                 cna.push(cls);
21837             });
21838             node.className = cna.length ? cna.join(' ') : '';
21839             if (!cna.length) {
21840                 node.removeAttribute("class");
21841             }
21842         }
21843         
21844         if (node.hasAttribute("lang")) {
21845             node.removeAttribute("lang");
21846         }
21847         
21848         if (node.hasAttribute("style")) {
21849             
21850             var styles = node.getAttribute("style").split(";");
21851             var nstyle = [];
21852             Roo.each(styles, function(s) {
21853                 if (!s.match(/:/)) {
21854                     return;
21855                 }
21856                 var kv = s.split(":");
21857                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21858                     return;
21859                 }
21860                 // what ever is left... we allow.
21861                 nstyle.push(s);
21862             });
21863             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21864             if (!nstyle.length) {
21865                 node.removeAttribute('style');
21866             }
21867         }
21868         this.iterateChildren(node, this.cleanWord);
21869         
21870         
21871         
21872     },
21873     /**
21874      * iterateChildren of a Node, calling fn each time, using this as the scole..
21875      * @param {DomNode} node node to iterate children of.
21876      * @param {Function} fn method of this class to call on each item.
21877      */
21878     iterateChildren : function(node, fn)
21879     {
21880         if (!node.childNodes.length) {
21881                 return;
21882         }
21883         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21884            fn.call(this, node.childNodes[i])
21885         }
21886     },
21887     
21888     
21889     /**
21890      * cleanTableWidths.
21891      *
21892      * Quite often pasting from word etc.. results in tables with column and widths.
21893      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21894      *
21895      */
21896     cleanTableWidths : function(node)
21897     {
21898          
21899          
21900         if (!node) {
21901             this.cleanTableWidths(this.doc.body);
21902             return;
21903         }
21904         
21905         // ignore list...
21906         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21907             return; 
21908         }
21909         Roo.log(node.tagName);
21910         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21911             this.iterateChildren(node, this.cleanTableWidths);
21912             return;
21913         }
21914         if (node.hasAttribute('width')) {
21915             node.removeAttribute('width');
21916         }
21917         
21918          
21919         if (node.hasAttribute("style")) {
21920             // pretty basic...
21921             
21922             var styles = node.getAttribute("style").split(";");
21923             var nstyle = [];
21924             Roo.each(styles, function(s) {
21925                 if (!s.match(/:/)) {
21926                     return;
21927                 }
21928                 var kv = s.split(":");
21929                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21930                     return;
21931                 }
21932                 // what ever is left... we allow.
21933                 nstyle.push(s);
21934             });
21935             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21936             if (!nstyle.length) {
21937                 node.removeAttribute('style');
21938             }
21939         }
21940         
21941         this.iterateChildren(node, this.cleanTableWidths);
21942         
21943         
21944     },
21945     
21946     
21947     
21948     
21949     domToHTML : function(currentElement, depth, nopadtext) {
21950         
21951         depth = depth || 0;
21952         nopadtext = nopadtext || false;
21953     
21954         if (!currentElement) {
21955             return this.domToHTML(this.doc.body);
21956         }
21957         
21958         //Roo.log(currentElement);
21959         var j;
21960         var allText = false;
21961         var nodeName = currentElement.nodeName;
21962         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21963         
21964         if  (nodeName == '#text') {
21965             
21966             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21967         }
21968         
21969         
21970         var ret = '';
21971         if (nodeName != 'BODY') {
21972              
21973             var i = 0;
21974             // Prints the node tagName, such as <A>, <IMG>, etc
21975             if (tagName) {
21976                 var attr = [];
21977                 for(i = 0; i < currentElement.attributes.length;i++) {
21978                     // quoting?
21979                     var aname = currentElement.attributes.item(i).name;
21980                     if (!currentElement.attributes.item(i).value.length) {
21981                         continue;
21982                     }
21983                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21984                 }
21985                 
21986                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21987             } 
21988             else {
21989                 
21990                 // eack
21991             }
21992         } else {
21993             tagName = false;
21994         }
21995         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21996             return ret;
21997         }
21998         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21999             nopadtext = true;
22000         }
22001         
22002         
22003         // Traverse the tree
22004         i = 0;
22005         var currentElementChild = currentElement.childNodes.item(i);
22006         var allText = true;
22007         var innerHTML  = '';
22008         lastnode = '';
22009         while (currentElementChild) {
22010             // Formatting code (indent the tree so it looks nice on the screen)
22011             var nopad = nopadtext;
22012             if (lastnode == 'SPAN') {
22013                 nopad  = true;
22014             }
22015             // text
22016             if  (currentElementChild.nodeName == '#text') {
22017                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22018                 toadd = nopadtext ? toadd : toadd.trim();
22019                 if (!nopad && toadd.length > 80) {
22020                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22021                 }
22022                 innerHTML  += toadd;
22023                 
22024                 i++;
22025                 currentElementChild = currentElement.childNodes.item(i);
22026                 lastNode = '';
22027                 continue;
22028             }
22029             allText = false;
22030             
22031             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22032                 
22033             // Recursively traverse the tree structure of the child node
22034             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22035             lastnode = currentElementChild.nodeName;
22036             i++;
22037             currentElementChild=currentElement.childNodes.item(i);
22038         }
22039         
22040         ret += innerHTML;
22041         
22042         if (!allText) {
22043                 // The remaining code is mostly for formatting the tree
22044             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22045         }
22046         
22047         
22048         if (tagName) {
22049             ret+= "</"+tagName+">";
22050         }
22051         return ret;
22052         
22053     },
22054         
22055     applyBlacklists : function()
22056     {
22057         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22058         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22059         
22060         this.white = [];
22061         this.black = [];
22062         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22063             if (b.indexOf(tag) > -1) {
22064                 return;
22065             }
22066             this.white.push(tag);
22067             
22068         }, this);
22069         
22070         Roo.each(w, function(tag) {
22071             if (b.indexOf(tag) > -1) {
22072                 return;
22073             }
22074             if (this.white.indexOf(tag) > -1) {
22075                 return;
22076             }
22077             this.white.push(tag);
22078             
22079         }, this);
22080         
22081         
22082         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22083             if (w.indexOf(tag) > -1) {
22084                 return;
22085             }
22086             this.black.push(tag);
22087             
22088         }, this);
22089         
22090         Roo.each(b, function(tag) {
22091             if (w.indexOf(tag) > -1) {
22092                 return;
22093             }
22094             if (this.black.indexOf(tag) > -1) {
22095                 return;
22096             }
22097             this.black.push(tag);
22098             
22099         }, this);
22100         
22101         
22102         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22103         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22104         
22105         this.cwhite = [];
22106         this.cblack = [];
22107         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22108             if (b.indexOf(tag) > -1) {
22109                 return;
22110             }
22111             this.cwhite.push(tag);
22112             
22113         }, this);
22114         
22115         Roo.each(w, function(tag) {
22116             if (b.indexOf(tag) > -1) {
22117                 return;
22118             }
22119             if (this.cwhite.indexOf(tag) > -1) {
22120                 return;
22121             }
22122             this.cwhite.push(tag);
22123             
22124         }, this);
22125         
22126         
22127         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22128             if (w.indexOf(tag) > -1) {
22129                 return;
22130             }
22131             this.cblack.push(tag);
22132             
22133         }, this);
22134         
22135         Roo.each(b, function(tag) {
22136             if (w.indexOf(tag) > -1) {
22137                 return;
22138             }
22139             if (this.cblack.indexOf(tag) > -1) {
22140                 return;
22141             }
22142             this.cblack.push(tag);
22143             
22144         }, this);
22145     },
22146     
22147     setStylesheets : function(stylesheets)
22148     {
22149         if(typeof(stylesheets) == 'string'){
22150             Roo.get(this.iframe.contentDocument.head).createChild({
22151                 tag : 'link',
22152                 rel : 'stylesheet',
22153                 type : 'text/css',
22154                 href : stylesheets
22155             });
22156             
22157             return;
22158         }
22159         var _this = this;
22160      
22161         Roo.each(stylesheets, function(s) {
22162             if(!s.length){
22163                 return;
22164             }
22165             
22166             Roo.get(_this.iframe.contentDocument.head).createChild({
22167                 tag : 'link',
22168                 rel : 'stylesheet',
22169                 type : 'text/css',
22170                 href : s
22171             });
22172         });
22173
22174         
22175     },
22176     
22177     removeStylesheets : function()
22178     {
22179         var _this = this;
22180         
22181         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22182             s.remove();
22183         });
22184     }
22185     
22186     // hide stuff that is not compatible
22187     /**
22188      * @event blur
22189      * @hide
22190      */
22191     /**
22192      * @event change
22193      * @hide
22194      */
22195     /**
22196      * @event focus
22197      * @hide
22198      */
22199     /**
22200      * @event specialkey
22201      * @hide
22202      */
22203     /**
22204      * @cfg {String} fieldClass @hide
22205      */
22206     /**
22207      * @cfg {String} focusClass @hide
22208      */
22209     /**
22210      * @cfg {String} autoCreate @hide
22211      */
22212     /**
22213      * @cfg {String} inputType @hide
22214      */
22215     /**
22216      * @cfg {String} invalidClass @hide
22217      */
22218     /**
22219      * @cfg {String} invalidText @hide
22220      */
22221     /**
22222      * @cfg {String} msgFx @hide
22223      */
22224     /**
22225      * @cfg {String} validateOnBlur @hide
22226      */
22227 });
22228
22229 Roo.HtmlEditorCore.white = [
22230         'area', 'br', 'img', 'input', 'hr', 'wbr',
22231         
22232        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22233        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22234        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22235        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22236        'table',   'ul',         'xmp', 
22237        
22238        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22239       'thead',   'tr', 
22240      
22241       'dir', 'menu', 'ol', 'ul', 'dl',
22242        
22243       'embed',  'object'
22244 ];
22245
22246
22247 Roo.HtmlEditorCore.black = [
22248     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22249         'applet', // 
22250         'base',   'basefont', 'bgsound', 'blink',  'body', 
22251         'frame',  'frameset', 'head',    'html',   'ilayer', 
22252         'iframe', 'layer',  'link',     'meta',    'object',   
22253         'script', 'style' ,'title',  'xml' // clean later..
22254 ];
22255 Roo.HtmlEditorCore.clean = [
22256     'script', 'style', 'title', 'xml'
22257 ];
22258 Roo.HtmlEditorCore.remove = [
22259     'font'
22260 ];
22261 // attributes..
22262
22263 Roo.HtmlEditorCore.ablack = [
22264     'on'
22265 ];
22266     
22267 Roo.HtmlEditorCore.aclean = [ 
22268     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22269 ];
22270
22271 // protocols..
22272 Roo.HtmlEditorCore.pwhite= [
22273         'http',  'https',  'mailto'
22274 ];
22275
22276 // white listed style attributes.
22277 Roo.HtmlEditorCore.cwhite= [
22278       //  'text-align', /// default is to allow most things..
22279       
22280          
22281 //        'font-size'//??
22282 ];
22283
22284 // black listed style attributes.
22285 Roo.HtmlEditorCore.cblack= [
22286       //  'font-size' -- this can be set by the project 
22287 ];
22288
22289
22290 Roo.HtmlEditorCore.swapCodes   =[ 
22291     [    8211, "--" ], 
22292     [    8212, "--" ], 
22293     [    8216,  "'" ],  
22294     [    8217, "'" ],  
22295     [    8220, '"' ],  
22296     [    8221, '"' ],  
22297     [    8226, "*" ],  
22298     [    8230, "..." ]
22299 ]; 
22300
22301     /*
22302  * - LGPL
22303  *
22304  * HtmlEditor
22305  * 
22306  */
22307
22308 /**
22309  * @class Roo.bootstrap.HtmlEditor
22310  * @extends Roo.bootstrap.TextArea
22311  * Bootstrap HtmlEditor class
22312
22313  * @constructor
22314  * Create a new HtmlEditor
22315  * @param {Object} config The config object
22316  */
22317
22318 Roo.bootstrap.HtmlEditor = function(config){
22319     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22320     if (!this.toolbars) {
22321         this.toolbars = [];
22322     }
22323     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22324     this.addEvents({
22325             /**
22326              * @event initialize
22327              * Fires when the editor is fully initialized (including the iframe)
22328              * @param {HtmlEditor} this
22329              */
22330             initialize: true,
22331             /**
22332              * @event activate
22333              * Fires when the editor is first receives the focus. Any insertion must wait
22334              * until after this event.
22335              * @param {HtmlEditor} this
22336              */
22337             activate: true,
22338              /**
22339              * @event beforesync
22340              * Fires before the textarea is updated with content from the editor iframe. Return false
22341              * to cancel the sync.
22342              * @param {HtmlEditor} this
22343              * @param {String} html
22344              */
22345             beforesync: true,
22346              /**
22347              * @event beforepush
22348              * Fires before the iframe editor is updated with content from the textarea. Return false
22349              * to cancel the push.
22350              * @param {HtmlEditor} this
22351              * @param {String} html
22352              */
22353             beforepush: true,
22354              /**
22355              * @event sync
22356              * Fires when the textarea is updated with content from the editor iframe.
22357              * @param {HtmlEditor} this
22358              * @param {String} html
22359              */
22360             sync: true,
22361              /**
22362              * @event push
22363              * Fires when the iframe editor is updated with content from the textarea.
22364              * @param {HtmlEditor} this
22365              * @param {String} html
22366              */
22367             push: true,
22368              /**
22369              * @event editmodechange
22370              * Fires when the editor switches edit modes
22371              * @param {HtmlEditor} this
22372              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22373              */
22374             editmodechange: true,
22375             /**
22376              * @event editorevent
22377              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22378              * @param {HtmlEditor} this
22379              */
22380             editorevent: true,
22381             /**
22382              * @event firstfocus
22383              * Fires when on first focus - needed by toolbars..
22384              * @param {HtmlEditor} this
22385              */
22386             firstfocus: true,
22387             /**
22388              * @event autosave
22389              * Auto save the htmlEditor value as a file into Events
22390              * @param {HtmlEditor} this
22391              */
22392             autosave: true,
22393             /**
22394              * @event savedpreview
22395              * preview the saved version of htmlEditor
22396              * @param {HtmlEditor} this
22397              */
22398             savedpreview: true
22399         });
22400 };
22401
22402
22403 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22404     
22405     
22406       /**
22407      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22408      */
22409     toolbars : false,
22410    
22411      /**
22412      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22413      *                        Roo.resizable.
22414      */
22415     resizable : false,
22416      /**
22417      * @cfg {Number} height (in pixels)
22418      */   
22419     height: 300,
22420    /**
22421      * @cfg {Number} width (in pixels)
22422      */   
22423     width: false,
22424     
22425     /**
22426      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22427      * 
22428      */
22429     stylesheets: false,
22430     
22431     // id of frame..
22432     frameId: false,
22433     
22434     // private properties
22435     validationEvent : false,
22436     deferHeight: true,
22437     initialized : false,
22438     activated : false,
22439     
22440     onFocus : Roo.emptyFn,
22441     iframePad:3,
22442     hideMode:'offsets',
22443     
22444     
22445     tbContainer : false,
22446     
22447     toolbarContainer :function() {
22448         return this.wrap.select('.x-html-editor-tb',true).first();
22449     },
22450
22451     /**
22452      * Protected method that will not generally be called directly. It
22453      * is called when the editor creates its toolbar. Override this method if you need to
22454      * add custom toolbar buttons.
22455      * @param {HtmlEditor} editor
22456      */
22457     createToolbar : function(){
22458         
22459         Roo.log("create toolbars");
22460         
22461         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22462         this.toolbars[0].render(this.toolbarContainer());
22463         
22464         return;
22465         
22466 //        if (!editor.toolbars || !editor.toolbars.length) {
22467 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22468 //        }
22469 //        
22470 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22471 //            editor.toolbars[i] = Roo.factory(
22472 //                    typeof(editor.toolbars[i]) == 'string' ?
22473 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22474 //                Roo.bootstrap.HtmlEditor);
22475 //            editor.toolbars[i].init(editor);
22476 //        }
22477     },
22478
22479      
22480     // private
22481     onRender : function(ct, position)
22482     {
22483        // Roo.log("Call onRender: " + this.xtype);
22484         var _t = this;
22485         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22486       
22487         this.wrap = this.inputEl().wrap({
22488             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22489         });
22490         
22491         this.editorcore.onRender(ct, position);
22492          
22493         if (this.resizable) {
22494             this.resizeEl = new Roo.Resizable(this.wrap, {
22495                 pinned : true,
22496                 wrap: true,
22497                 dynamic : true,
22498                 minHeight : this.height,
22499                 height: this.height,
22500                 handles : this.resizable,
22501                 width: this.width,
22502                 listeners : {
22503                     resize : function(r, w, h) {
22504                         _t.onResize(w,h); // -something
22505                     }
22506                 }
22507             });
22508             
22509         }
22510         this.createToolbar(this);
22511        
22512         
22513         if(!this.width && this.resizable){
22514             this.setSize(this.wrap.getSize());
22515         }
22516         if (this.resizeEl) {
22517             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22518             // should trigger onReize..
22519         }
22520         
22521     },
22522
22523     // private
22524     onResize : function(w, h)
22525     {
22526         Roo.log('resize: ' +w + ',' + h );
22527         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22528         var ew = false;
22529         var eh = false;
22530         
22531         if(this.inputEl() ){
22532             if(typeof w == 'number'){
22533                 var aw = w - this.wrap.getFrameWidth('lr');
22534                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22535                 ew = aw;
22536             }
22537             if(typeof h == 'number'){
22538                  var tbh = -11;  // fixme it needs to tool bar size!
22539                 for (var i =0; i < this.toolbars.length;i++) {
22540                     // fixme - ask toolbars for heights?
22541                     tbh += this.toolbars[i].el.getHeight();
22542                     //if (this.toolbars[i].footer) {
22543                     //    tbh += this.toolbars[i].footer.el.getHeight();
22544                     //}
22545                 }
22546               
22547                 
22548                 
22549                 
22550                 
22551                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22552                 ah -= 5; // knock a few pixes off for look..
22553                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22554                 var eh = ah;
22555             }
22556         }
22557         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22558         this.editorcore.onResize(ew,eh);
22559         
22560     },
22561
22562     /**
22563      * Toggles the editor between standard and source edit mode.
22564      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22565      */
22566     toggleSourceEdit : function(sourceEditMode)
22567     {
22568         this.editorcore.toggleSourceEdit(sourceEditMode);
22569         
22570         if(this.editorcore.sourceEditMode){
22571             Roo.log('editor - showing textarea');
22572             
22573 //            Roo.log('in');
22574 //            Roo.log(this.syncValue());
22575             this.syncValue();
22576             this.inputEl().removeClass(['hide', 'x-hidden']);
22577             this.inputEl().dom.removeAttribute('tabIndex');
22578             this.inputEl().focus();
22579         }else{
22580             Roo.log('editor - hiding textarea');
22581 //            Roo.log('out')
22582 //            Roo.log(this.pushValue()); 
22583             this.pushValue();
22584             
22585             this.inputEl().addClass(['hide', 'x-hidden']);
22586             this.inputEl().dom.setAttribute('tabIndex', -1);
22587             //this.deferFocus();
22588         }
22589          
22590         if(this.resizable){
22591             this.setSize(this.wrap.getSize());
22592         }
22593         
22594         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22595     },
22596  
22597     // private (for BoxComponent)
22598     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22599
22600     // private (for BoxComponent)
22601     getResizeEl : function(){
22602         return this.wrap;
22603     },
22604
22605     // private (for BoxComponent)
22606     getPositionEl : function(){
22607         return this.wrap;
22608     },
22609
22610     // private
22611     initEvents : function(){
22612         this.originalValue = this.getValue();
22613     },
22614
22615 //    /**
22616 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22617 //     * @method
22618 //     */
22619 //    markInvalid : Roo.emptyFn,
22620 //    /**
22621 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22622 //     * @method
22623 //     */
22624 //    clearInvalid : Roo.emptyFn,
22625
22626     setValue : function(v){
22627         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22628         this.editorcore.pushValue();
22629     },
22630
22631      
22632     // private
22633     deferFocus : function(){
22634         this.focus.defer(10, this);
22635     },
22636
22637     // doc'ed in Field
22638     focus : function(){
22639         this.editorcore.focus();
22640         
22641     },
22642       
22643
22644     // private
22645     onDestroy : function(){
22646         
22647         
22648         
22649         if(this.rendered){
22650             
22651             for (var i =0; i < this.toolbars.length;i++) {
22652                 // fixme - ask toolbars for heights?
22653                 this.toolbars[i].onDestroy();
22654             }
22655             
22656             this.wrap.dom.innerHTML = '';
22657             this.wrap.remove();
22658         }
22659     },
22660
22661     // private
22662     onFirstFocus : function(){
22663         //Roo.log("onFirstFocus");
22664         this.editorcore.onFirstFocus();
22665          for (var i =0; i < this.toolbars.length;i++) {
22666             this.toolbars[i].onFirstFocus();
22667         }
22668         
22669     },
22670     
22671     // private
22672     syncValue : function()
22673     {   
22674         this.editorcore.syncValue();
22675     },
22676     
22677     pushValue : function()
22678     {   
22679         this.editorcore.pushValue();
22680     }
22681      
22682     
22683     // hide stuff that is not compatible
22684     /**
22685      * @event blur
22686      * @hide
22687      */
22688     /**
22689      * @event change
22690      * @hide
22691      */
22692     /**
22693      * @event focus
22694      * @hide
22695      */
22696     /**
22697      * @event specialkey
22698      * @hide
22699      */
22700     /**
22701      * @cfg {String} fieldClass @hide
22702      */
22703     /**
22704      * @cfg {String} focusClass @hide
22705      */
22706     /**
22707      * @cfg {String} autoCreate @hide
22708      */
22709     /**
22710      * @cfg {String} inputType @hide
22711      */
22712     /**
22713      * @cfg {String} invalidClass @hide
22714      */
22715     /**
22716      * @cfg {String} invalidText @hide
22717      */
22718     /**
22719      * @cfg {String} msgFx @hide
22720      */
22721     /**
22722      * @cfg {String} validateOnBlur @hide
22723      */
22724 });
22725  
22726     
22727    
22728    
22729    
22730       
22731 Roo.namespace('Roo.bootstrap.htmleditor');
22732 /**
22733  * @class Roo.bootstrap.HtmlEditorToolbar1
22734  * Basic Toolbar
22735  * 
22736  * Usage:
22737  *
22738  new Roo.bootstrap.HtmlEditor({
22739     ....
22740     toolbars : [
22741         new Roo.bootstrap.HtmlEditorToolbar1({
22742             disable : { fonts: 1 , format: 1, ..., ... , ...],
22743             btns : [ .... ]
22744         })
22745     }
22746      
22747  * 
22748  * @cfg {Object} disable List of elements to disable..
22749  * @cfg {Array} btns List of additional buttons.
22750  * 
22751  * 
22752  * NEEDS Extra CSS? 
22753  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22754  */
22755  
22756 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22757 {
22758     
22759     Roo.apply(this, config);
22760     
22761     // default disabled, based on 'good practice'..
22762     this.disable = this.disable || {};
22763     Roo.applyIf(this.disable, {
22764         fontSize : true,
22765         colors : true,
22766         specialElements : true
22767     });
22768     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22769     
22770     this.editor = config.editor;
22771     this.editorcore = config.editor.editorcore;
22772     
22773     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22774     
22775     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22776     // dont call parent... till later.
22777 }
22778 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22779      
22780     bar : true,
22781     
22782     editor : false,
22783     editorcore : false,
22784     
22785     
22786     formats : [
22787         "p" ,  
22788         "h1","h2","h3","h4","h5","h6", 
22789         "pre", "code", 
22790         "abbr", "acronym", "address", "cite", "samp", "var",
22791         'div','span'
22792     ],
22793     
22794     onRender : function(ct, position)
22795     {
22796        // Roo.log("Call onRender: " + this.xtype);
22797         
22798        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22799        Roo.log(this.el);
22800        this.el.dom.style.marginBottom = '0';
22801        var _this = this;
22802        var editorcore = this.editorcore;
22803        var editor= this.editor;
22804        
22805        var children = [];
22806        var btn = function(id,cmd , toggle, handler){
22807        
22808             var  event = toggle ? 'toggle' : 'click';
22809        
22810             var a = {
22811                 size : 'sm',
22812                 xtype: 'Button',
22813                 xns: Roo.bootstrap,
22814                 glyphicon : id,
22815                 cmd : id || cmd,
22816                 enableToggle:toggle !== false,
22817                 //html : 'submit'
22818                 pressed : toggle ? false : null,
22819                 listeners : {}
22820             };
22821             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22822                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22823             };
22824             children.push(a);
22825             return a;
22826        }
22827         
22828         var style = {
22829                 xtype: 'Button',
22830                 size : 'sm',
22831                 xns: Roo.bootstrap,
22832                 glyphicon : 'font',
22833                 //html : 'submit'
22834                 menu : {
22835                     xtype: 'Menu',
22836                     xns: Roo.bootstrap,
22837                     items:  []
22838                 }
22839         };
22840         Roo.each(this.formats, function(f) {
22841             style.menu.items.push({
22842                 xtype :'MenuItem',
22843                 xns: Roo.bootstrap,
22844                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22845                 tagname : f,
22846                 listeners : {
22847                     click : function()
22848                     {
22849                         editorcore.insertTag(this.tagname);
22850                         editor.focus();
22851                     }
22852                 }
22853                 
22854             });
22855         });
22856          children.push(style);   
22857             
22858             
22859         btn('bold',false,true);
22860         btn('italic',false,true);
22861         btn('align-left', 'justifyleft',true);
22862         btn('align-center', 'justifycenter',true);
22863         btn('align-right' , 'justifyright',true);
22864         btn('link', false, false, function(btn) {
22865             //Roo.log("create link?");
22866             var url = prompt(this.createLinkText, this.defaultLinkValue);
22867             if(url && url != 'http:/'+'/'){
22868                 this.editorcore.relayCmd('createlink', url);
22869             }
22870         }),
22871         btn('list','insertunorderedlist',true);
22872         btn('pencil', false,true, function(btn){
22873                 Roo.log(this);
22874                 
22875                 this.toggleSourceEdit(btn.pressed);
22876         });
22877         /*
22878         var cog = {
22879                 xtype: 'Button',
22880                 size : 'sm',
22881                 xns: Roo.bootstrap,
22882                 glyphicon : 'cog',
22883                 //html : 'submit'
22884                 menu : {
22885                     xtype: 'Menu',
22886                     xns: Roo.bootstrap,
22887                     items:  []
22888                 }
22889         };
22890         
22891         cog.menu.items.push({
22892             xtype :'MenuItem',
22893             xns: Roo.bootstrap,
22894             html : Clean styles,
22895             tagname : f,
22896             listeners : {
22897                 click : function()
22898                 {
22899                     editorcore.insertTag(this.tagname);
22900                     editor.focus();
22901                 }
22902             }
22903             
22904         });
22905        */
22906         
22907          
22908        this.xtype = 'NavSimplebar';
22909         
22910         for(var i=0;i< children.length;i++) {
22911             
22912             this.buttons.add(this.addxtypeChild(children[i]));
22913             
22914         }
22915         
22916         editor.on('editorevent', this.updateToolbar, this);
22917     },
22918     onBtnClick : function(id)
22919     {
22920        this.editorcore.relayCmd(id);
22921        this.editorcore.focus();
22922     },
22923     
22924     /**
22925      * Protected method that will not generally be called directly. It triggers
22926      * a toolbar update by reading the markup state of the current selection in the editor.
22927      */
22928     updateToolbar: function(){
22929
22930         if(!this.editorcore.activated){
22931             this.editor.onFirstFocus(); // is this neeed?
22932             return;
22933         }
22934
22935         var btns = this.buttons; 
22936         var doc = this.editorcore.doc;
22937         btns.get('bold').setActive(doc.queryCommandState('bold'));
22938         btns.get('italic').setActive(doc.queryCommandState('italic'));
22939         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22940         
22941         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22942         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22943         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22944         
22945         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22946         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22947          /*
22948         
22949         var ans = this.editorcore.getAllAncestors();
22950         if (this.formatCombo) {
22951             
22952             
22953             var store = this.formatCombo.store;
22954             this.formatCombo.setValue("");
22955             for (var i =0; i < ans.length;i++) {
22956                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22957                     // select it..
22958                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22959                     break;
22960                 }
22961             }
22962         }
22963         
22964         
22965         
22966         // hides menus... - so this cant be on a menu...
22967         Roo.bootstrap.MenuMgr.hideAll();
22968         */
22969         Roo.bootstrap.MenuMgr.hideAll();
22970         //this.editorsyncValue();
22971     },
22972     onFirstFocus: function() {
22973         this.buttons.each(function(item){
22974            item.enable();
22975         });
22976     },
22977     toggleSourceEdit : function(sourceEditMode){
22978         
22979           
22980         if(sourceEditMode){
22981             Roo.log("disabling buttons");
22982            this.buttons.each( function(item){
22983                 if(item.cmd != 'pencil'){
22984                     item.disable();
22985                 }
22986             });
22987           
22988         }else{
22989             Roo.log("enabling buttons");
22990             if(this.editorcore.initialized){
22991                 this.buttons.each( function(item){
22992                     item.enable();
22993                 });
22994             }
22995             
22996         }
22997         Roo.log("calling toggole on editor");
22998         // tell the editor that it's been pressed..
22999         this.editor.toggleSourceEdit(sourceEditMode);
23000        
23001     }
23002 });
23003
23004
23005
23006
23007
23008 /**
23009  * @class Roo.bootstrap.Table.AbstractSelectionModel
23010  * @extends Roo.util.Observable
23011  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23012  * implemented by descendant classes.  This class should not be directly instantiated.
23013  * @constructor
23014  */
23015 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23016     this.locked = false;
23017     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23018 };
23019
23020
23021 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23022     /** @ignore Called by the grid automatically. Do not call directly. */
23023     init : function(grid){
23024         this.grid = grid;
23025         this.initEvents();
23026     },
23027
23028     /**
23029      * Locks the selections.
23030      */
23031     lock : function(){
23032         this.locked = true;
23033     },
23034
23035     /**
23036      * Unlocks the selections.
23037      */
23038     unlock : function(){
23039         this.locked = false;
23040     },
23041
23042     /**
23043      * Returns true if the selections are locked.
23044      * @return {Boolean}
23045      */
23046     isLocked : function(){
23047         return this.locked;
23048     }
23049 });
23050 /**
23051  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23052  * @class Roo.bootstrap.Table.RowSelectionModel
23053  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23054  * It supports multiple selections and keyboard selection/navigation. 
23055  * @constructor
23056  * @param {Object} config
23057  */
23058
23059 Roo.bootstrap.Table.RowSelectionModel = function(config){
23060     Roo.apply(this, config);
23061     this.selections = new Roo.util.MixedCollection(false, function(o){
23062         return o.id;
23063     });
23064
23065     this.last = false;
23066     this.lastActive = false;
23067
23068     this.addEvents({
23069         /**
23070              * @event selectionchange
23071              * Fires when the selection changes
23072              * @param {SelectionModel} this
23073              */
23074             "selectionchange" : true,
23075         /**
23076              * @event afterselectionchange
23077              * Fires after the selection changes (eg. by key press or clicking)
23078              * @param {SelectionModel} this
23079              */
23080             "afterselectionchange" : true,
23081         /**
23082              * @event beforerowselect
23083              * Fires when a row is selected being selected, return false to cancel.
23084              * @param {SelectionModel} this
23085              * @param {Number} rowIndex The selected index
23086              * @param {Boolean} keepExisting False if other selections will be cleared
23087              */
23088             "beforerowselect" : true,
23089         /**
23090              * @event rowselect
23091              * Fires when a row is selected.
23092              * @param {SelectionModel} this
23093              * @param {Number} rowIndex The selected index
23094              * @param {Roo.data.Record} r The record
23095              */
23096             "rowselect" : true,
23097         /**
23098              * @event rowdeselect
23099              * Fires when a row is deselected.
23100              * @param {SelectionModel} this
23101              * @param {Number} rowIndex The selected index
23102              */
23103         "rowdeselect" : true
23104     });
23105     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23106     this.locked = false;
23107  };
23108
23109 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23110     /**
23111      * @cfg {Boolean} singleSelect
23112      * True to allow selection of only one row at a time (defaults to false)
23113      */
23114     singleSelect : false,
23115
23116     // private
23117     initEvents : function()
23118     {
23119
23120         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23121         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23122         //}else{ // allow click to work like normal
23123          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23124         //}
23125         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23126         this.grid.on("rowclick", this.handleMouseDown, this);
23127         
23128         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23129             "up" : function(e){
23130                 if(!e.shiftKey){
23131                     this.selectPrevious(e.shiftKey);
23132                 }else if(this.last !== false && this.lastActive !== false){
23133                     var last = this.last;
23134                     this.selectRange(this.last,  this.lastActive-1);
23135                     this.grid.getView().focusRow(this.lastActive);
23136                     if(last !== false){
23137                         this.last = last;
23138                     }
23139                 }else{
23140                     this.selectFirstRow();
23141                 }
23142                 this.fireEvent("afterselectionchange", this);
23143             },
23144             "down" : function(e){
23145                 if(!e.shiftKey){
23146                     this.selectNext(e.shiftKey);
23147                 }else if(this.last !== false && this.lastActive !== false){
23148                     var last = this.last;
23149                     this.selectRange(this.last,  this.lastActive+1);
23150                     this.grid.getView().focusRow(this.lastActive);
23151                     if(last !== false){
23152                         this.last = last;
23153                     }
23154                 }else{
23155                     this.selectFirstRow();
23156                 }
23157                 this.fireEvent("afterselectionchange", this);
23158             },
23159             scope: this
23160         });
23161         this.grid.store.on('load', function(){
23162             this.selections.clear();
23163         },this);
23164         /*
23165         var view = this.grid.view;
23166         view.on("refresh", this.onRefresh, this);
23167         view.on("rowupdated", this.onRowUpdated, this);
23168         view.on("rowremoved", this.onRemove, this);
23169         */
23170     },
23171
23172     // private
23173     onRefresh : function()
23174     {
23175         var ds = this.grid.store, i, v = this.grid.view;
23176         var s = this.selections;
23177         s.each(function(r){
23178             if((i = ds.indexOfId(r.id)) != -1){
23179                 v.onRowSelect(i);
23180             }else{
23181                 s.remove(r);
23182             }
23183         });
23184     },
23185
23186     // private
23187     onRemove : function(v, index, r){
23188         this.selections.remove(r);
23189     },
23190
23191     // private
23192     onRowUpdated : function(v, index, r){
23193         if(this.isSelected(r)){
23194             v.onRowSelect(index);
23195         }
23196     },
23197
23198     /**
23199      * Select records.
23200      * @param {Array} records The records to select
23201      * @param {Boolean} keepExisting (optional) True to keep existing selections
23202      */
23203     selectRecords : function(records, keepExisting)
23204     {
23205         if(!keepExisting){
23206             this.clearSelections();
23207         }
23208             var ds = this.grid.store;
23209         for(var i = 0, len = records.length; i < len; i++){
23210             this.selectRow(ds.indexOf(records[i]), true);
23211         }
23212     },
23213
23214     /**
23215      * Gets the number of selected rows.
23216      * @return {Number}
23217      */
23218     getCount : function(){
23219         return this.selections.length;
23220     },
23221
23222     /**
23223      * Selects the first row in the grid.
23224      */
23225     selectFirstRow : function(){
23226         this.selectRow(0);
23227     },
23228
23229     /**
23230      * Select the last row.
23231      * @param {Boolean} keepExisting (optional) True to keep existing selections
23232      */
23233     selectLastRow : function(keepExisting){
23234         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23235         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23236     },
23237
23238     /**
23239      * Selects the row immediately following the last selected row.
23240      * @param {Boolean} keepExisting (optional) True to keep existing selections
23241      */
23242     selectNext : function(keepExisting)
23243     {
23244             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23245             this.selectRow(this.last+1, keepExisting);
23246             this.grid.getView().focusRow(this.last);
23247         }
23248     },
23249
23250     /**
23251      * Selects the row that precedes the last selected row.
23252      * @param {Boolean} keepExisting (optional) True to keep existing selections
23253      */
23254     selectPrevious : function(keepExisting){
23255         if(this.last){
23256             this.selectRow(this.last-1, keepExisting);
23257             this.grid.getView().focusRow(this.last);
23258         }
23259     },
23260
23261     /**
23262      * Returns the selected records
23263      * @return {Array} Array of selected records
23264      */
23265     getSelections : function(){
23266         return [].concat(this.selections.items);
23267     },
23268
23269     /**
23270      * Returns the first selected record.
23271      * @return {Record}
23272      */
23273     getSelected : function(){
23274         return this.selections.itemAt(0);
23275     },
23276
23277
23278     /**
23279      * Clears all selections.
23280      */
23281     clearSelections : function(fast)
23282     {
23283         if(this.locked) {
23284             return;
23285         }
23286         if(fast !== true){
23287                 var ds = this.grid.store;
23288             var s = this.selections;
23289             s.each(function(r){
23290                 this.deselectRow(ds.indexOfId(r.id));
23291             }, this);
23292             s.clear();
23293         }else{
23294             this.selections.clear();
23295         }
23296         this.last = false;
23297     },
23298
23299
23300     /**
23301      * Selects all rows.
23302      */
23303     selectAll : function(){
23304         if(this.locked) {
23305             return;
23306         }
23307         this.selections.clear();
23308         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23309             this.selectRow(i, true);
23310         }
23311     },
23312
23313     /**
23314      * Returns True if there is a selection.
23315      * @return {Boolean}
23316      */
23317     hasSelection : function(){
23318         return this.selections.length > 0;
23319     },
23320
23321     /**
23322      * Returns True if the specified row is selected.
23323      * @param {Number/Record} record The record or index of the record to check
23324      * @return {Boolean}
23325      */
23326     isSelected : function(index){
23327             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23328         return (r && this.selections.key(r.id) ? true : false);
23329     },
23330
23331     /**
23332      * Returns True if the specified record id is selected.
23333      * @param {String} id The id of record to check
23334      * @return {Boolean}
23335      */
23336     isIdSelected : function(id){
23337         return (this.selections.key(id) ? true : false);
23338     },
23339
23340
23341     // private
23342     handleMouseDBClick : function(e, t){
23343         
23344     },
23345     // private
23346     handleMouseDown : function(e, t)
23347     {
23348             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23349         if(this.isLocked() || rowIndex < 0 ){
23350             return;
23351         };
23352         if(e.shiftKey && this.last !== false){
23353             var last = this.last;
23354             this.selectRange(last, rowIndex, e.ctrlKey);
23355             this.last = last; // reset the last
23356             t.focus();
23357     
23358         }else{
23359             var isSelected = this.isSelected(rowIndex);
23360             //Roo.log("select row:" + rowIndex);
23361             if(isSelected){
23362                 this.deselectRow(rowIndex);
23363             } else {
23364                         this.selectRow(rowIndex, true);
23365             }
23366     
23367             /*
23368                 if(e.button !== 0 && isSelected){
23369                 alert('rowIndex 2: ' + rowIndex);
23370                     view.focusRow(rowIndex);
23371                 }else if(e.ctrlKey && isSelected){
23372                     this.deselectRow(rowIndex);
23373                 }else if(!isSelected){
23374                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23375                     view.focusRow(rowIndex);
23376                 }
23377             */
23378         }
23379         this.fireEvent("afterselectionchange", this);
23380     },
23381     // private
23382     handleDragableRowClick :  function(grid, rowIndex, e) 
23383     {
23384         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23385             this.selectRow(rowIndex, false);
23386             grid.view.focusRow(rowIndex);
23387              this.fireEvent("afterselectionchange", this);
23388         }
23389     },
23390     
23391     /**
23392      * Selects multiple rows.
23393      * @param {Array} rows Array of the indexes of the row to select
23394      * @param {Boolean} keepExisting (optional) True to keep existing selections
23395      */
23396     selectRows : function(rows, keepExisting){
23397         if(!keepExisting){
23398             this.clearSelections();
23399         }
23400         for(var i = 0, len = rows.length; i < len; i++){
23401             this.selectRow(rows[i], true);
23402         }
23403     },
23404
23405     /**
23406      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23407      * @param {Number} startRow The index of the first row in the range
23408      * @param {Number} endRow The index of the last row in the range
23409      * @param {Boolean} keepExisting (optional) True to retain existing selections
23410      */
23411     selectRange : function(startRow, endRow, keepExisting){
23412         if(this.locked) {
23413             return;
23414         }
23415         if(!keepExisting){
23416             this.clearSelections();
23417         }
23418         if(startRow <= endRow){
23419             for(var i = startRow; i <= endRow; i++){
23420                 this.selectRow(i, true);
23421             }
23422         }else{
23423             for(var i = startRow; i >= endRow; i--){
23424                 this.selectRow(i, true);
23425             }
23426         }
23427     },
23428
23429     /**
23430      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23431      * @param {Number} startRow The index of the first row in the range
23432      * @param {Number} endRow The index of the last row in the range
23433      */
23434     deselectRange : function(startRow, endRow, preventViewNotify){
23435         if(this.locked) {
23436             return;
23437         }
23438         for(var i = startRow; i <= endRow; i++){
23439             this.deselectRow(i, preventViewNotify);
23440         }
23441     },
23442
23443     /**
23444      * Selects a row.
23445      * @param {Number} row The index of the row to select
23446      * @param {Boolean} keepExisting (optional) True to keep existing selections
23447      */
23448     selectRow : function(index, keepExisting, preventViewNotify)
23449     {
23450             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23451             return;
23452         }
23453         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23454             if(!keepExisting || this.singleSelect){
23455                 this.clearSelections();
23456             }
23457             
23458             var r = this.grid.store.getAt(index);
23459             //console.log('selectRow - record id :' + r.id);
23460             
23461             this.selections.add(r);
23462             this.last = this.lastActive = index;
23463             if(!preventViewNotify){
23464                 var proxy = new Roo.Element(
23465                                 this.grid.getRowDom(index)
23466                 );
23467                 proxy.addClass('bg-info info');
23468             }
23469             this.fireEvent("rowselect", this, index, r);
23470             this.fireEvent("selectionchange", this);
23471         }
23472     },
23473
23474     /**
23475      * Deselects a row.
23476      * @param {Number} row The index of the row to deselect
23477      */
23478     deselectRow : function(index, preventViewNotify)
23479     {
23480         if(this.locked) {
23481             return;
23482         }
23483         if(this.last == index){
23484             this.last = false;
23485         }
23486         if(this.lastActive == index){
23487             this.lastActive = false;
23488         }
23489         
23490         var r = this.grid.store.getAt(index);
23491         if (!r) {
23492             return;
23493         }
23494         
23495         this.selections.remove(r);
23496         //.console.log('deselectRow - record id :' + r.id);
23497         if(!preventViewNotify){
23498         
23499             var proxy = new Roo.Element(
23500                 this.grid.getRowDom(index)
23501             );
23502             proxy.removeClass('bg-info info');
23503         }
23504         this.fireEvent("rowdeselect", this, index);
23505         this.fireEvent("selectionchange", this);
23506     },
23507
23508     // private
23509     restoreLast : function(){
23510         if(this._last){
23511             this.last = this._last;
23512         }
23513     },
23514
23515     // private
23516     acceptsNav : function(row, col, cm){
23517         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23518     },
23519
23520     // private
23521     onEditorKey : function(field, e){
23522         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23523         if(k == e.TAB){
23524             e.stopEvent();
23525             ed.completeEdit();
23526             if(e.shiftKey){
23527                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23528             }else{
23529                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23530             }
23531         }else if(k == e.ENTER && !e.ctrlKey){
23532             e.stopEvent();
23533             ed.completeEdit();
23534             if(e.shiftKey){
23535                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23536             }else{
23537                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23538             }
23539         }else if(k == e.ESC){
23540             ed.cancelEdit();
23541         }
23542         if(newCell){
23543             g.startEditing(newCell[0], newCell[1]);
23544         }
23545     }
23546 });
23547 /*
23548  * Based on:
23549  * Ext JS Library 1.1.1
23550  * Copyright(c) 2006-2007, Ext JS, LLC.
23551  *
23552  * Originally Released Under LGPL - original licence link has changed is not relivant.
23553  *
23554  * Fork - LGPL
23555  * <script type="text/javascript">
23556  */
23557  
23558 /**
23559  * @class Roo.bootstrap.PagingToolbar
23560  * @extends Roo.bootstrap.NavSimplebar
23561  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23562  * @constructor
23563  * Create a new PagingToolbar
23564  * @param {Object} config The config object
23565  * @param {Roo.data.Store} store
23566  */
23567 Roo.bootstrap.PagingToolbar = function(config)
23568 {
23569     // old args format still supported... - xtype is prefered..
23570         // created from xtype...
23571     
23572     this.ds = config.dataSource;
23573     
23574     if (config.store && !this.ds) {
23575         this.store= Roo.factory(config.store, Roo.data);
23576         this.ds = this.store;
23577         this.ds.xmodule = this.xmodule || false;
23578     }
23579     
23580     this.toolbarItems = [];
23581     if (config.items) {
23582         this.toolbarItems = config.items;
23583     }
23584     
23585     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23586     
23587     this.cursor = 0;
23588     
23589     if (this.ds) { 
23590         this.bind(this.ds);
23591     }
23592     
23593     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23594     
23595 };
23596
23597 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23598     /**
23599      * @cfg {Roo.data.Store} dataSource
23600      * The underlying data store providing the paged data
23601      */
23602     /**
23603      * @cfg {String/HTMLElement/Element} container
23604      * container The id or element that will contain the toolbar
23605      */
23606     /**
23607      * @cfg {Boolean} displayInfo
23608      * True to display the displayMsg (defaults to false)
23609      */
23610     /**
23611      * @cfg {Number} pageSize
23612      * The number of records to display per page (defaults to 20)
23613      */
23614     pageSize: 20,
23615     /**
23616      * @cfg {String} displayMsg
23617      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23618      */
23619     displayMsg : 'Displaying {0} - {1} of {2}',
23620     /**
23621      * @cfg {String} emptyMsg
23622      * The message to display when no records are found (defaults to "No data to display")
23623      */
23624     emptyMsg : 'No data to display',
23625     /**
23626      * Customizable piece of the default paging text (defaults to "Page")
23627      * @type String
23628      */
23629     beforePageText : "Page",
23630     /**
23631      * Customizable piece of the default paging text (defaults to "of %0")
23632      * @type String
23633      */
23634     afterPageText : "of {0}",
23635     /**
23636      * Customizable piece of the default paging text (defaults to "First Page")
23637      * @type String
23638      */
23639     firstText : "First Page",
23640     /**
23641      * Customizable piece of the default paging text (defaults to "Previous Page")
23642      * @type String
23643      */
23644     prevText : "Previous Page",
23645     /**
23646      * Customizable piece of the default paging text (defaults to "Next Page")
23647      * @type String
23648      */
23649     nextText : "Next Page",
23650     /**
23651      * Customizable piece of the default paging text (defaults to "Last Page")
23652      * @type String
23653      */
23654     lastText : "Last Page",
23655     /**
23656      * Customizable piece of the default paging text (defaults to "Refresh")
23657      * @type String
23658      */
23659     refreshText : "Refresh",
23660
23661     buttons : false,
23662     // private
23663     onRender : function(ct, position) 
23664     {
23665         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23666         this.navgroup.parentId = this.id;
23667         this.navgroup.onRender(this.el, null);
23668         // add the buttons to the navgroup
23669         
23670         if(this.displayInfo){
23671             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23672             this.displayEl = this.el.select('.x-paging-info', true).first();
23673 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23674 //            this.displayEl = navel.el.select('span',true).first();
23675         }
23676         
23677         var _this = this;
23678         
23679         if(this.buttons){
23680             Roo.each(_this.buttons, function(e){ // this might need to use render????
23681                Roo.factory(e).onRender(_this.el, null);
23682             });
23683         }
23684             
23685         Roo.each(_this.toolbarItems, function(e) {
23686             _this.navgroup.addItem(e);
23687         });
23688         
23689         
23690         this.first = this.navgroup.addItem({
23691             tooltip: this.firstText,
23692             cls: "prev",
23693             icon : 'fa fa-backward',
23694             disabled: true,
23695             preventDefault: true,
23696             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23697         });
23698         
23699         this.prev =  this.navgroup.addItem({
23700             tooltip: this.prevText,
23701             cls: "prev",
23702             icon : 'fa fa-step-backward',
23703             disabled: true,
23704             preventDefault: true,
23705             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23706         });
23707     //this.addSeparator();
23708         
23709         
23710         var field = this.navgroup.addItem( {
23711             tagtype : 'span',
23712             cls : 'x-paging-position',
23713             
23714             html : this.beforePageText  +
23715                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23716                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23717          } ); //?? escaped?
23718         
23719         this.field = field.el.select('input', true).first();
23720         this.field.on("keydown", this.onPagingKeydown, this);
23721         this.field.on("focus", function(){this.dom.select();});
23722     
23723     
23724         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23725         //this.field.setHeight(18);
23726         //this.addSeparator();
23727         this.next = this.navgroup.addItem({
23728             tooltip: this.nextText,
23729             cls: "next",
23730             html : ' <i class="fa fa-step-forward">',
23731             disabled: true,
23732             preventDefault: true,
23733             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23734         });
23735         this.last = this.navgroup.addItem({
23736             tooltip: this.lastText,
23737             icon : 'fa fa-forward',
23738             cls: "next",
23739             disabled: true,
23740             preventDefault: true,
23741             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23742         });
23743     //this.addSeparator();
23744         this.loading = this.navgroup.addItem({
23745             tooltip: this.refreshText,
23746             icon: 'fa fa-refresh',
23747             preventDefault: true,
23748             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23749         });
23750         
23751     },
23752
23753     // private
23754     updateInfo : function(){
23755         if(this.displayEl){
23756             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23757             var msg = count == 0 ?
23758                 this.emptyMsg :
23759                 String.format(
23760                     this.displayMsg,
23761                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23762                 );
23763             this.displayEl.update(msg);
23764         }
23765     },
23766
23767     // private
23768     onLoad : function(ds, r, o){
23769        this.cursor = o.params ? o.params.start : 0;
23770        var d = this.getPageData(),
23771             ap = d.activePage,
23772             ps = d.pages;
23773         
23774        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23775        this.field.dom.value = ap;
23776        this.first.setDisabled(ap == 1);
23777        this.prev.setDisabled(ap == 1);
23778        this.next.setDisabled(ap == ps);
23779        this.last.setDisabled(ap == ps);
23780        this.loading.enable();
23781        this.updateInfo();
23782     },
23783
23784     // private
23785     getPageData : function(){
23786         var total = this.ds.getTotalCount();
23787         return {
23788             total : total,
23789             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23790             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23791         };
23792     },
23793
23794     // private
23795     onLoadError : function(){
23796         this.loading.enable();
23797     },
23798
23799     // private
23800     onPagingKeydown : function(e){
23801         var k = e.getKey();
23802         var d = this.getPageData();
23803         if(k == e.RETURN){
23804             var v = this.field.dom.value, pageNum;
23805             if(!v || isNaN(pageNum = parseInt(v, 10))){
23806                 this.field.dom.value = d.activePage;
23807                 return;
23808             }
23809             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23810             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23811             e.stopEvent();
23812         }
23813         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))
23814         {
23815           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23816           this.field.dom.value = pageNum;
23817           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23818           e.stopEvent();
23819         }
23820         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23821         {
23822           var v = this.field.dom.value, pageNum; 
23823           var increment = (e.shiftKey) ? 10 : 1;
23824           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23825                 increment *= -1;
23826           }
23827           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23828             this.field.dom.value = d.activePage;
23829             return;
23830           }
23831           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23832           {
23833             this.field.dom.value = parseInt(v, 10) + increment;
23834             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23835             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23836           }
23837           e.stopEvent();
23838         }
23839     },
23840
23841     // private
23842     beforeLoad : function(){
23843         if(this.loading){
23844             this.loading.disable();
23845         }
23846     },
23847
23848     // private
23849     onClick : function(which){
23850         
23851         var ds = this.ds;
23852         if (!ds) {
23853             return;
23854         }
23855         
23856         switch(which){
23857             case "first":
23858                 ds.load({params:{start: 0, limit: this.pageSize}});
23859             break;
23860             case "prev":
23861                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23862             break;
23863             case "next":
23864                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23865             break;
23866             case "last":
23867                 var total = ds.getTotalCount();
23868                 var extra = total % this.pageSize;
23869                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23870                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23871             break;
23872             case "refresh":
23873                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23874             break;
23875         }
23876     },
23877
23878     /**
23879      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23880      * @param {Roo.data.Store} store The data store to unbind
23881      */
23882     unbind : function(ds){
23883         ds.un("beforeload", this.beforeLoad, this);
23884         ds.un("load", this.onLoad, this);
23885         ds.un("loadexception", this.onLoadError, this);
23886         ds.un("remove", this.updateInfo, this);
23887         ds.un("add", this.updateInfo, this);
23888         this.ds = undefined;
23889     },
23890
23891     /**
23892      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23893      * @param {Roo.data.Store} store The data store to bind
23894      */
23895     bind : function(ds){
23896         ds.on("beforeload", this.beforeLoad, this);
23897         ds.on("load", this.onLoad, this);
23898         ds.on("loadexception", this.onLoadError, this);
23899         ds.on("remove", this.updateInfo, this);
23900         ds.on("add", this.updateInfo, this);
23901         this.ds = ds;
23902     }
23903 });/*
23904  * - LGPL
23905  *
23906  * element
23907  * 
23908  */
23909
23910 /**
23911  * @class Roo.bootstrap.MessageBar
23912  * @extends Roo.bootstrap.Component
23913  * Bootstrap MessageBar class
23914  * @cfg {String} html contents of the MessageBar
23915  * @cfg {String} weight (info | success | warning | danger) default info
23916  * @cfg {String} beforeClass insert the bar before the given class
23917  * @cfg {Boolean} closable (true | false) default false
23918  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23919  * 
23920  * @constructor
23921  * Create a new Element
23922  * @param {Object} config The config object
23923  */
23924
23925 Roo.bootstrap.MessageBar = function(config){
23926     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23927 };
23928
23929 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23930     
23931     html: '',
23932     weight: 'info',
23933     closable: false,
23934     fixed: false,
23935     beforeClass: 'bootstrap-sticky-wrap',
23936     
23937     getAutoCreate : function(){
23938         
23939         var cfg = {
23940             tag: 'div',
23941             cls: 'alert alert-dismissable alert-' + this.weight,
23942             cn: [
23943                 {
23944                     tag: 'span',
23945                     cls: 'message',
23946                     html: this.html || ''
23947                 }
23948             ]
23949         };
23950         
23951         if(this.fixed){
23952             cfg.cls += ' alert-messages-fixed';
23953         }
23954         
23955         if(this.closable){
23956             cfg.cn.push({
23957                 tag: 'button',
23958                 cls: 'close',
23959                 html: 'x'
23960             });
23961         }
23962         
23963         return cfg;
23964     },
23965     
23966     onRender : function(ct, position)
23967     {
23968         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23969         
23970         if(!this.el){
23971             var cfg = Roo.apply({},  this.getAutoCreate());
23972             cfg.id = Roo.id();
23973             
23974             if (this.cls) {
23975                 cfg.cls += ' ' + this.cls;
23976             }
23977             if (this.style) {
23978                 cfg.style = this.style;
23979             }
23980             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23981             
23982             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23983         }
23984         
23985         this.el.select('>button.close').on('click', this.hide, this);
23986         
23987     },
23988     
23989     show : function()
23990     {
23991         if (!this.rendered) {
23992             this.render();
23993         }
23994         
23995         this.el.show();
23996         
23997         this.fireEvent('show', this);
23998         
23999     },
24000     
24001     hide : function()
24002     {
24003         if (!this.rendered) {
24004             this.render();
24005         }
24006         
24007         this.el.hide();
24008         
24009         this.fireEvent('hide', this);
24010     },
24011     
24012     update : function()
24013     {
24014 //        var e = this.el.dom.firstChild;
24015 //        
24016 //        if(this.closable){
24017 //            e = e.nextSibling;
24018 //        }
24019 //        
24020 //        e.data = this.html || '';
24021
24022         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24023     }
24024    
24025 });
24026
24027  
24028
24029      /*
24030  * - LGPL
24031  *
24032  * Graph
24033  * 
24034  */
24035
24036
24037 /**
24038  * @class Roo.bootstrap.Graph
24039  * @extends Roo.bootstrap.Component
24040  * Bootstrap Graph class
24041 > Prameters
24042  -sm {number} sm 4
24043  -md {number} md 5
24044  @cfg {String} graphtype  bar | vbar | pie
24045  @cfg {number} g_x coodinator | centre x (pie)
24046  @cfg {number} g_y coodinator | centre y (pie)
24047  @cfg {number} g_r radius (pie)
24048  @cfg {number} g_height height of the chart (respected by all elements in the set)
24049  @cfg {number} g_width width of the chart (respected by all elements in the set)
24050  @cfg {Object} title The title of the chart
24051     
24052  -{Array}  values
24053  -opts (object) options for the chart 
24054      o {
24055      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24056      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24057      o vgutter (number)
24058      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.
24059      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24060      o to
24061      o stretch (boolean)
24062      o }
24063  -opts (object) options for the pie
24064      o{
24065      o cut
24066      o startAngle (number)
24067      o endAngle (number)
24068      } 
24069  *
24070  * @constructor
24071  * Create a new Input
24072  * @param {Object} config The config object
24073  */
24074
24075 Roo.bootstrap.Graph = function(config){
24076     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24077     
24078     this.addEvents({
24079         // img events
24080         /**
24081          * @event click
24082          * The img click event for the img.
24083          * @param {Roo.EventObject} e
24084          */
24085         "click" : true
24086     });
24087 };
24088
24089 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24090     
24091     sm: 4,
24092     md: 5,
24093     graphtype: 'bar',
24094     g_height: 250,
24095     g_width: 400,
24096     g_x: 50,
24097     g_y: 50,
24098     g_r: 30,
24099     opts:{
24100         //g_colors: this.colors,
24101         g_type: 'soft',
24102         g_gutter: '20%'
24103
24104     },
24105     title : false,
24106
24107     getAutoCreate : function(){
24108         
24109         var cfg = {
24110             tag: 'div',
24111             html : null
24112         };
24113         
24114         
24115         return  cfg;
24116     },
24117
24118     onRender : function(ct,position){
24119         
24120         
24121         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24122         
24123         if (typeof(Raphael) == 'undefined') {
24124             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24125             return;
24126         }
24127         
24128         this.raphael = Raphael(this.el.dom);
24129         
24130                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24131                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24132                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24133                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24134                 /*
24135                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24136                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24137                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24138                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24139                 
24140                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24141                 r.barchart(330, 10, 300, 220, data1);
24142                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24143                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24144                 */
24145                 
24146                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24147                 // r.barchart(30, 30, 560, 250,  xdata, {
24148                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24149                 //     axis : "0 0 1 1",
24150                 //     axisxlabels :  xdata
24151                 //     //yvalues : cols,
24152                    
24153                 // });
24154 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24155 //        
24156 //        this.load(null,xdata,{
24157 //                axis : "0 0 1 1",
24158 //                axisxlabels :  xdata
24159 //                });
24160
24161     },
24162
24163     load : function(graphtype,xdata,opts)
24164     {
24165         this.raphael.clear();
24166         if(!graphtype) {
24167             graphtype = this.graphtype;
24168         }
24169         if(!opts){
24170             opts = this.opts;
24171         }
24172         var r = this.raphael,
24173             fin = function () {
24174                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24175             },
24176             fout = function () {
24177                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24178             },
24179             pfin = function() {
24180                 this.sector.stop();
24181                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24182
24183                 if (this.label) {
24184                     this.label[0].stop();
24185                     this.label[0].attr({ r: 7.5 });
24186                     this.label[1].attr({ "font-weight": 800 });
24187                 }
24188             },
24189             pfout = function() {
24190                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24191
24192                 if (this.label) {
24193                     this.label[0].animate({ r: 5 }, 500, "bounce");
24194                     this.label[1].attr({ "font-weight": 400 });
24195                 }
24196             };
24197
24198         switch(graphtype){
24199             case 'bar':
24200                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24201                 break;
24202             case 'hbar':
24203                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24204                 break;
24205             case 'pie':
24206 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24207 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24208 //            
24209                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24210                 
24211                 break;
24212
24213         }
24214         
24215         if(this.title){
24216             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24217         }
24218         
24219     },
24220     
24221     setTitle: function(o)
24222     {
24223         this.title = o;
24224     },
24225     
24226     initEvents: function() {
24227         
24228         if(!this.href){
24229             this.el.on('click', this.onClick, this);
24230         }
24231     },
24232     
24233     onClick : function(e)
24234     {
24235         Roo.log('img onclick');
24236         this.fireEvent('click', this, e);
24237     }
24238    
24239 });
24240
24241  
24242 /*
24243  * - LGPL
24244  *
24245  * numberBox
24246  * 
24247  */
24248 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24249
24250 /**
24251  * @class Roo.bootstrap.dash.NumberBox
24252  * @extends Roo.bootstrap.Component
24253  * Bootstrap NumberBox class
24254  * @cfg {String} headline Box headline
24255  * @cfg {String} content Box content
24256  * @cfg {String} icon Box icon
24257  * @cfg {String} footer Footer text
24258  * @cfg {String} fhref Footer href
24259  * 
24260  * @constructor
24261  * Create a new NumberBox
24262  * @param {Object} config The config object
24263  */
24264
24265
24266 Roo.bootstrap.dash.NumberBox = function(config){
24267     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24268     
24269 };
24270
24271 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24272     
24273     headline : '',
24274     content : '',
24275     icon : '',
24276     footer : '',
24277     fhref : '',
24278     ficon : '',
24279     
24280     getAutoCreate : function(){
24281         
24282         var cfg = {
24283             tag : 'div',
24284             cls : 'small-box ',
24285             cn : [
24286                 {
24287                     tag : 'div',
24288                     cls : 'inner',
24289                     cn :[
24290                         {
24291                             tag : 'h3',
24292                             cls : 'roo-headline',
24293                             html : this.headline
24294                         },
24295                         {
24296                             tag : 'p',
24297                             cls : 'roo-content',
24298                             html : this.content
24299                         }
24300                     ]
24301                 }
24302             ]
24303         };
24304         
24305         if(this.icon){
24306             cfg.cn.push({
24307                 tag : 'div',
24308                 cls : 'icon',
24309                 cn :[
24310                     {
24311                         tag : 'i',
24312                         cls : 'ion ' + this.icon
24313                     }
24314                 ]
24315             });
24316         }
24317         
24318         if(this.footer){
24319             var footer = {
24320                 tag : 'a',
24321                 cls : 'small-box-footer',
24322                 href : this.fhref || '#',
24323                 html : this.footer
24324             };
24325             
24326             cfg.cn.push(footer);
24327             
24328         }
24329         
24330         return  cfg;
24331     },
24332
24333     onRender : function(ct,position){
24334         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24335
24336
24337        
24338                 
24339     },
24340
24341     setHeadline: function (value)
24342     {
24343         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24344     },
24345     
24346     setFooter: function (value, href)
24347     {
24348         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24349         
24350         if(href){
24351             this.el.select('a.small-box-footer',true).first().attr('href', href);
24352         }
24353         
24354     },
24355
24356     setContent: function (value)
24357     {
24358         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24359     },
24360
24361     initEvents: function() 
24362     {   
24363         
24364     }
24365     
24366 });
24367
24368  
24369 /*
24370  * - LGPL
24371  *
24372  * TabBox
24373  * 
24374  */
24375 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24376
24377 /**
24378  * @class Roo.bootstrap.dash.TabBox
24379  * @extends Roo.bootstrap.Component
24380  * Bootstrap TabBox class
24381  * @cfg {String} title Title of the TabBox
24382  * @cfg {String} icon Icon of the TabBox
24383  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24384  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24385  * 
24386  * @constructor
24387  * Create a new TabBox
24388  * @param {Object} config The config object
24389  */
24390
24391
24392 Roo.bootstrap.dash.TabBox = function(config){
24393     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24394     this.addEvents({
24395         // raw events
24396         /**
24397          * @event addpane
24398          * When a pane is added
24399          * @param {Roo.bootstrap.dash.TabPane} pane
24400          */
24401         "addpane" : true,
24402         /**
24403          * @event activatepane
24404          * When a pane is activated
24405          * @param {Roo.bootstrap.dash.TabPane} pane
24406          */
24407         "activatepane" : true
24408         
24409          
24410     });
24411     
24412     this.panes = [];
24413 };
24414
24415 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24416
24417     title : '',
24418     icon : false,
24419     showtabs : true,
24420     tabScrollable : false,
24421     
24422     getChildContainer : function()
24423     {
24424         return this.el.select('.tab-content', true).first();
24425     },
24426     
24427     getAutoCreate : function(){
24428         
24429         var header = {
24430             tag: 'li',
24431             cls: 'pull-left header',
24432             html: this.title,
24433             cn : []
24434         };
24435         
24436         if(this.icon){
24437             header.cn.push({
24438                 tag: 'i',
24439                 cls: 'fa ' + this.icon
24440             });
24441         }
24442         
24443         var h = {
24444             tag: 'ul',
24445             cls: 'nav nav-tabs pull-right',
24446             cn: [
24447                 header
24448             ]
24449         };
24450         
24451         if(this.tabScrollable){
24452             h = {
24453                 tag: 'div',
24454                 cls: 'tab-header',
24455                 cn: [
24456                     {
24457                         tag: 'ul',
24458                         cls: 'nav nav-tabs pull-right',
24459                         cn: [
24460                             header
24461                         ]
24462                     }
24463                 ]
24464             };
24465         }
24466         
24467         var cfg = {
24468             tag: 'div',
24469             cls: 'nav-tabs-custom',
24470             cn: [
24471                 h,
24472                 {
24473                     tag: 'div',
24474                     cls: 'tab-content no-padding',
24475                     cn: []
24476                 }
24477             ]
24478         };
24479
24480         return  cfg;
24481     },
24482     initEvents : function()
24483     {
24484         //Roo.log('add add pane handler');
24485         this.on('addpane', this.onAddPane, this);
24486     },
24487      /**
24488      * Updates the box title
24489      * @param {String} html to set the title to.
24490      */
24491     setTitle : function(value)
24492     {
24493         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24494     },
24495     onAddPane : function(pane)
24496     {
24497         this.panes.push(pane);
24498         //Roo.log('addpane');
24499         //Roo.log(pane);
24500         // tabs are rendere left to right..
24501         if(!this.showtabs){
24502             return;
24503         }
24504         
24505         var ctr = this.el.select('.nav-tabs', true).first();
24506          
24507          
24508         var existing = ctr.select('.nav-tab',true);
24509         var qty = existing.getCount();;
24510         
24511         
24512         var tab = ctr.createChild({
24513             tag : 'li',
24514             cls : 'nav-tab' + (qty ? '' : ' active'),
24515             cn : [
24516                 {
24517                     tag : 'a',
24518                     href:'#',
24519                     html : pane.title
24520                 }
24521             ]
24522         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24523         pane.tab = tab;
24524         
24525         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24526         if (!qty) {
24527             pane.el.addClass('active');
24528         }
24529         
24530                 
24531     },
24532     onTabClick : function(ev,un,ob,pane)
24533     {
24534         //Roo.log('tab - prev default');
24535         ev.preventDefault();
24536         
24537         
24538         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24539         pane.tab.addClass('active');
24540         //Roo.log(pane.title);
24541         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24542         // technically we should have a deactivate event.. but maybe add later.
24543         // and it should not de-activate the selected tab...
24544         this.fireEvent('activatepane', pane);
24545         pane.el.addClass('active');
24546         pane.fireEvent('activate');
24547         
24548         
24549     },
24550     
24551     getActivePane : function()
24552     {
24553         var r = false;
24554         Roo.each(this.panes, function(p) {
24555             if(p.el.hasClass('active')){
24556                 r = p;
24557                 return false;
24558             }
24559             
24560             return;
24561         });
24562         
24563         return r;
24564     }
24565     
24566     
24567 });
24568
24569  
24570 /*
24571  * - LGPL
24572  *
24573  * Tab pane
24574  * 
24575  */
24576 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24577 /**
24578  * @class Roo.bootstrap.TabPane
24579  * @extends Roo.bootstrap.Component
24580  * Bootstrap TabPane class
24581  * @cfg {Boolean} active (false | true) Default false
24582  * @cfg {String} title title of panel
24583
24584  * 
24585  * @constructor
24586  * Create a new TabPane
24587  * @param {Object} config The config object
24588  */
24589
24590 Roo.bootstrap.dash.TabPane = function(config){
24591     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24592     
24593     this.addEvents({
24594         // raw events
24595         /**
24596          * @event activate
24597          * When a pane is activated
24598          * @param {Roo.bootstrap.dash.TabPane} pane
24599          */
24600         "activate" : true
24601          
24602     });
24603 };
24604
24605 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24606     
24607     active : false,
24608     title : '',
24609     
24610     // the tabBox that this is attached to.
24611     tab : false,
24612      
24613     getAutoCreate : function() 
24614     {
24615         var cfg = {
24616             tag: 'div',
24617             cls: 'tab-pane'
24618         };
24619         
24620         if(this.active){
24621             cfg.cls += ' active';
24622         }
24623         
24624         return cfg;
24625     },
24626     initEvents  : function()
24627     {
24628         //Roo.log('trigger add pane handler');
24629         this.parent().fireEvent('addpane', this)
24630     },
24631     
24632      /**
24633      * Updates the tab title 
24634      * @param {String} html to set the title to.
24635      */
24636     setTitle: function(str)
24637     {
24638         if (!this.tab) {
24639             return;
24640         }
24641         this.title = str;
24642         this.tab.select('a', true).first().dom.innerHTML = str;
24643         
24644     }
24645     
24646     
24647     
24648 });
24649
24650  
24651
24652
24653  /*
24654  * - LGPL
24655  *
24656  * menu
24657  * 
24658  */
24659 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24660
24661 /**
24662  * @class Roo.bootstrap.menu.Menu
24663  * @extends Roo.bootstrap.Component
24664  * Bootstrap Menu class - container for Menu
24665  * @cfg {String} html Text of the menu
24666  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24667  * @cfg {String} icon Font awesome icon
24668  * @cfg {String} pos Menu align to (top | bottom) default bottom
24669  * 
24670  * 
24671  * @constructor
24672  * Create a new Menu
24673  * @param {Object} config The config object
24674  */
24675
24676
24677 Roo.bootstrap.menu.Menu = function(config){
24678     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24679     
24680     this.addEvents({
24681         /**
24682          * @event beforeshow
24683          * Fires before this menu is displayed
24684          * @param {Roo.bootstrap.menu.Menu} this
24685          */
24686         beforeshow : true,
24687         /**
24688          * @event beforehide
24689          * Fires before this menu is hidden
24690          * @param {Roo.bootstrap.menu.Menu} this
24691          */
24692         beforehide : true,
24693         /**
24694          * @event show
24695          * Fires after this menu is displayed
24696          * @param {Roo.bootstrap.menu.Menu} this
24697          */
24698         show : true,
24699         /**
24700          * @event hide
24701          * Fires after this menu is hidden
24702          * @param {Roo.bootstrap.menu.Menu} this
24703          */
24704         hide : true,
24705         /**
24706          * @event click
24707          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24708          * @param {Roo.bootstrap.menu.Menu} this
24709          * @param {Roo.EventObject} e
24710          */
24711         click : true
24712     });
24713     
24714 };
24715
24716 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24717     
24718     submenu : false,
24719     html : '',
24720     weight : 'default',
24721     icon : false,
24722     pos : 'bottom',
24723     
24724     
24725     getChildContainer : function() {
24726         if(this.isSubMenu){
24727             return this.el;
24728         }
24729         
24730         return this.el.select('ul.dropdown-menu', true).first();  
24731     },
24732     
24733     getAutoCreate : function()
24734     {
24735         var text = [
24736             {
24737                 tag : 'span',
24738                 cls : 'roo-menu-text',
24739                 html : this.html
24740             }
24741         ];
24742         
24743         if(this.icon){
24744             text.unshift({
24745                 tag : 'i',
24746                 cls : 'fa ' + this.icon
24747             })
24748         }
24749         
24750         
24751         var cfg = {
24752             tag : 'div',
24753             cls : 'btn-group',
24754             cn : [
24755                 {
24756                     tag : 'button',
24757                     cls : 'dropdown-button btn btn-' + this.weight,
24758                     cn : text
24759                 },
24760                 {
24761                     tag : 'button',
24762                     cls : 'dropdown-toggle btn btn-' + this.weight,
24763                     cn : [
24764                         {
24765                             tag : 'span',
24766                             cls : 'caret'
24767                         }
24768                     ]
24769                 },
24770                 {
24771                     tag : 'ul',
24772                     cls : 'dropdown-menu'
24773                 }
24774             ]
24775             
24776         };
24777         
24778         if(this.pos == 'top'){
24779             cfg.cls += ' dropup';
24780         }
24781         
24782         if(this.isSubMenu){
24783             cfg = {
24784                 tag : 'ul',
24785                 cls : 'dropdown-menu'
24786             }
24787         }
24788         
24789         return cfg;
24790     },
24791     
24792     onRender : function(ct, position)
24793     {
24794         this.isSubMenu = ct.hasClass('dropdown-submenu');
24795         
24796         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24797     },
24798     
24799     initEvents : function() 
24800     {
24801         if(this.isSubMenu){
24802             return;
24803         }
24804         
24805         this.hidden = true;
24806         
24807         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24808         this.triggerEl.on('click', this.onTriggerPress, this);
24809         
24810         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24811         this.buttonEl.on('click', this.onClick, this);
24812         
24813     },
24814     
24815     list : function()
24816     {
24817         if(this.isSubMenu){
24818             return this.el;
24819         }
24820         
24821         return this.el.select('ul.dropdown-menu', true).first();
24822     },
24823     
24824     onClick : function(e)
24825     {
24826         this.fireEvent("click", this, e);
24827     },
24828     
24829     onTriggerPress  : function(e)
24830     {   
24831         if (this.isVisible()) {
24832             this.hide();
24833         } else {
24834             this.show();
24835         }
24836     },
24837     
24838     isVisible : function(){
24839         return !this.hidden;
24840     },
24841     
24842     show : function()
24843     {
24844         this.fireEvent("beforeshow", this);
24845         
24846         this.hidden = false;
24847         this.el.addClass('open');
24848         
24849         Roo.get(document).on("mouseup", this.onMouseUp, this);
24850         
24851         this.fireEvent("show", this);
24852         
24853         
24854     },
24855     
24856     hide : function()
24857     {
24858         this.fireEvent("beforehide", this);
24859         
24860         this.hidden = true;
24861         this.el.removeClass('open');
24862         
24863         Roo.get(document).un("mouseup", this.onMouseUp);
24864         
24865         this.fireEvent("hide", this);
24866     },
24867     
24868     onMouseUp : function()
24869     {
24870         this.hide();
24871     }
24872     
24873 });
24874
24875  
24876  /*
24877  * - LGPL
24878  *
24879  * menu item
24880  * 
24881  */
24882 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24883
24884 /**
24885  * @class Roo.bootstrap.menu.Item
24886  * @extends Roo.bootstrap.Component
24887  * Bootstrap MenuItem class
24888  * @cfg {Boolean} submenu (true | false) default false
24889  * @cfg {String} html text of the item
24890  * @cfg {String} href the link
24891  * @cfg {Boolean} disable (true | false) default false
24892  * @cfg {Boolean} preventDefault (true | false) default true
24893  * @cfg {String} icon Font awesome icon
24894  * @cfg {String} pos Submenu align to (left | right) default right 
24895  * 
24896  * 
24897  * @constructor
24898  * Create a new Item
24899  * @param {Object} config The config object
24900  */
24901
24902
24903 Roo.bootstrap.menu.Item = function(config){
24904     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24905     this.addEvents({
24906         /**
24907          * @event mouseover
24908          * Fires when the mouse is hovering over this menu
24909          * @param {Roo.bootstrap.menu.Item} this
24910          * @param {Roo.EventObject} e
24911          */
24912         mouseover : true,
24913         /**
24914          * @event mouseout
24915          * Fires when the mouse exits this menu
24916          * @param {Roo.bootstrap.menu.Item} this
24917          * @param {Roo.EventObject} e
24918          */
24919         mouseout : true,
24920         // raw events
24921         /**
24922          * @event click
24923          * The raw click event for the entire grid.
24924          * @param {Roo.EventObject} e
24925          */
24926         click : true
24927     });
24928 };
24929
24930 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24931     
24932     submenu : false,
24933     href : '',
24934     html : '',
24935     preventDefault: true,
24936     disable : false,
24937     icon : false,
24938     pos : 'right',
24939     
24940     getAutoCreate : function()
24941     {
24942         var text = [
24943             {
24944                 tag : 'span',
24945                 cls : 'roo-menu-item-text',
24946                 html : this.html
24947             }
24948         ];
24949         
24950         if(this.icon){
24951             text.unshift({
24952                 tag : 'i',
24953                 cls : 'fa ' + this.icon
24954             })
24955         }
24956         
24957         var cfg = {
24958             tag : 'li',
24959             cn : [
24960                 {
24961                     tag : 'a',
24962                     href : this.href || '#',
24963                     cn : text
24964                 }
24965             ]
24966         };
24967         
24968         if(this.disable){
24969             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24970         }
24971         
24972         if(this.submenu){
24973             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24974             
24975             if(this.pos == 'left'){
24976                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24977             }
24978         }
24979         
24980         return cfg;
24981     },
24982     
24983     initEvents : function() 
24984     {
24985         this.el.on('mouseover', this.onMouseOver, this);
24986         this.el.on('mouseout', this.onMouseOut, this);
24987         
24988         this.el.select('a', true).first().on('click', this.onClick, this);
24989         
24990     },
24991     
24992     onClick : function(e)
24993     {
24994         if(this.preventDefault){
24995             e.preventDefault();
24996         }
24997         
24998         this.fireEvent("click", this, e);
24999     },
25000     
25001     onMouseOver : function(e)
25002     {
25003         if(this.submenu && this.pos == 'left'){
25004             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25005         }
25006         
25007         this.fireEvent("mouseover", this, e);
25008     },
25009     
25010     onMouseOut : function(e)
25011     {
25012         this.fireEvent("mouseout", this, e);
25013     }
25014 });
25015
25016  
25017
25018  /*
25019  * - LGPL
25020  *
25021  * menu separator
25022  * 
25023  */
25024 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25025
25026 /**
25027  * @class Roo.bootstrap.menu.Separator
25028  * @extends Roo.bootstrap.Component
25029  * Bootstrap Separator class
25030  * 
25031  * @constructor
25032  * Create a new Separator
25033  * @param {Object} config The config object
25034  */
25035
25036
25037 Roo.bootstrap.menu.Separator = function(config){
25038     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25039 };
25040
25041 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25042     
25043     getAutoCreate : function(){
25044         var cfg = {
25045             tag : 'li',
25046             cls: 'divider'
25047         };
25048         
25049         return cfg;
25050     }
25051    
25052 });
25053
25054  
25055
25056  /*
25057  * - LGPL
25058  *
25059  * Tooltip
25060  * 
25061  */
25062
25063 /**
25064  * @class Roo.bootstrap.Tooltip
25065  * Bootstrap Tooltip class
25066  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25067  * to determine which dom element triggers the tooltip.
25068  * 
25069  * It needs to add support for additional attributes like tooltip-position
25070  * 
25071  * @constructor
25072  * Create a new Toolti
25073  * @param {Object} config The config object
25074  */
25075
25076 Roo.bootstrap.Tooltip = function(config){
25077     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25078     
25079     this.alignment = Roo.bootstrap.Tooltip.alignment;
25080     
25081     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25082         this.alignment = config.alignment;
25083     }
25084     
25085 };
25086
25087 Roo.apply(Roo.bootstrap.Tooltip, {
25088     /**
25089      * @function init initialize tooltip monitoring.
25090      * @static
25091      */
25092     currentEl : false,
25093     currentTip : false,
25094     currentRegion : false,
25095     
25096     //  init : delay?
25097     
25098     init : function()
25099     {
25100         Roo.get(document).on('mouseover', this.enter ,this);
25101         Roo.get(document).on('mouseout', this.leave, this);
25102          
25103         
25104         this.currentTip = new Roo.bootstrap.Tooltip();
25105     },
25106     
25107     enter : function(ev)
25108     {
25109         var dom = ev.getTarget();
25110         
25111         //Roo.log(['enter',dom]);
25112         var el = Roo.fly(dom);
25113         if (this.currentEl) {
25114             //Roo.log(dom);
25115             //Roo.log(this.currentEl);
25116             //Roo.log(this.currentEl.contains(dom));
25117             if (this.currentEl == el) {
25118                 return;
25119             }
25120             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25121                 return;
25122             }
25123
25124         }
25125         
25126         if (this.currentTip.el) {
25127             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25128         }    
25129         //Roo.log(ev);
25130         
25131         if(!el || el.dom == document){
25132             return;
25133         }
25134         
25135         var bindEl = el;
25136         
25137         // you can not look for children, as if el is the body.. then everythign is the child..
25138         if (!el.attr('tooltip')) { //
25139             if (!el.select("[tooltip]").elements.length) {
25140                 return;
25141             }
25142             // is the mouse over this child...?
25143             bindEl = el.select("[tooltip]").first();
25144             var xy = ev.getXY();
25145             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25146                 //Roo.log("not in region.");
25147                 return;
25148             }
25149             //Roo.log("child element over..");
25150             
25151         }
25152         this.currentEl = bindEl;
25153         this.currentTip.bind(bindEl);
25154         this.currentRegion = Roo.lib.Region.getRegion(dom);
25155         this.currentTip.enter();
25156         
25157     },
25158     leave : function(ev)
25159     {
25160         var dom = ev.getTarget();
25161         //Roo.log(['leave',dom]);
25162         if (!this.currentEl) {
25163             return;
25164         }
25165         
25166         
25167         if (dom != this.currentEl.dom) {
25168             return;
25169         }
25170         var xy = ev.getXY();
25171         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25172             return;
25173         }
25174         // only activate leave if mouse cursor is outside... bounding box..
25175         
25176         
25177         
25178         
25179         if (this.currentTip) {
25180             this.currentTip.leave();
25181         }
25182         //Roo.log('clear currentEl');
25183         this.currentEl = false;
25184         
25185         
25186     },
25187     alignment : {
25188         'left' : ['r-l', [-2,0], 'right'],
25189         'right' : ['l-r', [2,0], 'left'],
25190         'bottom' : ['t-b', [0,2], 'top'],
25191         'top' : [ 'b-t', [0,-2], 'bottom']
25192     }
25193     
25194 });
25195
25196
25197 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25198     
25199     
25200     bindEl : false,
25201     
25202     delay : null, // can be { show : 300 , hide: 500}
25203     
25204     timeout : null,
25205     
25206     hoverState : null, //???
25207     
25208     placement : 'bottom', 
25209     
25210     alignment : false,
25211     
25212     getAutoCreate : function(){
25213     
25214         var cfg = {
25215            cls : 'tooltip',
25216            role : 'tooltip',
25217            cn : [
25218                 {
25219                     cls : 'tooltip-arrow'
25220                 },
25221                 {
25222                     cls : 'tooltip-inner'
25223                 }
25224            ]
25225         };
25226         
25227         return cfg;
25228     },
25229     bind : function(el)
25230     {
25231         this.bindEl = el;
25232     },
25233       
25234     
25235     enter : function () {
25236        
25237         if (this.timeout != null) {
25238             clearTimeout(this.timeout);
25239         }
25240         
25241         this.hoverState = 'in';
25242          //Roo.log("enter - show");
25243         if (!this.delay || !this.delay.show) {
25244             this.show();
25245             return;
25246         }
25247         var _t = this;
25248         this.timeout = setTimeout(function () {
25249             if (_t.hoverState == 'in') {
25250                 _t.show();
25251             }
25252         }, this.delay.show);
25253     },
25254     leave : function()
25255     {
25256         clearTimeout(this.timeout);
25257     
25258         this.hoverState = 'out';
25259          if (!this.delay || !this.delay.hide) {
25260             this.hide();
25261             return;
25262         }
25263        
25264         var _t = this;
25265         this.timeout = setTimeout(function () {
25266             //Roo.log("leave - timeout");
25267             
25268             if (_t.hoverState == 'out') {
25269                 _t.hide();
25270                 Roo.bootstrap.Tooltip.currentEl = false;
25271             }
25272         }, delay);
25273     },
25274     
25275     show : function (msg)
25276     {
25277         if (!this.el) {
25278             this.render(document.body);
25279         }
25280         // set content.
25281         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25282         
25283         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25284         
25285         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25286         
25287         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25288         
25289         var placement = typeof this.placement == 'function' ?
25290             this.placement.call(this, this.el, on_el) :
25291             this.placement;
25292             
25293         var autoToken = /\s?auto?\s?/i;
25294         var autoPlace = autoToken.test(placement);
25295         if (autoPlace) {
25296             placement = placement.replace(autoToken, '') || 'top';
25297         }
25298         
25299         //this.el.detach()
25300         //this.el.setXY([0,0]);
25301         this.el.show();
25302         //this.el.dom.style.display='block';
25303         
25304         //this.el.appendTo(on_el);
25305         
25306         var p = this.getPosition();
25307         var box = this.el.getBox();
25308         
25309         if (autoPlace) {
25310             // fixme..
25311         }
25312         
25313         var align = this.alignment[placement];
25314         
25315         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25316         
25317         if(placement == 'top' || placement == 'bottom'){
25318             if(xy[0] < 0){
25319                 placement = 'right';
25320             }
25321             
25322             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25323                 placement = 'left';
25324             }
25325             
25326             var scroll = Roo.select('body', true).first().getScroll();
25327             
25328             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25329                 placement = 'top';
25330             }
25331             
25332         }
25333         
25334         this.el.alignTo(this.bindEl, align[0],align[1]);
25335         //var arrow = this.el.select('.arrow',true).first();
25336         //arrow.set(align[2], 
25337         
25338         this.el.addClass(placement);
25339         
25340         this.el.addClass('in fade');
25341         
25342         this.hoverState = null;
25343         
25344         if (this.el.hasClass('fade')) {
25345             // fade it?
25346         }
25347         
25348     },
25349     hide : function()
25350     {
25351          
25352         if (!this.el) {
25353             return;
25354         }
25355         //this.el.setXY([0,0]);
25356         this.el.removeClass('in');
25357         //this.el.hide();
25358         
25359     }
25360     
25361 });
25362  
25363
25364  /*
25365  * - LGPL
25366  *
25367  * Location Picker
25368  * 
25369  */
25370
25371 /**
25372  * @class Roo.bootstrap.LocationPicker
25373  * @extends Roo.bootstrap.Component
25374  * Bootstrap LocationPicker class
25375  * @cfg {Number} latitude Position when init default 0
25376  * @cfg {Number} longitude Position when init default 0
25377  * @cfg {Number} zoom default 15
25378  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25379  * @cfg {Boolean} mapTypeControl default false
25380  * @cfg {Boolean} disableDoubleClickZoom default false
25381  * @cfg {Boolean} scrollwheel default true
25382  * @cfg {Boolean} streetViewControl default false
25383  * @cfg {Number} radius default 0
25384  * @cfg {String} locationName
25385  * @cfg {Boolean} draggable default true
25386  * @cfg {Boolean} enableAutocomplete default false
25387  * @cfg {Boolean} enableReverseGeocode default true
25388  * @cfg {String} markerTitle
25389  * 
25390  * @constructor
25391  * Create a new LocationPicker
25392  * @param {Object} config The config object
25393  */
25394
25395
25396 Roo.bootstrap.LocationPicker = function(config){
25397     
25398     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25399     
25400     this.addEvents({
25401         /**
25402          * @event initial
25403          * Fires when the picker initialized.
25404          * @param {Roo.bootstrap.LocationPicker} this
25405          * @param {Google Location} location
25406          */
25407         initial : true,
25408         /**
25409          * @event positionchanged
25410          * Fires when the picker position changed.
25411          * @param {Roo.bootstrap.LocationPicker} this
25412          * @param {Google Location} location
25413          */
25414         positionchanged : true,
25415         /**
25416          * @event resize
25417          * Fires when the map resize.
25418          * @param {Roo.bootstrap.LocationPicker} this
25419          */
25420         resize : true,
25421         /**
25422          * @event show
25423          * Fires when the map show.
25424          * @param {Roo.bootstrap.LocationPicker} this
25425          */
25426         show : true,
25427         /**
25428          * @event hide
25429          * Fires when the map hide.
25430          * @param {Roo.bootstrap.LocationPicker} this
25431          */
25432         hide : true,
25433         /**
25434          * @event mapClick
25435          * Fires when click the map.
25436          * @param {Roo.bootstrap.LocationPicker} this
25437          * @param {Map event} e
25438          */
25439         mapClick : true,
25440         /**
25441          * @event mapRightClick
25442          * Fires when right click the map.
25443          * @param {Roo.bootstrap.LocationPicker} this
25444          * @param {Map event} e
25445          */
25446         mapRightClick : true,
25447         /**
25448          * @event markerClick
25449          * Fires when click the marker.
25450          * @param {Roo.bootstrap.LocationPicker} this
25451          * @param {Map event} e
25452          */
25453         markerClick : true,
25454         /**
25455          * @event markerRightClick
25456          * Fires when right click the marker.
25457          * @param {Roo.bootstrap.LocationPicker} this
25458          * @param {Map event} e
25459          */
25460         markerRightClick : true,
25461         /**
25462          * @event OverlayViewDraw
25463          * Fires when OverlayView Draw
25464          * @param {Roo.bootstrap.LocationPicker} this
25465          */
25466         OverlayViewDraw : true,
25467         /**
25468          * @event OverlayViewOnAdd
25469          * Fires when OverlayView Draw
25470          * @param {Roo.bootstrap.LocationPicker} this
25471          */
25472         OverlayViewOnAdd : true,
25473         /**
25474          * @event OverlayViewOnRemove
25475          * Fires when OverlayView Draw
25476          * @param {Roo.bootstrap.LocationPicker} this
25477          */
25478         OverlayViewOnRemove : true,
25479         /**
25480          * @event OverlayViewShow
25481          * Fires when OverlayView Draw
25482          * @param {Roo.bootstrap.LocationPicker} this
25483          * @param {Pixel} cpx
25484          */
25485         OverlayViewShow : true,
25486         /**
25487          * @event OverlayViewHide
25488          * Fires when OverlayView Draw
25489          * @param {Roo.bootstrap.LocationPicker} this
25490          */
25491         OverlayViewHide : true,
25492         /**
25493          * @event loadexception
25494          * Fires when load google lib failed.
25495          * @param {Roo.bootstrap.LocationPicker} this
25496          */
25497         loadexception : true
25498     });
25499         
25500 };
25501
25502 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25503     
25504     gMapContext: false,
25505     
25506     latitude: 0,
25507     longitude: 0,
25508     zoom: 15,
25509     mapTypeId: false,
25510     mapTypeControl: false,
25511     disableDoubleClickZoom: false,
25512     scrollwheel: true,
25513     streetViewControl: false,
25514     radius: 0,
25515     locationName: '',
25516     draggable: true,
25517     enableAutocomplete: false,
25518     enableReverseGeocode: true,
25519     markerTitle: '',
25520     
25521     getAutoCreate: function()
25522     {
25523
25524         var cfg = {
25525             tag: 'div',
25526             cls: 'roo-location-picker'
25527         };
25528         
25529         return cfg
25530     },
25531     
25532     initEvents: function(ct, position)
25533     {       
25534         if(!this.el.getWidth() || this.isApplied()){
25535             return;
25536         }
25537         
25538         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25539         
25540         this.initial();
25541     },
25542     
25543     initial: function()
25544     {
25545         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25546             this.fireEvent('loadexception', this);
25547             return;
25548         }
25549         
25550         if(!this.mapTypeId){
25551             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25552         }
25553         
25554         this.gMapContext = this.GMapContext();
25555         
25556         this.initOverlayView();
25557         
25558         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25559         
25560         var _this = this;
25561                 
25562         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25563             _this.setPosition(_this.gMapContext.marker.position);
25564         });
25565         
25566         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25567             _this.fireEvent('mapClick', this, event);
25568             
25569         });
25570
25571         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25572             _this.fireEvent('mapRightClick', this, event);
25573             
25574         });
25575         
25576         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25577             _this.fireEvent('markerClick', this, event);
25578             
25579         });
25580
25581         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25582             _this.fireEvent('markerRightClick', this, event);
25583             
25584         });
25585         
25586         this.setPosition(this.gMapContext.location);
25587         
25588         this.fireEvent('initial', this, this.gMapContext.location);
25589     },
25590     
25591     initOverlayView: function()
25592     {
25593         var _this = this;
25594         
25595         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25596             
25597             draw: function()
25598             {
25599                 _this.fireEvent('OverlayViewDraw', _this);
25600             },
25601             
25602             onAdd: function()
25603             {
25604                 _this.fireEvent('OverlayViewOnAdd', _this);
25605             },
25606             
25607             onRemove: function()
25608             {
25609                 _this.fireEvent('OverlayViewOnRemove', _this);
25610             },
25611             
25612             show: function(cpx)
25613             {
25614                 _this.fireEvent('OverlayViewShow', _this, cpx);
25615             },
25616             
25617             hide: function()
25618             {
25619                 _this.fireEvent('OverlayViewHide', _this);
25620             }
25621             
25622         });
25623     },
25624     
25625     fromLatLngToContainerPixel: function(event)
25626     {
25627         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25628     },
25629     
25630     isApplied: function() 
25631     {
25632         return this.getGmapContext() == false ? false : true;
25633     },
25634     
25635     getGmapContext: function() 
25636     {
25637         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25638     },
25639     
25640     GMapContext: function() 
25641     {
25642         var position = new google.maps.LatLng(this.latitude, this.longitude);
25643         
25644         var _map = new google.maps.Map(this.el.dom, {
25645             center: position,
25646             zoom: this.zoom,
25647             mapTypeId: this.mapTypeId,
25648             mapTypeControl: this.mapTypeControl,
25649             disableDoubleClickZoom: this.disableDoubleClickZoom,
25650             scrollwheel: this.scrollwheel,
25651             streetViewControl: this.streetViewControl,
25652             locationName: this.locationName,
25653             draggable: this.draggable,
25654             enableAutocomplete: this.enableAutocomplete,
25655             enableReverseGeocode: this.enableReverseGeocode
25656         });
25657         
25658         var _marker = new google.maps.Marker({
25659             position: position,
25660             map: _map,
25661             title: this.markerTitle,
25662             draggable: this.draggable
25663         });
25664         
25665         return {
25666             map: _map,
25667             marker: _marker,
25668             circle: null,
25669             location: position,
25670             radius: this.radius,
25671             locationName: this.locationName,
25672             addressComponents: {
25673                 formatted_address: null,
25674                 addressLine1: null,
25675                 addressLine2: null,
25676                 streetName: null,
25677                 streetNumber: null,
25678                 city: null,
25679                 district: null,
25680                 state: null,
25681                 stateOrProvince: null
25682             },
25683             settings: this,
25684             domContainer: this.el.dom,
25685             geodecoder: new google.maps.Geocoder()
25686         };
25687     },
25688     
25689     drawCircle: function(center, radius, options) 
25690     {
25691         if (this.gMapContext.circle != null) {
25692             this.gMapContext.circle.setMap(null);
25693         }
25694         if (radius > 0) {
25695             radius *= 1;
25696             options = Roo.apply({}, options, {
25697                 strokeColor: "#0000FF",
25698                 strokeOpacity: .35,
25699                 strokeWeight: 2,
25700                 fillColor: "#0000FF",
25701                 fillOpacity: .2
25702             });
25703             
25704             options.map = this.gMapContext.map;
25705             options.radius = radius;
25706             options.center = center;
25707             this.gMapContext.circle = new google.maps.Circle(options);
25708             return this.gMapContext.circle;
25709         }
25710         
25711         return null;
25712     },
25713     
25714     setPosition: function(location) 
25715     {
25716         this.gMapContext.location = location;
25717         this.gMapContext.marker.setPosition(location);
25718         this.gMapContext.map.panTo(location);
25719         this.drawCircle(location, this.gMapContext.radius, {});
25720         
25721         var _this = this;
25722         
25723         if (this.gMapContext.settings.enableReverseGeocode) {
25724             this.gMapContext.geodecoder.geocode({
25725                 latLng: this.gMapContext.location
25726             }, function(results, status) {
25727                 
25728                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25729                     _this.gMapContext.locationName = results[0].formatted_address;
25730                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25731                     
25732                     _this.fireEvent('positionchanged', this, location);
25733                 }
25734             });
25735             
25736             return;
25737         }
25738         
25739         this.fireEvent('positionchanged', this, location);
25740     },
25741     
25742     resize: function()
25743     {
25744         google.maps.event.trigger(this.gMapContext.map, "resize");
25745         
25746         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25747         
25748         this.fireEvent('resize', this);
25749     },
25750     
25751     setPositionByLatLng: function(latitude, longitude)
25752     {
25753         this.setPosition(new google.maps.LatLng(latitude, longitude));
25754     },
25755     
25756     getCurrentPosition: function() 
25757     {
25758         return {
25759             latitude: this.gMapContext.location.lat(),
25760             longitude: this.gMapContext.location.lng()
25761         };
25762     },
25763     
25764     getAddressName: function() 
25765     {
25766         return this.gMapContext.locationName;
25767     },
25768     
25769     getAddressComponents: function() 
25770     {
25771         return this.gMapContext.addressComponents;
25772     },
25773     
25774     address_component_from_google_geocode: function(address_components) 
25775     {
25776         var result = {};
25777         
25778         for (var i = 0; i < address_components.length; i++) {
25779             var component = address_components[i];
25780             if (component.types.indexOf("postal_code") >= 0) {
25781                 result.postalCode = component.short_name;
25782             } else if (component.types.indexOf("street_number") >= 0) {
25783                 result.streetNumber = component.short_name;
25784             } else if (component.types.indexOf("route") >= 0) {
25785                 result.streetName = component.short_name;
25786             } else if (component.types.indexOf("neighborhood") >= 0) {
25787                 result.city = component.short_name;
25788             } else if (component.types.indexOf("locality") >= 0) {
25789                 result.city = component.short_name;
25790             } else if (component.types.indexOf("sublocality") >= 0) {
25791                 result.district = component.short_name;
25792             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25793                 result.stateOrProvince = component.short_name;
25794             } else if (component.types.indexOf("country") >= 0) {
25795                 result.country = component.short_name;
25796             }
25797         }
25798         
25799         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25800         result.addressLine2 = "";
25801         return result;
25802     },
25803     
25804     setZoomLevel: function(zoom)
25805     {
25806         this.gMapContext.map.setZoom(zoom);
25807     },
25808     
25809     show: function()
25810     {
25811         if(!this.el){
25812             return;
25813         }
25814         
25815         this.el.show();
25816         
25817         this.resize();
25818         
25819         this.fireEvent('show', this);
25820     },
25821     
25822     hide: function()
25823     {
25824         if(!this.el){
25825             return;
25826         }
25827         
25828         this.el.hide();
25829         
25830         this.fireEvent('hide', this);
25831     }
25832     
25833 });
25834
25835 Roo.apply(Roo.bootstrap.LocationPicker, {
25836     
25837     OverlayView : function(map, options)
25838     {
25839         options = options || {};
25840         
25841         this.setMap(map);
25842     }
25843     
25844     
25845 });/*
25846  * - LGPL
25847  *
25848  * Alert
25849  * 
25850  */
25851
25852 /**
25853  * @class Roo.bootstrap.Alert
25854  * @extends Roo.bootstrap.Component
25855  * Bootstrap Alert class
25856  * @cfg {String} title The title of alert
25857  * @cfg {String} html The content of alert
25858  * @cfg {String} weight (  success | info | warning | danger )
25859  * @cfg {String} faicon font-awesomeicon
25860  * 
25861  * @constructor
25862  * Create a new alert
25863  * @param {Object} config The config object
25864  */
25865
25866
25867 Roo.bootstrap.Alert = function(config){
25868     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25869     
25870 };
25871
25872 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25873     
25874     title: '',
25875     html: '',
25876     weight: false,
25877     faicon: false,
25878     
25879     getAutoCreate : function()
25880     {
25881         
25882         var cfg = {
25883             tag : 'div',
25884             cls : 'alert',
25885             cn : [
25886                 {
25887                     tag : 'i',
25888                     cls : 'roo-alert-icon'
25889                     
25890                 },
25891                 {
25892                     tag : 'b',
25893                     cls : 'roo-alert-title',
25894                     html : this.title
25895                 },
25896                 {
25897                     tag : 'span',
25898                     cls : 'roo-alert-text',
25899                     html : this.html
25900                 }
25901             ]
25902         };
25903         
25904         if(this.faicon){
25905             cfg.cn[0].cls += ' fa ' + this.faicon;
25906         }
25907         
25908         if(this.weight){
25909             cfg.cls += ' alert-' + this.weight;
25910         }
25911         
25912         return cfg;
25913     },
25914     
25915     initEvents: function() 
25916     {
25917         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25918     },
25919     
25920     setTitle : function(str)
25921     {
25922         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25923     },
25924     
25925     setText : function(str)
25926     {
25927         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25928     },
25929     
25930     setWeight : function(weight)
25931     {
25932         if(this.weight){
25933             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25934         }
25935         
25936         this.weight = weight;
25937         
25938         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25939     },
25940     
25941     setIcon : function(icon)
25942     {
25943         if(this.faicon){
25944             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25945         }
25946         
25947         this.faicon = icon;
25948         
25949         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25950     },
25951     
25952     hide: function() 
25953     {
25954         this.el.hide();   
25955     },
25956     
25957     show: function() 
25958     {  
25959         this.el.show();   
25960     }
25961     
25962 });
25963
25964  
25965 /*
25966 * Licence: LGPL
25967 */
25968
25969 /**
25970  * @class Roo.bootstrap.UploadCropbox
25971  * @extends Roo.bootstrap.Component
25972  * Bootstrap UploadCropbox class
25973  * @cfg {String} emptyText show when image has been loaded
25974  * @cfg {String} rotateNotify show when image too small to rotate
25975  * @cfg {Number} errorTimeout default 3000
25976  * @cfg {Number} minWidth default 300
25977  * @cfg {Number} minHeight default 300
25978  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25979  * @cfg {Boolean} isDocument (true|false) default false
25980  * @cfg {String} url action url
25981  * @cfg {String} paramName default 'imageUpload'
25982  * @cfg {String} method default POST
25983  * @cfg {Boolean} loadMask (true|false) default true
25984  * @cfg {Boolean} loadingText default 'Loading...'
25985  * 
25986  * @constructor
25987  * Create a new UploadCropbox
25988  * @param {Object} config The config object
25989  */
25990
25991 Roo.bootstrap.UploadCropbox = function(config){
25992     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25993     
25994     this.addEvents({
25995         /**
25996          * @event beforeselectfile
25997          * Fire before select file
25998          * @param {Roo.bootstrap.UploadCropbox} this
25999          */
26000         "beforeselectfile" : true,
26001         /**
26002          * @event initial
26003          * Fire after initEvent
26004          * @param {Roo.bootstrap.UploadCropbox} this
26005          */
26006         "initial" : true,
26007         /**
26008          * @event crop
26009          * Fire after initEvent
26010          * @param {Roo.bootstrap.UploadCropbox} this
26011          * @param {String} data
26012          */
26013         "crop" : true,
26014         /**
26015          * @event prepare
26016          * Fire when preparing the file data
26017          * @param {Roo.bootstrap.UploadCropbox} this
26018          * @param {Object} file
26019          */
26020         "prepare" : true,
26021         /**
26022          * @event exception
26023          * Fire when get exception
26024          * @param {Roo.bootstrap.UploadCropbox} this
26025          * @param {XMLHttpRequest} xhr
26026          */
26027         "exception" : true,
26028         /**
26029          * @event beforeloadcanvas
26030          * Fire before load the canvas
26031          * @param {Roo.bootstrap.UploadCropbox} this
26032          * @param {String} src
26033          */
26034         "beforeloadcanvas" : true,
26035         /**
26036          * @event trash
26037          * Fire when trash image
26038          * @param {Roo.bootstrap.UploadCropbox} this
26039          */
26040         "trash" : true,
26041         /**
26042          * @event download
26043          * Fire when download the image
26044          * @param {Roo.bootstrap.UploadCropbox} this
26045          */
26046         "download" : true,
26047         /**
26048          * @event footerbuttonclick
26049          * Fire when footerbuttonclick
26050          * @param {Roo.bootstrap.UploadCropbox} this
26051          * @param {String} type
26052          */
26053         "footerbuttonclick" : true,
26054         /**
26055          * @event resize
26056          * Fire when resize
26057          * @param {Roo.bootstrap.UploadCropbox} this
26058          */
26059         "resize" : true,
26060         /**
26061          * @event rotate
26062          * Fire when rotate the image
26063          * @param {Roo.bootstrap.UploadCropbox} this
26064          * @param {String} pos
26065          */
26066         "rotate" : true,
26067         /**
26068          * @event inspect
26069          * Fire when inspect the file
26070          * @param {Roo.bootstrap.UploadCropbox} this
26071          * @param {Object} file
26072          */
26073         "inspect" : true,
26074         /**
26075          * @event upload
26076          * Fire when xhr upload the file
26077          * @param {Roo.bootstrap.UploadCropbox} this
26078          * @param {Object} data
26079          */
26080         "upload" : true,
26081         /**
26082          * @event arrange
26083          * Fire when arrange the file data
26084          * @param {Roo.bootstrap.UploadCropbox} this
26085          * @param {Object} formData
26086          */
26087         "arrange" : true
26088     });
26089     
26090     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26091 };
26092
26093 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26094     
26095     emptyText : 'Click to upload image',
26096     rotateNotify : 'Image is too small to rotate',
26097     errorTimeout : 3000,
26098     scale : 0,
26099     baseScale : 1,
26100     rotate : 0,
26101     dragable : false,
26102     pinching : false,
26103     mouseX : 0,
26104     mouseY : 0,
26105     cropData : false,
26106     minWidth : 300,
26107     minHeight : 300,
26108     file : false,
26109     exif : {},
26110     baseRotate : 1,
26111     cropType : 'image/jpeg',
26112     buttons : false,
26113     canvasLoaded : false,
26114     isDocument : false,
26115     method : 'POST',
26116     paramName : 'imageUpload',
26117     loadMask : true,
26118     loadingText : 'Loading...',
26119     maskEl : false,
26120     
26121     getAutoCreate : function()
26122     {
26123         var cfg = {
26124             tag : 'div',
26125             cls : 'roo-upload-cropbox',
26126             cn : [
26127                 {
26128                     tag : 'input',
26129                     cls : 'roo-upload-cropbox-selector',
26130                     type : 'file'
26131                 },
26132                 {
26133                     tag : 'div',
26134                     cls : 'roo-upload-cropbox-body',
26135                     style : 'cursor:pointer',
26136                     cn : [
26137                         {
26138                             tag : 'div',
26139                             cls : 'roo-upload-cropbox-preview'
26140                         },
26141                         {
26142                             tag : 'div',
26143                             cls : 'roo-upload-cropbox-thumb'
26144                         },
26145                         {
26146                             tag : 'div',
26147                             cls : 'roo-upload-cropbox-empty-notify',
26148                             html : this.emptyText
26149                         },
26150                         {
26151                             tag : 'div',
26152                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26153                             html : this.rotateNotify
26154                         }
26155                     ]
26156                 },
26157                 {
26158                     tag : 'div',
26159                     cls : 'roo-upload-cropbox-footer',
26160                     cn : {
26161                         tag : 'div',
26162                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26163                         cn : []
26164                     }
26165                 }
26166             ]
26167         };
26168         
26169         return cfg;
26170     },
26171     
26172     onRender : function(ct, position)
26173     {
26174         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26175         
26176         if (this.buttons.length) {
26177             
26178             Roo.each(this.buttons, function(bb) {
26179                 
26180                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26181                 
26182                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26183                 
26184             }, this);
26185         }
26186         
26187         if(this.loadMask){
26188             this.maskEl = this.el;
26189         }
26190     },
26191     
26192     initEvents : function()
26193     {
26194         this.urlAPI = (window.createObjectURL && window) || 
26195                                 (window.URL && URL.revokeObjectURL && URL) || 
26196                                 (window.webkitURL && webkitURL);
26197                         
26198         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26199         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26200         
26201         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26202         this.selectorEl.hide();
26203         
26204         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26205         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26206         
26207         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26208         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26209         this.thumbEl.hide();
26210         
26211         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26212         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26213         
26214         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26215         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26216         this.errorEl.hide();
26217         
26218         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26219         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26220         this.footerEl.hide();
26221         
26222         this.setThumbBoxSize();
26223         
26224         this.bind();
26225         
26226         this.resize();
26227         
26228         this.fireEvent('initial', this);
26229     },
26230
26231     bind : function()
26232     {
26233         var _this = this;
26234         
26235         window.addEventListener("resize", function() { _this.resize(); } );
26236         
26237         this.bodyEl.on('click', this.beforeSelectFile, this);
26238         
26239         if(Roo.isTouch){
26240             this.bodyEl.on('touchstart', this.onTouchStart, this);
26241             this.bodyEl.on('touchmove', this.onTouchMove, this);
26242             this.bodyEl.on('touchend', this.onTouchEnd, this);
26243         }
26244         
26245         if(!Roo.isTouch){
26246             this.bodyEl.on('mousedown', this.onMouseDown, this);
26247             this.bodyEl.on('mousemove', this.onMouseMove, this);
26248             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26249             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26250             Roo.get(document).on('mouseup', this.onMouseUp, this);
26251         }
26252         
26253         this.selectorEl.on('change', this.onFileSelected, this);
26254     },
26255     
26256     reset : function()
26257     {    
26258         this.scale = 0;
26259         this.baseScale = 1;
26260         this.rotate = 0;
26261         this.baseRotate = 1;
26262         this.dragable = false;
26263         this.pinching = false;
26264         this.mouseX = 0;
26265         this.mouseY = 0;
26266         this.cropData = false;
26267         this.notifyEl.dom.innerHTML = this.emptyText;
26268         
26269         this.selectorEl.dom.value = '';
26270         
26271     },
26272     
26273     resize : function()
26274     {
26275         if(this.fireEvent('resize', this) != false){
26276             this.setThumbBoxPosition();
26277             this.setCanvasPosition();
26278         }
26279     },
26280     
26281     onFooterButtonClick : function(e, el, o, type)
26282     {
26283         switch (type) {
26284             case 'rotate-left' :
26285                 this.onRotateLeft(e);
26286                 break;
26287             case 'rotate-right' :
26288                 this.onRotateRight(e);
26289                 break;
26290             case 'picture' :
26291                 this.beforeSelectFile(e);
26292                 break;
26293             case 'trash' :
26294                 this.trash(e);
26295                 break;
26296             case 'crop' :
26297                 this.crop(e);
26298                 break;
26299             case 'download' :
26300                 this.download(e);
26301                 break;
26302             default :
26303                 break;
26304         }
26305         
26306         this.fireEvent('footerbuttonclick', this, type);
26307     },
26308     
26309     beforeSelectFile : function(e)
26310     {
26311         e.preventDefault();
26312         
26313         if(this.fireEvent('beforeselectfile', this) != false){
26314             this.selectorEl.dom.click();
26315         }
26316     },
26317     
26318     onFileSelected : function(e)
26319     {
26320         e.preventDefault();
26321         
26322         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26323             return;
26324         }
26325         
26326         var file = this.selectorEl.dom.files[0];
26327         
26328         if(this.fireEvent('inspect', this, file) != false){
26329             this.prepare(file);
26330         }
26331         
26332     },
26333     
26334     trash : function(e)
26335     {
26336         this.fireEvent('trash', this);
26337     },
26338     
26339     download : function(e)
26340     {
26341         this.fireEvent('download', this);
26342     },
26343     
26344     loadCanvas : function(src)
26345     {   
26346         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26347             
26348             this.reset();
26349             
26350             this.imageEl = document.createElement('img');
26351             
26352             var _this = this;
26353             
26354             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26355             
26356             this.imageEl.src = src;
26357         }
26358     },
26359     
26360     onLoadCanvas : function()
26361     {   
26362         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26363         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26364         
26365         this.bodyEl.un('click', this.beforeSelectFile, this);
26366         
26367         this.notifyEl.hide();
26368         this.thumbEl.show();
26369         this.footerEl.show();
26370         
26371         this.baseRotateLevel();
26372         
26373         if(this.isDocument){
26374             this.setThumbBoxSize();
26375         }
26376         
26377         this.setThumbBoxPosition();
26378         
26379         this.baseScaleLevel();
26380         
26381         this.draw();
26382         
26383         this.resize();
26384         
26385         this.canvasLoaded = true;
26386         
26387         if(this.loadMask){
26388             this.maskEl.unmask();
26389         }
26390         
26391     },
26392     
26393     setCanvasPosition : function()
26394     {   
26395         if(!this.canvasEl){
26396             return;
26397         }
26398         
26399         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26400         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26401         
26402         this.previewEl.setLeft(pw);
26403         this.previewEl.setTop(ph);
26404         
26405     },
26406     
26407     onMouseDown : function(e)
26408     {   
26409         e.stopEvent();
26410         
26411         this.dragable = true;
26412         this.pinching = false;
26413         
26414         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26415             this.dragable = false;
26416             return;
26417         }
26418         
26419         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26420         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26421         
26422     },
26423     
26424     onMouseMove : function(e)
26425     {   
26426         e.stopEvent();
26427         
26428         if(!this.canvasLoaded){
26429             return;
26430         }
26431         
26432         if (!this.dragable){
26433             return;
26434         }
26435         
26436         var minX = Math.ceil(this.thumbEl.getLeft(true));
26437         var minY = Math.ceil(this.thumbEl.getTop(true));
26438         
26439         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26440         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26441         
26442         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26443         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26444         
26445         x = x - this.mouseX;
26446         y = y - this.mouseY;
26447         
26448         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26449         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26450         
26451         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26452         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26453         
26454         this.previewEl.setLeft(bgX);
26455         this.previewEl.setTop(bgY);
26456         
26457         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26458         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26459     },
26460     
26461     onMouseUp : function(e)
26462     {   
26463         e.stopEvent();
26464         
26465         this.dragable = false;
26466     },
26467     
26468     onMouseWheel : function(e)
26469     {   
26470         e.stopEvent();
26471         
26472         this.startScale = this.scale;
26473         
26474         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26475         
26476         if(!this.zoomable()){
26477             this.scale = this.startScale;
26478             return;
26479         }
26480         
26481         this.draw();
26482         
26483         return;
26484     },
26485     
26486     zoomable : function()
26487     {
26488         var minScale = this.thumbEl.getWidth() / this.minWidth;
26489         
26490         if(this.minWidth < this.minHeight){
26491             minScale = this.thumbEl.getHeight() / this.minHeight;
26492         }
26493         
26494         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26495         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26496         
26497         if(
26498                 this.isDocument &&
26499                 (this.rotate == 0 || this.rotate == 180) && 
26500                 (
26501                     width > this.imageEl.OriginWidth || 
26502                     height > this.imageEl.OriginHeight ||
26503                     (width < this.minWidth && height < this.minHeight)
26504                 )
26505         ){
26506             return false;
26507         }
26508         
26509         if(
26510                 this.isDocument &&
26511                 (this.rotate == 90 || this.rotate == 270) && 
26512                 (
26513                     width > this.imageEl.OriginWidth || 
26514                     height > this.imageEl.OriginHeight ||
26515                     (width < this.minHeight && height < this.minWidth)
26516                 )
26517         ){
26518             return false;
26519         }
26520         
26521         if(
26522                 !this.isDocument &&
26523                 (this.rotate == 0 || this.rotate == 180) && 
26524                 (
26525                     width < this.minWidth || 
26526                     width > this.imageEl.OriginWidth || 
26527                     height < this.minHeight || 
26528                     height > this.imageEl.OriginHeight
26529                 )
26530         ){
26531             return false;
26532         }
26533         
26534         if(
26535                 !this.isDocument &&
26536                 (this.rotate == 90 || this.rotate == 270) && 
26537                 (
26538                     width < this.minHeight || 
26539                     width > this.imageEl.OriginWidth || 
26540                     height < this.minWidth || 
26541                     height > this.imageEl.OriginHeight
26542                 )
26543         ){
26544             return false;
26545         }
26546         
26547         return true;
26548         
26549     },
26550     
26551     onRotateLeft : function(e)
26552     {   
26553         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26554             
26555             var minScale = this.thumbEl.getWidth() / this.minWidth;
26556             
26557             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26558             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26559             
26560             this.startScale = this.scale;
26561             
26562             while (this.getScaleLevel() < minScale){
26563             
26564                 this.scale = this.scale + 1;
26565                 
26566                 if(!this.zoomable()){
26567                     break;
26568                 }
26569                 
26570                 if(
26571                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26572                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26573                 ){
26574                     continue;
26575                 }
26576                 
26577                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26578
26579                 this.draw();
26580                 
26581                 return;
26582             }
26583             
26584             this.scale = this.startScale;
26585             
26586             this.onRotateFail();
26587             
26588             return false;
26589         }
26590         
26591         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26592
26593         if(this.isDocument){
26594             this.setThumbBoxSize();
26595             this.setThumbBoxPosition();
26596             this.setCanvasPosition();
26597         }
26598         
26599         this.draw();
26600         
26601         this.fireEvent('rotate', this, 'left');
26602         
26603     },
26604     
26605     onRotateRight : function(e)
26606     {
26607         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26608             
26609             var minScale = this.thumbEl.getWidth() / this.minWidth;
26610         
26611             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26612             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26613             
26614             this.startScale = this.scale;
26615             
26616             while (this.getScaleLevel() < minScale){
26617             
26618                 this.scale = this.scale + 1;
26619                 
26620                 if(!this.zoomable()){
26621                     break;
26622                 }
26623                 
26624                 if(
26625                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26626                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26627                 ){
26628                     continue;
26629                 }
26630                 
26631                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26632
26633                 this.draw();
26634                 
26635                 return;
26636             }
26637             
26638             this.scale = this.startScale;
26639             
26640             this.onRotateFail();
26641             
26642             return false;
26643         }
26644         
26645         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26646
26647         if(this.isDocument){
26648             this.setThumbBoxSize();
26649             this.setThumbBoxPosition();
26650             this.setCanvasPosition();
26651         }
26652         
26653         this.draw();
26654         
26655         this.fireEvent('rotate', this, 'right');
26656     },
26657     
26658     onRotateFail : function()
26659     {
26660         this.errorEl.show(true);
26661         
26662         var _this = this;
26663         
26664         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26665     },
26666     
26667     draw : function()
26668     {
26669         this.previewEl.dom.innerHTML = '';
26670         
26671         var canvasEl = document.createElement("canvas");
26672         
26673         var contextEl = canvasEl.getContext("2d");
26674         
26675         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26676         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26677         var center = this.imageEl.OriginWidth / 2;
26678         
26679         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26680             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26681             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26682             center = this.imageEl.OriginHeight / 2;
26683         }
26684         
26685         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26686         
26687         contextEl.translate(center, center);
26688         contextEl.rotate(this.rotate * Math.PI / 180);
26689
26690         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26691         
26692         this.canvasEl = document.createElement("canvas");
26693         
26694         this.contextEl = this.canvasEl.getContext("2d");
26695         
26696         switch (this.rotate) {
26697             case 0 :
26698                 
26699                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26700                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26701                 
26702                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26703                 
26704                 break;
26705             case 90 : 
26706                 
26707                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26708                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26709                 
26710                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26711                     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);
26712                     break;
26713                 }
26714                 
26715                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26716                 
26717                 break;
26718             case 180 :
26719                 
26720                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26721                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26722                 
26723                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26724                     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);
26725                     break;
26726                 }
26727                 
26728                 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);
26729                 
26730                 break;
26731             case 270 :
26732                 
26733                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26734                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26735         
26736                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26737                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26738                     break;
26739                 }
26740                 
26741                 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);
26742                 
26743                 break;
26744             default : 
26745                 break;
26746         }
26747         
26748         this.previewEl.appendChild(this.canvasEl);
26749         
26750         this.setCanvasPosition();
26751     },
26752     
26753     crop : function()
26754     {
26755         if(!this.canvasLoaded){
26756             return;
26757         }
26758         
26759         var imageCanvas = document.createElement("canvas");
26760         
26761         var imageContext = imageCanvas.getContext("2d");
26762         
26763         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26764         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26765         
26766         var center = imageCanvas.width / 2;
26767         
26768         imageContext.translate(center, center);
26769         
26770         imageContext.rotate(this.rotate * Math.PI / 180);
26771         
26772         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26773         
26774         var canvas = document.createElement("canvas");
26775         
26776         var context = canvas.getContext("2d");
26777                 
26778         canvas.width = this.minWidth;
26779         canvas.height = this.minHeight;
26780
26781         switch (this.rotate) {
26782             case 0 :
26783                 
26784                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26785                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26786                 
26787                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26788                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26789                 
26790                 var targetWidth = this.minWidth - 2 * x;
26791                 var targetHeight = this.minHeight - 2 * y;
26792                 
26793                 var scale = 1;
26794                 
26795                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26796                     scale = targetWidth / width;
26797                 }
26798                 
26799                 if(x > 0 && y == 0){
26800                     scale = targetHeight / height;
26801                 }
26802                 
26803                 if(x > 0 && y > 0){
26804                     scale = targetWidth / width;
26805                     
26806                     if(width < height){
26807                         scale = targetHeight / height;
26808                     }
26809                 }
26810                 
26811                 context.scale(scale, scale);
26812                 
26813                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26814                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26815
26816                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26817                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26818
26819                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26820                 
26821                 break;
26822             case 90 : 
26823                 
26824                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26825                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26826                 
26827                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26828                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26829                 
26830                 var targetWidth = this.minWidth - 2 * x;
26831                 var targetHeight = this.minHeight - 2 * y;
26832                 
26833                 var scale = 1;
26834                 
26835                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26836                     scale = targetWidth / width;
26837                 }
26838                 
26839                 if(x > 0 && y == 0){
26840                     scale = targetHeight / height;
26841                 }
26842                 
26843                 if(x > 0 && y > 0){
26844                     scale = targetWidth / width;
26845                     
26846                     if(width < height){
26847                         scale = targetHeight / height;
26848                     }
26849                 }
26850                 
26851                 context.scale(scale, scale);
26852                 
26853                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26854                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26855
26856                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26857                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26858                 
26859                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26860                 
26861                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26862                 
26863                 break;
26864             case 180 :
26865                 
26866                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26867                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26868                 
26869                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26870                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26871                 
26872                 var targetWidth = this.minWidth - 2 * x;
26873                 var targetHeight = this.minHeight - 2 * y;
26874                 
26875                 var scale = 1;
26876                 
26877                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26878                     scale = targetWidth / width;
26879                 }
26880                 
26881                 if(x > 0 && y == 0){
26882                     scale = targetHeight / height;
26883                 }
26884                 
26885                 if(x > 0 && y > 0){
26886                     scale = targetWidth / width;
26887                     
26888                     if(width < height){
26889                         scale = targetHeight / height;
26890                     }
26891                 }
26892                 
26893                 context.scale(scale, scale);
26894                 
26895                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26896                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26897
26898                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26899                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26900
26901                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26902                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26903                 
26904                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26905                 
26906                 break;
26907             case 270 :
26908                 
26909                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26910                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26911                 
26912                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26913                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26914                 
26915                 var targetWidth = this.minWidth - 2 * x;
26916                 var targetHeight = this.minHeight - 2 * y;
26917                 
26918                 var scale = 1;
26919                 
26920                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26921                     scale = targetWidth / width;
26922                 }
26923                 
26924                 if(x > 0 && y == 0){
26925                     scale = targetHeight / height;
26926                 }
26927                 
26928                 if(x > 0 && y > 0){
26929                     scale = targetWidth / width;
26930                     
26931                     if(width < height){
26932                         scale = targetHeight / height;
26933                     }
26934                 }
26935                 
26936                 context.scale(scale, scale);
26937                 
26938                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26939                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26940
26941                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26942                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26943                 
26944                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26945                 
26946                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26947                 
26948                 break;
26949             default : 
26950                 break;
26951         }
26952         
26953         this.cropData = canvas.toDataURL(this.cropType);
26954         
26955         if(this.fireEvent('crop', this, this.cropData) !== false){
26956             this.process(this.file, this.cropData);
26957         }
26958         
26959         return;
26960         
26961     },
26962     
26963     setThumbBoxSize : function()
26964     {
26965         var width, height;
26966         
26967         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26968             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26969             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26970             
26971             this.minWidth = width;
26972             this.minHeight = height;
26973             
26974             if(this.rotate == 90 || this.rotate == 270){
26975                 this.minWidth = height;
26976                 this.minHeight = width;
26977             }
26978         }
26979         
26980         height = 300;
26981         width = Math.ceil(this.minWidth * height / this.minHeight);
26982         
26983         if(this.minWidth > this.minHeight){
26984             width = 300;
26985             height = Math.ceil(this.minHeight * width / this.minWidth);
26986         }
26987         
26988         this.thumbEl.setStyle({
26989             width : width + 'px',
26990             height : height + 'px'
26991         });
26992
26993         return;
26994             
26995     },
26996     
26997     setThumbBoxPosition : function()
26998     {
26999         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27000         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27001         
27002         this.thumbEl.setLeft(x);
27003         this.thumbEl.setTop(y);
27004         
27005     },
27006     
27007     baseRotateLevel : function()
27008     {
27009         this.baseRotate = 1;
27010         
27011         if(
27012                 typeof(this.exif) != 'undefined' &&
27013                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27014                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27015         ){
27016             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27017         }
27018         
27019         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27020         
27021     },
27022     
27023     baseScaleLevel : function()
27024     {
27025         var width, height;
27026         
27027         if(this.isDocument){
27028             
27029             if(this.baseRotate == 6 || this.baseRotate == 8){
27030             
27031                 height = this.thumbEl.getHeight();
27032                 this.baseScale = height / this.imageEl.OriginWidth;
27033
27034                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27035                     width = this.thumbEl.getWidth();
27036                     this.baseScale = width / this.imageEl.OriginHeight;
27037                 }
27038
27039                 return;
27040             }
27041
27042             height = this.thumbEl.getHeight();
27043             this.baseScale = height / this.imageEl.OriginHeight;
27044
27045             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27046                 width = this.thumbEl.getWidth();
27047                 this.baseScale = width / this.imageEl.OriginWidth;
27048             }
27049
27050             return;
27051         }
27052         
27053         if(this.baseRotate == 6 || this.baseRotate == 8){
27054             
27055             width = this.thumbEl.getHeight();
27056             this.baseScale = width / this.imageEl.OriginHeight;
27057             
27058             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27059                 height = this.thumbEl.getWidth();
27060                 this.baseScale = height / this.imageEl.OriginHeight;
27061             }
27062             
27063             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27064                 height = this.thumbEl.getWidth();
27065                 this.baseScale = height / this.imageEl.OriginHeight;
27066                 
27067                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27068                     width = this.thumbEl.getHeight();
27069                     this.baseScale = width / this.imageEl.OriginWidth;
27070                 }
27071             }
27072             
27073             return;
27074         }
27075         
27076         width = this.thumbEl.getWidth();
27077         this.baseScale = width / this.imageEl.OriginWidth;
27078         
27079         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27080             height = this.thumbEl.getHeight();
27081             this.baseScale = height / this.imageEl.OriginHeight;
27082         }
27083         
27084         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27085             
27086             height = this.thumbEl.getHeight();
27087             this.baseScale = height / this.imageEl.OriginHeight;
27088             
27089             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27090                 width = this.thumbEl.getWidth();
27091                 this.baseScale = width / this.imageEl.OriginWidth;
27092             }
27093             
27094         }
27095         
27096         return;
27097     },
27098     
27099     getScaleLevel : function()
27100     {
27101         return this.baseScale * Math.pow(1.1, this.scale);
27102     },
27103     
27104     onTouchStart : function(e)
27105     {
27106         if(!this.canvasLoaded){
27107             this.beforeSelectFile(e);
27108             return;
27109         }
27110         
27111         var touches = e.browserEvent.touches;
27112         
27113         if(!touches){
27114             return;
27115         }
27116         
27117         if(touches.length == 1){
27118             this.onMouseDown(e);
27119             return;
27120         }
27121         
27122         if(touches.length != 2){
27123             return;
27124         }
27125         
27126         var coords = [];
27127         
27128         for(var i = 0, finger; finger = touches[i]; i++){
27129             coords.push(finger.pageX, finger.pageY);
27130         }
27131         
27132         var x = Math.pow(coords[0] - coords[2], 2);
27133         var y = Math.pow(coords[1] - coords[3], 2);
27134         
27135         this.startDistance = Math.sqrt(x + y);
27136         
27137         this.startScale = this.scale;
27138         
27139         this.pinching = true;
27140         this.dragable = false;
27141         
27142     },
27143     
27144     onTouchMove : function(e)
27145     {
27146         if(!this.pinching && !this.dragable){
27147             return;
27148         }
27149         
27150         var touches = e.browserEvent.touches;
27151         
27152         if(!touches){
27153             return;
27154         }
27155         
27156         if(this.dragable){
27157             this.onMouseMove(e);
27158             return;
27159         }
27160         
27161         var coords = [];
27162         
27163         for(var i = 0, finger; finger = touches[i]; i++){
27164             coords.push(finger.pageX, finger.pageY);
27165         }
27166         
27167         var x = Math.pow(coords[0] - coords[2], 2);
27168         var y = Math.pow(coords[1] - coords[3], 2);
27169         
27170         this.endDistance = Math.sqrt(x + y);
27171         
27172         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27173         
27174         if(!this.zoomable()){
27175             this.scale = this.startScale;
27176             return;
27177         }
27178         
27179         this.draw();
27180         
27181     },
27182     
27183     onTouchEnd : function(e)
27184     {
27185         this.pinching = false;
27186         this.dragable = false;
27187         
27188     },
27189     
27190     process : function(file, crop)
27191     {
27192         if(this.loadMask){
27193             this.maskEl.mask(this.loadingText);
27194         }
27195         
27196         this.xhr = new XMLHttpRequest();
27197         
27198         file.xhr = this.xhr;
27199
27200         this.xhr.open(this.method, this.url, true);
27201         
27202         var headers = {
27203             "Accept": "application/json",
27204             "Cache-Control": "no-cache",
27205             "X-Requested-With": "XMLHttpRequest"
27206         };
27207         
27208         for (var headerName in headers) {
27209             var headerValue = headers[headerName];
27210             if (headerValue) {
27211                 this.xhr.setRequestHeader(headerName, headerValue);
27212             }
27213         }
27214         
27215         var _this = this;
27216         
27217         this.xhr.onload = function()
27218         {
27219             _this.xhrOnLoad(_this.xhr);
27220         }
27221         
27222         this.xhr.onerror = function()
27223         {
27224             _this.xhrOnError(_this.xhr);
27225         }
27226         
27227         var formData = new FormData();
27228
27229         formData.append('returnHTML', 'NO');
27230         
27231         if(crop){
27232             formData.append('crop', crop);
27233         }
27234         
27235         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27236             formData.append(this.paramName, file, file.name);
27237         }
27238         
27239         if(typeof(file.filename) != 'undefined'){
27240             formData.append('filename', file.filename);
27241         }
27242         
27243         if(typeof(file.mimetype) != 'undefined'){
27244             formData.append('mimetype', file.mimetype);
27245         }
27246         
27247         if(this.fireEvent('arrange', this, formData) != false){
27248             this.xhr.send(formData);
27249         };
27250     },
27251     
27252     xhrOnLoad : function(xhr)
27253     {
27254         if(this.loadMask){
27255             this.maskEl.unmask();
27256         }
27257         
27258         if (xhr.readyState !== 4) {
27259             this.fireEvent('exception', this, xhr);
27260             return;
27261         }
27262
27263         var response = Roo.decode(xhr.responseText);
27264         
27265         if(!response.success){
27266             this.fireEvent('exception', this, xhr);
27267             return;
27268         }
27269         
27270         var response = Roo.decode(xhr.responseText);
27271         
27272         this.fireEvent('upload', this, response);
27273         
27274     },
27275     
27276     xhrOnError : function()
27277     {
27278         if(this.loadMask){
27279             this.maskEl.unmask();
27280         }
27281         
27282         Roo.log('xhr on error');
27283         
27284         var response = Roo.decode(xhr.responseText);
27285           
27286         Roo.log(response);
27287         
27288     },
27289     
27290     prepare : function(file)
27291     {   
27292         if(this.loadMask){
27293             this.maskEl.mask(this.loadingText);
27294         }
27295         
27296         this.file = false;
27297         this.exif = {};
27298         
27299         if(typeof(file) === 'string'){
27300             this.loadCanvas(file);
27301             return;
27302         }
27303         
27304         if(!file || !this.urlAPI){
27305             return;
27306         }
27307         
27308         this.file = file;
27309         this.cropType = file.type;
27310         
27311         var _this = this;
27312         
27313         if(this.fireEvent('prepare', this, this.file) != false){
27314             
27315             var reader = new FileReader();
27316             
27317             reader.onload = function (e) {
27318                 if (e.target.error) {
27319                     Roo.log(e.target.error);
27320                     return;
27321                 }
27322                 
27323                 var buffer = e.target.result,
27324                     dataView = new DataView(buffer),
27325                     offset = 2,
27326                     maxOffset = dataView.byteLength - 4,
27327                     markerBytes,
27328                     markerLength;
27329                 
27330                 if (dataView.getUint16(0) === 0xffd8) {
27331                     while (offset < maxOffset) {
27332                         markerBytes = dataView.getUint16(offset);
27333                         
27334                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27335                             markerLength = dataView.getUint16(offset + 2) + 2;
27336                             if (offset + markerLength > dataView.byteLength) {
27337                                 Roo.log('Invalid meta data: Invalid segment size.');
27338                                 break;
27339                             }
27340                             
27341                             if(markerBytes == 0xffe1){
27342                                 _this.parseExifData(
27343                                     dataView,
27344                                     offset,
27345                                     markerLength
27346                                 );
27347                             }
27348                             
27349                             offset += markerLength;
27350                             
27351                             continue;
27352                         }
27353                         
27354                         break;
27355                     }
27356                     
27357                 }
27358                 
27359                 var url = _this.urlAPI.createObjectURL(_this.file);
27360                 
27361                 _this.loadCanvas(url);
27362                 
27363                 return;
27364             }
27365             
27366             reader.readAsArrayBuffer(this.file);
27367             
27368         }
27369         
27370     },
27371     
27372     parseExifData : function(dataView, offset, length)
27373     {
27374         var tiffOffset = offset + 10,
27375             littleEndian,
27376             dirOffset;
27377     
27378         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27379             // No Exif data, might be XMP data instead
27380             return;
27381         }
27382         
27383         // Check for the ASCII code for "Exif" (0x45786966):
27384         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27385             // No Exif data, might be XMP data instead
27386             return;
27387         }
27388         if (tiffOffset + 8 > dataView.byteLength) {
27389             Roo.log('Invalid Exif data: Invalid segment size.');
27390             return;
27391         }
27392         // Check for the two null bytes:
27393         if (dataView.getUint16(offset + 8) !== 0x0000) {
27394             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27395             return;
27396         }
27397         // Check the byte alignment:
27398         switch (dataView.getUint16(tiffOffset)) {
27399         case 0x4949:
27400             littleEndian = true;
27401             break;
27402         case 0x4D4D:
27403             littleEndian = false;
27404             break;
27405         default:
27406             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27407             return;
27408         }
27409         // Check for the TIFF tag marker (0x002A):
27410         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27411             Roo.log('Invalid Exif data: Missing TIFF marker.');
27412             return;
27413         }
27414         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27415         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27416         
27417         this.parseExifTags(
27418             dataView,
27419             tiffOffset,
27420             tiffOffset + dirOffset,
27421             littleEndian
27422         );
27423     },
27424     
27425     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27426     {
27427         var tagsNumber,
27428             dirEndOffset,
27429             i;
27430         if (dirOffset + 6 > dataView.byteLength) {
27431             Roo.log('Invalid Exif data: Invalid directory offset.');
27432             return;
27433         }
27434         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27435         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27436         if (dirEndOffset + 4 > dataView.byteLength) {
27437             Roo.log('Invalid Exif data: Invalid directory size.');
27438             return;
27439         }
27440         for (i = 0; i < tagsNumber; i += 1) {
27441             this.parseExifTag(
27442                 dataView,
27443                 tiffOffset,
27444                 dirOffset + 2 + 12 * i, // tag offset
27445                 littleEndian
27446             );
27447         }
27448         // Return the offset to the next directory:
27449         return dataView.getUint32(dirEndOffset, littleEndian);
27450     },
27451     
27452     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27453     {
27454         var tag = dataView.getUint16(offset, littleEndian);
27455         
27456         this.exif[tag] = this.getExifValue(
27457             dataView,
27458             tiffOffset,
27459             offset,
27460             dataView.getUint16(offset + 2, littleEndian), // tag type
27461             dataView.getUint32(offset + 4, littleEndian), // tag length
27462             littleEndian
27463         );
27464     },
27465     
27466     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27467     {
27468         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27469             tagSize,
27470             dataOffset,
27471             values,
27472             i,
27473             str,
27474             c;
27475     
27476         if (!tagType) {
27477             Roo.log('Invalid Exif data: Invalid tag type.');
27478             return;
27479         }
27480         
27481         tagSize = tagType.size * length;
27482         // Determine if the value is contained in the dataOffset bytes,
27483         // or if the value at the dataOffset is a pointer to the actual data:
27484         dataOffset = tagSize > 4 ?
27485                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27486         if (dataOffset + tagSize > dataView.byteLength) {
27487             Roo.log('Invalid Exif data: Invalid data offset.');
27488             return;
27489         }
27490         if (length === 1) {
27491             return tagType.getValue(dataView, dataOffset, littleEndian);
27492         }
27493         values = [];
27494         for (i = 0; i < length; i += 1) {
27495             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27496         }
27497         
27498         if (tagType.ascii) {
27499             str = '';
27500             // Concatenate the chars:
27501             for (i = 0; i < values.length; i += 1) {
27502                 c = values[i];
27503                 // Ignore the terminating NULL byte(s):
27504                 if (c === '\u0000') {
27505                     break;
27506                 }
27507                 str += c;
27508             }
27509             return str;
27510         }
27511         return values;
27512     }
27513     
27514 });
27515
27516 Roo.apply(Roo.bootstrap.UploadCropbox, {
27517     tags : {
27518         'Orientation': 0x0112
27519     },
27520     
27521     Orientation: {
27522             1: 0, //'top-left',
27523 //            2: 'top-right',
27524             3: 180, //'bottom-right',
27525 //            4: 'bottom-left',
27526 //            5: 'left-top',
27527             6: 90, //'right-top',
27528 //            7: 'right-bottom',
27529             8: 270 //'left-bottom'
27530     },
27531     
27532     exifTagTypes : {
27533         // byte, 8-bit unsigned int:
27534         1: {
27535             getValue: function (dataView, dataOffset) {
27536                 return dataView.getUint8(dataOffset);
27537             },
27538             size: 1
27539         },
27540         // ascii, 8-bit byte:
27541         2: {
27542             getValue: function (dataView, dataOffset) {
27543                 return String.fromCharCode(dataView.getUint8(dataOffset));
27544             },
27545             size: 1,
27546             ascii: true
27547         },
27548         // short, 16 bit int:
27549         3: {
27550             getValue: function (dataView, dataOffset, littleEndian) {
27551                 return dataView.getUint16(dataOffset, littleEndian);
27552             },
27553             size: 2
27554         },
27555         // long, 32 bit int:
27556         4: {
27557             getValue: function (dataView, dataOffset, littleEndian) {
27558                 return dataView.getUint32(dataOffset, littleEndian);
27559             },
27560             size: 4
27561         },
27562         // rational = two long values, first is numerator, second is denominator:
27563         5: {
27564             getValue: function (dataView, dataOffset, littleEndian) {
27565                 return dataView.getUint32(dataOffset, littleEndian) /
27566                     dataView.getUint32(dataOffset + 4, littleEndian);
27567             },
27568             size: 8
27569         },
27570         // slong, 32 bit signed int:
27571         9: {
27572             getValue: function (dataView, dataOffset, littleEndian) {
27573                 return dataView.getInt32(dataOffset, littleEndian);
27574             },
27575             size: 4
27576         },
27577         // srational, two slongs, first is numerator, second is denominator:
27578         10: {
27579             getValue: function (dataView, dataOffset, littleEndian) {
27580                 return dataView.getInt32(dataOffset, littleEndian) /
27581                     dataView.getInt32(dataOffset + 4, littleEndian);
27582             },
27583             size: 8
27584         }
27585     },
27586     
27587     footer : {
27588         STANDARD : [
27589             {
27590                 tag : 'div',
27591                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27592                 action : 'rotate-left',
27593                 cn : [
27594                     {
27595                         tag : 'button',
27596                         cls : 'btn btn-default',
27597                         html : '<i class="fa fa-undo"></i>'
27598                     }
27599                 ]
27600             },
27601             {
27602                 tag : 'div',
27603                 cls : 'btn-group roo-upload-cropbox-picture',
27604                 action : 'picture',
27605                 cn : [
27606                     {
27607                         tag : 'button',
27608                         cls : 'btn btn-default',
27609                         html : '<i class="fa fa-picture-o"></i>'
27610                     }
27611                 ]
27612             },
27613             {
27614                 tag : 'div',
27615                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27616                 action : 'rotate-right',
27617                 cn : [
27618                     {
27619                         tag : 'button',
27620                         cls : 'btn btn-default',
27621                         html : '<i class="fa fa-repeat"></i>'
27622                     }
27623                 ]
27624             }
27625         ],
27626         DOCUMENT : [
27627             {
27628                 tag : 'div',
27629                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27630                 action : 'rotate-left',
27631                 cn : [
27632                     {
27633                         tag : 'button',
27634                         cls : 'btn btn-default',
27635                         html : '<i class="fa fa-undo"></i>'
27636                     }
27637                 ]
27638             },
27639             {
27640                 tag : 'div',
27641                 cls : 'btn-group roo-upload-cropbox-download',
27642                 action : 'download',
27643                 cn : [
27644                     {
27645                         tag : 'button',
27646                         cls : 'btn btn-default',
27647                         html : '<i class="fa fa-download"></i>'
27648                     }
27649                 ]
27650             },
27651             {
27652                 tag : 'div',
27653                 cls : 'btn-group roo-upload-cropbox-crop',
27654                 action : 'crop',
27655                 cn : [
27656                     {
27657                         tag : 'button',
27658                         cls : 'btn btn-default',
27659                         html : '<i class="fa fa-crop"></i>'
27660                     }
27661                 ]
27662             },
27663             {
27664                 tag : 'div',
27665                 cls : 'btn-group roo-upload-cropbox-trash',
27666                 action : 'trash',
27667                 cn : [
27668                     {
27669                         tag : 'button',
27670                         cls : 'btn btn-default',
27671                         html : '<i class="fa fa-trash"></i>'
27672                     }
27673                 ]
27674             },
27675             {
27676                 tag : 'div',
27677                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27678                 action : 'rotate-right',
27679                 cn : [
27680                     {
27681                         tag : 'button',
27682                         cls : 'btn btn-default',
27683                         html : '<i class="fa fa-repeat"></i>'
27684                     }
27685                 ]
27686             }
27687         ],
27688         ROTATOR : [
27689             {
27690                 tag : 'div',
27691                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27692                 action : 'rotate-left',
27693                 cn : [
27694                     {
27695                         tag : 'button',
27696                         cls : 'btn btn-default',
27697                         html : '<i class="fa fa-undo"></i>'
27698                     }
27699                 ]
27700             },
27701             {
27702                 tag : 'div',
27703                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27704                 action : 'rotate-right',
27705                 cn : [
27706                     {
27707                         tag : 'button',
27708                         cls : 'btn btn-default',
27709                         html : '<i class="fa fa-repeat"></i>'
27710                     }
27711                 ]
27712             }
27713         ]
27714     }
27715 });
27716
27717 /*
27718 * Licence: LGPL
27719 */
27720
27721 /**
27722  * @class Roo.bootstrap.DocumentManager
27723  * @extends Roo.bootstrap.Component
27724  * Bootstrap DocumentManager class
27725  * @cfg {String} paramName default 'imageUpload'
27726  * @cfg {String} toolTipName default 'filename'
27727  * @cfg {String} method default POST
27728  * @cfg {String} url action url
27729  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27730  * @cfg {Boolean} multiple multiple upload default true
27731  * @cfg {Number} thumbSize default 300
27732  * @cfg {String} fieldLabel
27733  * @cfg {Number} labelWidth default 4
27734  * @cfg {String} labelAlign (left|top) default left
27735  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27736 * @cfg {Number} labellg set the width of label (1-12)
27737  * @cfg {Number} labelmd set the width of label (1-12)
27738  * @cfg {Number} labelsm set the width of label (1-12)
27739  * @cfg {Number} labelxs set the width of label (1-12)
27740  * 
27741  * @constructor
27742  * Create a new DocumentManager
27743  * @param {Object} config The config object
27744  */
27745
27746 Roo.bootstrap.DocumentManager = function(config){
27747     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27748     
27749     this.files = [];
27750     this.delegates = [];
27751     
27752     this.addEvents({
27753         /**
27754          * @event initial
27755          * Fire when initial the DocumentManager
27756          * @param {Roo.bootstrap.DocumentManager} this
27757          */
27758         "initial" : true,
27759         /**
27760          * @event inspect
27761          * inspect selected file
27762          * @param {Roo.bootstrap.DocumentManager} this
27763          * @param {File} file
27764          */
27765         "inspect" : true,
27766         /**
27767          * @event exception
27768          * Fire when xhr load exception
27769          * @param {Roo.bootstrap.DocumentManager} this
27770          * @param {XMLHttpRequest} xhr
27771          */
27772         "exception" : true,
27773         /**
27774          * @event afterupload
27775          * Fire when xhr load exception
27776          * @param {Roo.bootstrap.DocumentManager} this
27777          * @param {XMLHttpRequest} xhr
27778          */
27779         "afterupload" : true,
27780         /**
27781          * @event prepare
27782          * prepare the form data
27783          * @param {Roo.bootstrap.DocumentManager} this
27784          * @param {Object} formData
27785          */
27786         "prepare" : true,
27787         /**
27788          * @event remove
27789          * Fire when remove the file
27790          * @param {Roo.bootstrap.DocumentManager} this
27791          * @param {Object} file
27792          */
27793         "remove" : true,
27794         /**
27795          * @event refresh
27796          * Fire after refresh the file
27797          * @param {Roo.bootstrap.DocumentManager} this
27798          */
27799         "refresh" : true,
27800         /**
27801          * @event click
27802          * Fire after click the image
27803          * @param {Roo.bootstrap.DocumentManager} this
27804          * @param {Object} file
27805          */
27806         "click" : true,
27807         /**
27808          * @event edit
27809          * Fire when upload a image and editable set to true
27810          * @param {Roo.bootstrap.DocumentManager} this
27811          * @param {Object} file
27812          */
27813         "edit" : true,
27814         /**
27815          * @event beforeselectfile
27816          * Fire before select file
27817          * @param {Roo.bootstrap.DocumentManager} this
27818          */
27819         "beforeselectfile" : true,
27820         /**
27821          * @event process
27822          * Fire before process file
27823          * @param {Roo.bootstrap.DocumentManager} this
27824          * @param {Object} file
27825          */
27826         "process" : true
27827         
27828     });
27829 };
27830
27831 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27832     
27833     boxes : 0,
27834     inputName : '',
27835     thumbSize : 300,
27836     multiple : true,
27837     files : false,
27838     method : 'POST',
27839     url : '',
27840     paramName : 'imageUpload',
27841     toolTipName : 'filename',
27842     fieldLabel : '',
27843     labelWidth : 4,
27844     labelAlign : 'left',
27845     editable : true,
27846     delegates : false,
27847     xhr : false, 
27848     
27849     labellg : 0,
27850     labelmd : 0,
27851     labelsm : 0,
27852     labelxs : 0,
27853     
27854     getAutoCreate : function()
27855     {   
27856         var managerWidget = {
27857             tag : 'div',
27858             cls : 'roo-document-manager',
27859             cn : [
27860                 {
27861                     tag : 'input',
27862                     cls : 'roo-document-manager-selector',
27863                     type : 'file'
27864                 },
27865                 {
27866                     tag : 'div',
27867                     cls : 'roo-document-manager-uploader',
27868                     cn : [
27869                         {
27870                             tag : 'div',
27871                             cls : 'roo-document-manager-upload-btn',
27872                             html : '<i class="fa fa-plus"></i>'
27873                         }
27874                     ]
27875                     
27876                 }
27877             ]
27878         };
27879         
27880         var content = [
27881             {
27882                 tag : 'div',
27883                 cls : 'column col-md-12',
27884                 cn : managerWidget
27885             }
27886         ];
27887         
27888         if(this.fieldLabel.length){
27889             
27890             content = [
27891                 {
27892                     tag : 'div',
27893                     cls : 'column col-md-12',
27894                     html : this.fieldLabel
27895                 },
27896                 {
27897                     tag : 'div',
27898                     cls : 'column col-md-12',
27899                     cn : managerWidget
27900                 }
27901             ];
27902
27903             if(this.labelAlign == 'left'){
27904                 content = [
27905                     {
27906                         tag : 'div',
27907                         cls : 'column',
27908                         html : this.fieldLabel
27909                     },
27910                     {
27911                         tag : 'div',
27912                         cls : 'column',
27913                         cn : managerWidget
27914                     }
27915                 ];
27916                 
27917                 if(this.labelWidth > 12){
27918                     content[0].style = "width: " + this.labelWidth + 'px';
27919                 }
27920
27921                 if(this.labelWidth < 13 && this.labelmd == 0){
27922                     this.labelmd = this.labelWidth;
27923                 }
27924
27925                 if(this.labellg > 0){
27926                     content[0].cls += ' col-lg-' + this.labellg;
27927                     content[1].cls += ' col-lg-' + (12 - this.labellg);
27928                 }
27929
27930                 if(this.labelmd > 0){
27931                     content[0].cls += ' col-md-' + this.labelmd;
27932                     content[1].cls += ' col-md-' + (12 - this.labelmd);
27933                 }
27934
27935                 if(this.labelsm > 0){
27936                     content[0].cls += ' col-sm-' + this.labelsm;
27937                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
27938                 }
27939
27940                 if(this.labelxs > 0){
27941                     content[0].cls += ' col-xs-' + this.labelxs;
27942                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
27943                 }
27944                 
27945             }
27946         }
27947         
27948         var cfg = {
27949             tag : 'div',
27950             cls : 'row clearfix',
27951             cn : content
27952         };
27953         
27954         return cfg;
27955         
27956     },
27957     
27958     initEvents : function()
27959     {
27960         this.managerEl = this.el.select('.roo-document-manager', true).first();
27961         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27962         
27963         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27964         this.selectorEl.hide();
27965         
27966         if(this.multiple){
27967             this.selectorEl.attr('multiple', 'multiple');
27968         }
27969         
27970         this.selectorEl.on('change', this.onFileSelected, this);
27971         
27972         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27973         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27974         
27975         this.uploader.on('click', this.onUploaderClick, this);
27976         
27977         this.renderProgressDialog();
27978         
27979         var _this = this;
27980         
27981         window.addEventListener("resize", function() { _this.refresh(); } );
27982         
27983         this.fireEvent('initial', this);
27984     },
27985     
27986     renderProgressDialog : function()
27987     {
27988         var _this = this;
27989         
27990         this.progressDialog = new Roo.bootstrap.Modal({
27991             cls : 'roo-document-manager-progress-dialog',
27992             allow_close : false,
27993             title : '',
27994             buttons : [
27995                 {
27996                     name  :'cancel',
27997                     weight : 'danger',
27998                     html : 'Cancel'
27999                 }
28000             ], 
28001             listeners : { 
28002                 btnclick : function() {
28003                     _this.uploadCancel();
28004                     this.hide();
28005                 }
28006             }
28007         });
28008          
28009         this.progressDialog.render(Roo.get(document.body));
28010          
28011         this.progress = new Roo.bootstrap.Progress({
28012             cls : 'roo-document-manager-progress',
28013             active : true,
28014             striped : true
28015         });
28016         
28017         this.progress.render(this.progressDialog.getChildContainer());
28018         
28019         this.progressBar = new Roo.bootstrap.ProgressBar({
28020             cls : 'roo-document-manager-progress-bar',
28021             aria_valuenow : 0,
28022             aria_valuemin : 0,
28023             aria_valuemax : 12,
28024             panel : 'success'
28025         });
28026         
28027         this.progressBar.render(this.progress.getChildContainer());
28028     },
28029     
28030     onUploaderClick : function(e)
28031     {
28032         e.preventDefault();
28033      
28034         if(this.fireEvent('beforeselectfile', this) != false){
28035             this.selectorEl.dom.click();
28036         }
28037         
28038     },
28039     
28040     onFileSelected : function(e)
28041     {
28042         e.preventDefault();
28043         
28044         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28045             return;
28046         }
28047         
28048         Roo.each(this.selectorEl.dom.files, function(file){
28049             if(this.fireEvent('inspect', this, file) != false){
28050                 this.files.push(file);
28051             }
28052         }, this);
28053         
28054         this.queue();
28055         
28056     },
28057     
28058     queue : function()
28059     {
28060         this.selectorEl.dom.value = '';
28061         
28062         if(!this.files.length){
28063             return;
28064         }
28065         
28066         if(this.boxes > 0 && this.files.length > this.boxes){
28067             this.files = this.files.slice(0, this.boxes);
28068         }
28069         
28070         this.uploader.show();
28071         
28072         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28073             this.uploader.hide();
28074         }
28075         
28076         var _this = this;
28077         
28078         var files = [];
28079         
28080         var docs = [];
28081         
28082         Roo.each(this.files, function(file){
28083             
28084             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28085                 var f = this.renderPreview(file);
28086                 files.push(f);
28087                 return;
28088             }
28089             
28090             if(file.type.indexOf('image') != -1){
28091                 this.delegates.push(
28092                     (function(){
28093                         _this.process(file);
28094                     }).createDelegate(this)
28095                 );
28096         
28097                 return;
28098             }
28099             
28100             docs.push(
28101                 (function(){
28102                     _this.process(file);
28103                 }).createDelegate(this)
28104             );
28105             
28106         }, this);
28107         
28108         this.files = files;
28109         
28110         this.delegates = this.delegates.concat(docs);
28111         
28112         if(!this.delegates.length){
28113             this.refresh();
28114             return;
28115         }
28116         
28117         this.progressBar.aria_valuemax = this.delegates.length;
28118         
28119         this.arrange();
28120         
28121         return;
28122     },
28123     
28124     arrange : function()
28125     {
28126         if(!this.delegates.length){
28127             this.progressDialog.hide();
28128             this.refresh();
28129             return;
28130         }
28131         
28132         var delegate = this.delegates.shift();
28133         
28134         this.progressDialog.show();
28135         
28136         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28137         
28138         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28139         
28140         delegate();
28141     },
28142     
28143     refresh : function()
28144     {
28145         this.uploader.show();
28146         
28147         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28148             this.uploader.hide();
28149         }
28150         
28151         Roo.isTouch ? this.closable(false) : this.closable(true);
28152         
28153         this.fireEvent('refresh', this);
28154     },
28155     
28156     onRemove : function(e, el, o)
28157     {
28158         e.preventDefault();
28159         
28160         this.fireEvent('remove', this, o);
28161         
28162     },
28163     
28164     remove : function(o)
28165     {
28166         var files = [];
28167         
28168         Roo.each(this.files, function(file){
28169             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28170                 files.push(file);
28171                 return;
28172             }
28173
28174             o.target.remove();
28175
28176         }, this);
28177         
28178         this.files = files;
28179         
28180         this.refresh();
28181     },
28182     
28183     clear : function()
28184     {
28185         Roo.each(this.files, function(file){
28186             if(!file.target){
28187                 return;
28188             }
28189             
28190             file.target.remove();
28191
28192         }, this);
28193         
28194         this.files = [];
28195         
28196         this.refresh();
28197     },
28198     
28199     onClick : function(e, el, o)
28200     {
28201         e.preventDefault();
28202         
28203         this.fireEvent('click', this, o);
28204         
28205     },
28206     
28207     closable : function(closable)
28208     {
28209         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28210             
28211             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28212             
28213             if(closable){
28214                 el.show();
28215                 return;
28216             }
28217             
28218             el.hide();
28219             
28220         }, this);
28221     },
28222     
28223     xhrOnLoad : function(xhr)
28224     {
28225         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28226             el.remove();
28227         }, this);
28228         
28229         if (xhr.readyState !== 4) {
28230             this.arrange();
28231             this.fireEvent('exception', this, xhr);
28232             return;
28233         }
28234
28235         var response = Roo.decode(xhr.responseText);
28236         
28237         if(!response.success){
28238             this.arrange();
28239             this.fireEvent('exception', this, xhr);
28240             return;
28241         }
28242         
28243         var file = this.renderPreview(response.data);
28244         
28245         this.files.push(file);
28246         
28247         this.arrange();
28248         
28249         this.fireEvent('afterupload', this, xhr);
28250         
28251     },
28252     
28253     xhrOnError : function(xhr)
28254     {
28255         Roo.log('xhr on error');
28256         
28257         var response = Roo.decode(xhr.responseText);
28258           
28259         Roo.log(response);
28260         
28261         this.arrange();
28262     },
28263     
28264     process : function(file)
28265     {
28266         if(this.fireEvent('process', this, file) !== false){
28267             if(this.editable && file.type.indexOf('image') != -1){
28268                 this.fireEvent('edit', this, file);
28269                 return;
28270             }
28271
28272             this.uploadStart(file, false);
28273
28274             return;
28275         }
28276         
28277     },
28278     
28279     uploadStart : function(file, crop)
28280     {
28281         this.xhr = new XMLHttpRequest();
28282         
28283         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28284             this.arrange();
28285             return;
28286         }
28287         
28288         file.xhr = this.xhr;
28289             
28290         this.managerEl.createChild({
28291             tag : 'div',
28292             cls : 'roo-document-manager-loading',
28293             cn : [
28294                 {
28295                     tag : 'div',
28296                     tooltip : file.name,
28297                     cls : 'roo-document-manager-thumb',
28298                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28299                 }
28300             ]
28301
28302         });
28303
28304         this.xhr.open(this.method, this.url, true);
28305         
28306         var headers = {
28307             "Accept": "application/json",
28308             "Cache-Control": "no-cache",
28309             "X-Requested-With": "XMLHttpRequest"
28310         };
28311         
28312         for (var headerName in headers) {
28313             var headerValue = headers[headerName];
28314             if (headerValue) {
28315                 this.xhr.setRequestHeader(headerName, headerValue);
28316             }
28317         }
28318         
28319         var _this = this;
28320         
28321         this.xhr.onload = function()
28322         {
28323             _this.xhrOnLoad(_this.xhr);
28324         }
28325         
28326         this.xhr.onerror = function()
28327         {
28328             _this.xhrOnError(_this.xhr);
28329         }
28330         
28331         var formData = new FormData();
28332
28333         formData.append('returnHTML', 'NO');
28334         
28335         if(crop){
28336             formData.append('crop', crop);
28337         }
28338         
28339         formData.append(this.paramName, file, file.name);
28340         
28341         var options = {
28342             file : file, 
28343             manually : false
28344         };
28345         
28346         if(this.fireEvent('prepare', this, formData, options) != false){
28347             
28348             if(options.manually){
28349                 return;
28350             }
28351             
28352             this.xhr.send(formData);
28353             return;
28354         };
28355         
28356         this.uploadCancel();
28357     },
28358     
28359     uploadCancel : function()
28360     {
28361         if (this.xhr) {
28362             this.xhr.abort();
28363         }
28364         
28365         this.delegates = [];
28366         
28367         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28368             el.remove();
28369         }, this);
28370         
28371         this.arrange();
28372     },
28373     
28374     renderPreview : function(file)
28375     {
28376         if(typeof(file.target) != 'undefined' && file.target){
28377             return file;
28378         }
28379         
28380         var previewEl = this.managerEl.createChild({
28381             tag : 'div',
28382             cls : 'roo-document-manager-preview',
28383             cn : [
28384                 {
28385                     tag : 'div',
28386                     tooltip : file[this.toolTipName],
28387                     cls : 'roo-document-manager-thumb',
28388                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28389                 },
28390                 {
28391                     tag : 'button',
28392                     cls : 'close',
28393                     html : '<i class="fa fa-times-circle"></i>'
28394                 }
28395             ]
28396         });
28397
28398         var close = previewEl.select('button.close', true).first();
28399
28400         close.on('click', this.onRemove, this, file);
28401
28402         file.target = previewEl;
28403
28404         var image = previewEl.select('img', true).first();
28405         
28406         var _this = this;
28407         
28408         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28409         
28410         image.on('click', this.onClick, this, file);
28411         
28412         return file;
28413         
28414     },
28415     
28416     onPreviewLoad : function(file, image)
28417     {
28418         if(typeof(file.target) == 'undefined' || !file.target){
28419             return;
28420         }
28421         
28422         var width = image.dom.naturalWidth || image.dom.width;
28423         var height = image.dom.naturalHeight || image.dom.height;
28424         
28425         if(width > height){
28426             file.target.addClass('wide');
28427             return;
28428         }
28429         
28430         file.target.addClass('tall');
28431         return;
28432         
28433     },
28434     
28435     uploadFromSource : function(file, crop)
28436     {
28437         this.xhr = new XMLHttpRequest();
28438         
28439         this.managerEl.createChild({
28440             tag : 'div',
28441             cls : 'roo-document-manager-loading',
28442             cn : [
28443                 {
28444                     tag : 'div',
28445                     tooltip : file.name,
28446                     cls : 'roo-document-manager-thumb',
28447                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28448                 }
28449             ]
28450
28451         });
28452
28453         this.xhr.open(this.method, this.url, true);
28454         
28455         var headers = {
28456             "Accept": "application/json",
28457             "Cache-Control": "no-cache",
28458             "X-Requested-With": "XMLHttpRequest"
28459         };
28460         
28461         for (var headerName in headers) {
28462             var headerValue = headers[headerName];
28463             if (headerValue) {
28464                 this.xhr.setRequestHeader(headerName, headerValue);
28465             }
28466         }
28467         
28468         var _this = this;
28469         
28470         this.xhr.onload = function()
28471         {
28472             _this.xhrOnLoad(_this.xhr);
28473         }
28474         
28475         this.xhr.onerror = function()
28476         {
28477             _this.xhrOnError(_this.xhr);
28478         }
28479         
28480         var formData = new FormData();
28481
28482         formData.append('returnHTML', 'NO');
28483         
28484         formData.append('crop', crop);
28485         
28486         if(typeof(file.filename) != 'undefined'){
28487             formData.append('filename', file.filename);
28488         }
28489         
28490         if(typeof(file.mimetype) != 'undefined'){
28491             formData.append('mimetype', file.mimetype);
28492         }
28493         
28494         Roo.log(formData);
28495         
28496         if(this.fireEvent('prepare', this, formData) != false){
28497             this.xhr.send(formData);
28498         };
28499     }
28500 });
28501
28502 /*
28503 * Licence: LGPL
28504 */
28505
28506 /**
28507  * @class Roo.bootstrap.DocumentViewer
28508  * @extends Roo.bootstrap.Component
28509  * Bootstrap DocumentViewer class
28510  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28511  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28512  * 
28513  * @constructor
28514  * Create a new DocumentViewer
28515  * @param {Object} config The config object
28516  */
28517
28518 Roo.bootstrap.DocumentViewer = function(config){
28519     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28520     
28521     this.addEvents({
28522         /**
28523          * @event initial
28524          * Fire after initEvent
28525          * @param {Roo.bootstrap.DocumentViewer} this
28526          */
28527         "initial" : true,
28528         /**
28529          * @event click
28530          * Fire after click
28531          * @param {Roo.bootstrap.DocumentViewer} this
28532          */
28533         "click" : true,
28534         /**
28535          * @event download
28536          * Fire after download button
28537          * @param {Roo.bootstrap.DocumentViewer} this
28538          */
28539         "download" : true,
28540         /**
28541          * @event trash
28542          * Fire after trash button
28543          * @param {Roo.bootstrap.DocumentViewer} this
28544          */
28545         "trash" : true
28546         
28547     });
28548 };
28549
28550 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28551     
28552     showDownload : true,
28553     
28554     showTrash : true,
28555     
28556     getAutoCreate : function()
28557     {
28558         var cfg = {
28559             tag : 'div',
28560             cls : 'roo-document-viewer',
28561             cn : [
28562                 {
28563                     tag : 'div',
28564                     cls : 'roo-document-viewer-body',
28565                     cn : [
28566                         {
28567                             tag : 'div',
28568                             cls : 'roo-document-viewer-thumb',
28569                             cn : [
28570                                 {
28571                                     tag : 'img',
28572                                     cls : 'roo-document-viewer-image'
28573                                 }
28574                             ]
28575                         }
28576                     ]
28577                 },
28578                 {
28579                     tag : 'div',
28580                     cls : 'roo-document-viewer-footer',
28581                     cn : {
28582                         tag : 'div',
28583                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28584                         cn : [
28585                             {
28586                                 tag : 'div',
28587                                 cls : 'btn-group roo-document-viewer-download',
28588                                 cn : [
28589                                     {
28590                                         tag : 'button',
28591                                         cls : 'btn btn-default',
28592                                         html : '<i class="fa fa-download"></i>'
28593                                     }
28594                                 ]
28595                             },
28596                             {
28597                                 tag : 'div',
28598                                 cls : 'btn-group roo-document-viewer-trash',
28599                                 cn : [
28600                                     {
28601                                         tag : 'button',
28602                                         cls : 'btn btn-default',
28603                                         html : '<i class="fa fa-trash"></i>'
28604                                     }
28605                                 ]
28606                             }
28607                         ]
28608                     }
28609                 }
28610             ]
28611         };
28612         
28613         return cfg;
28614     },
28615     
28616     initEvents : function()
28617     {
28618         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28619         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28620         
28621         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28622         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28623         
28624         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28625         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28626         
28627         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28628         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28629         
28630         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28631         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28632         
28633         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28634         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28635         
28636         this.bodyEl.on('click', this.onClick, this);
28637         this.downloadBtn.on('click', this.onDownload, this);
28638         this.trashBtn.on('click', this.onTrash, this);
28639         
28640         this.downloadBtn.hide();
28641         this.trashBtn.hide();
28642         
28643         if(this.showDownload){
28644             this.downloadBtn.show();
28645         }
28646         
28647         if(this.showTrash){
28648             this.trashBtn.show();
28649         }
28650         
28651         if(!this.showDownload && !this.showTrash) {
28652             this.footerEl.hide();
28653         }
28654         
28655     },
28656     
28657     initial : function()
28658     {
28659         this.fireEvent('initial', this);
28660         
28661     },
28662     
28663     onClick : function(e)
28664     {
28665         e.preventDefault();
28666         
28667         this.fireEvent('click', this);
28668     },
28669     
28670     onDownload : function(e)
28671     {
28672         e.preventDefault();
28673         
28674         this.fireEvent('download', this);
28675     },
28676     
28677     onTrash : function(e)
28678     {
28679         e.preventDefault();
28680         
28681         this.fireEvent('trash', this);
28682     }
28683     
28684 });
28685 /*
28686  * - LGPL
28687  *
28688  * nav progress bar
28689  * 
28690  */
28691
28692 /**
28693  * @class Roo.bootstrap.NavProgressBar
28694  * @extends Roo.bootstrap.Component
28695  * Bootstrap NavProgressBar class
28696  * 
28697  * @constructor
28698  * Create a new nav progress bar
28699  * @param {Object} config The config object
28700  */
28701
28702 Roo.bootstrap.NavProgressBar = function(config){
28703     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28704
28705     this.bullets = this.bullets || [];
28706    
28707 //    Roo.bootstrap.NavProgressBar.register(this);
28708      this.addEvents({
28709         /**
28710              * @event changed
28711              * Fires when the active item changes
28712              * @param {Roo.bootstrap.NavProgressBar} this
28713              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28714              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28715          */
28716         'changed': true
28717      });
28718     
28719 };
28720
28721 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28722     
28723     bullets : [],
28724     barItems : [],
28725     
28726     getAutoCreate : function()
28727     {
28728         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28729         
28730         cfg = {
28731             tag : 'div',
28732             cls : 'roo-navigation-bar-group',
28733             cn : [
28734                 {
28735                     tag : 'div',
28736                     cls : 'roo-navigation-top-bar'
28737                 },
28738                 {
28739                     tag : 'div',
28740                     cls : 'roo-navigation-bullets-bar',
28741                     cn : [
28742                         {
28743                             tag : 'ul',
28744                             cls : 'roo-navigation-bar'
28745                         }
28746                     ]
28747                 },
28748                 
28749                 {
28750                     tag : 'div',
28751                     cls : 'roo-navigation-bottom-bar'
28752                 }
28753             ]
28754             
28755         };
28756         
28757         return cfg;
28758         
28759     },
28760     
28761     initEvents: function() 
28762     {
28763         
28764     },
28765     
28766     onRender : function(ct, position) 
28767     {
28768         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28769         
28770         if(this.bullets.length){
28771             Roo.each(this.bullets, function(b){
28772                this.addItem(b);
28773             }, this);
28774         }
28775         
28776         this.format();
28777         
28778     },
28779     
28780     addItem : function(cfg)
28781     {
28782         var item = new Roo.bootstrap.NavProgressItem(cfg);
28783         
28784         item.parentId = this.id;
28785         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28786         
28787         if(cfg.html){
28788             var top = new Roo.bootstrap.Element({
28789                 tag : 'div',
28790                 cls : 'roo-navigation-bar-text'
28791             });
28792             
28793             var bottom = new Roo.bootstrap.Element({
28794                 tag : 'div',
28795                 cls : 'roo-navigation-bar-text'
28796             });
28797             
28798             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28799             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28800             
28801             var topText = new Roo.bootstrap.Element({
28802                 tag : 'span',
28803                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28804             });
28805             
28806             var bottomText = new Roo.bootstrap.Element({
28807                 tag : 'span',
28808                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28809             });
28810             
28811             topText.onRender(top.el, null);
28812             bottomText.onRender(bottom.el, null);
28813             
28814             item.topEl = top;
28815             item.bottomEl = bottom;
28816         }
28817         
28818         this.barItems.push(item);
28819         
28820         return item;
28821     },
28822     
28823     getActive : function()
28824     {
28825         var active = false;
28826         
28827         Roo.each(this.barItems, function(v){
28828             
28829             if (!v.isActive()) {
28830                 return;
28831             }
28832             
28833             active = v;
28834             return false;
28835             
28836         });
28837         
28838         return active;
28839     },
28840     
28841     setActiveItem : function(item)
28842     {
28843         var prev = false;
28844         
28845         Roo.each(this.barItems, function(v){
28846             if (v.rid == item.rid) {
28847                 return ;
28848             }
28849             
28850             if (v.isActive()) {
28851                 v.setActive(false);
28852                 prev = v;
28853             }
28854         });
28855
28856         item.setActive(true);
28857         
28858         this.fireEvent('changed', this, item, prev);
28859     },
28860     
28861     getBarItem: function(rid)
28862     {
28863         var ret = false;
28864         
28865         Roo.each(this.barItems, function(e) {
28866             if (e.rid != rid) {
28867                 return;
28868             }
28869             
28870             ret =  e;
28871             return false;
28872         });
28873         
28874         return ret;
28875     },
28876     
28877     indexOfItem : function(item)
28878     {
28879         var index = false;
28880         
28881         Roo.each(this.barItems, function(v, i){
28882             
28883             if (v.rid != item.rid) {
28884                 return;
28885             }
28886             
28887             index = i;
28888             return false
28889         });
28890         
28891         return index;
28892     },
28893     
28894     setActiveNext : function()
28895     {
28896         var i = this.indexOfItem(this.getActive());
28897         
28898         if (i > this.barItems.length) {
28899             return;
28900         }
28901         
28902         this.setActiveItem(this.barItems[i+1]);
28903     },
28904     
28905     setActivePrev : function()
28906     {
28907         var i = this.indexOfItem(this.getActive());
28908         
28909         if (i  < 1) {
28910             return;
28911         }
28912         
28913         this.setActiveItem(this.barItems[i-1]);
28914     },
28915     
28916     format : function()
28917     {
28918         if(!this.barItems.length){
28919             return;
28920         }
28921      
28922         var width = 100 / this.barItems.length;
28923         
28924         Roo.each(this.barItems, function(i){
28925             i.el.setStyle('width', width + '%');
28926             i.topEl.el.setStyle('width', width + '%');
28927             i.bottomEl.el.setStyle('width', width + '%');
28928         }, this);
28929         
28930     }
28931     
28932 });
28933 /*
28934  * - LGPL
28935  *
28936  * Nav Progress Item
28937  * 
28938  */
28939
28940 /**
28941  * @class Roo.bootstrap.NavProgressItem
28942  * @extends Roo.bootstrap.Component
28943  * Bootstrap NavProgressItem class
28944  * @cfg {String} rid the reference id
28945  * @cfg {Boolean} active (true|false) Is item active default false
28946  * @cfg {Boolean} disabled (true|false) Is item active default false
28947  * @cfg {String} html
28948  * @cfg {String} position (top|bottom) text position default bottom
28949  * @cfg {String} icon show icon instead of number
28950  * 
28951  * @constructor
28952  * Create a new NavProgressItem
28953  * @param {Object} config The config object
28954  */
28955 Roo.bootstrap.NavProgressItem = function(config){
28956     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28957     this.addEvents({
28958         // raw events
28959         /**
28960          * @event click
28961          * The raw click event for the entire grid.
28962          * @param {Roo.bootstrap.NavProgressItem} this
28963          * @param {Roo.EventObject} e
28964          */
28965         "click" : true
28966     });
28967    
28968 };
28969
28970 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28971     
28972     rid : '',
28973     active : false,
28974     disabled : false,
28975     html : '',
28976     position : 'bottom',
28977     icon : false,
28978     
28979     getAutoCreate : function()
28980     {
28981         var iconCls = 'roo-navigation-bar-item-icon';
28982         
28983         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28984         
28985         var cfg = {
28986             tag: 'li',
28987             cls: 'roo-navigation-bar-item',
28988             cn : [
28989                 {
28990                     tag : 'i',
28991                     cls : iconCls
28992                 }
28993             ]
28994         };
28995         
28996         if(this.active){
28997             cfg.cls += ' active';
28998         }
28999         if(this.disabled){
29000             cfg.cls += ' disabled';
29001         }
29002         
29003         return cfg;
29004     },
29005     
29006     disable : function()
29007     {
29008         this.setDisabled(true);
29009     },
29010     
29011     enable : function()
29012     {
29013         this.setDisabled(false);
29014     },
29015     
29016     initEvents: function() 
29017     {
29018         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29019         
29020         this.iconEl.on('click', this.onClick, this);
29021     },
29022     
29023     onClick : function(e)
29024     {
29025         e.preventDefault();
29026         
29027         if(this.disabled){
29028             return;
29029         }
29030         
29031         if(this.fireEvent('click', this, e) === false){
29032             return;
29033         };
29034         
29035         this.parent().setActiveItem(this);
29036     },
29037     
29038     isActive: function () 
29039     {
29040         return this.active;
29041     },
29042     
29043     setActive : function(state)
29044     {
29045         if(this.active == state){
29046             return;
29047         }
29048         
29049         this.active = state;
29050         
29051         if (state) {
29052             this.el.addClass('active');
29053             return;
29054         }
29055         
29056         this.el.removeClass('active');
29057         
29058         return;
29059     },
29060     
29061     setDisabled : function(state)
29062     {
29063         if(this.disabled == state){
29064             return;
29065         }
29066         
29067         this.disabled = state;
29068         
29069         if (state) {
29070             this.el.addClass('disabled');
29071             return;
29072         }
29073         
29074         this.el.removeClass('disabled');
29075     },
29076     
29077     tooltipEl : function()
29078     {
29079         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29080     }
29081 });
29082  
29083
29084  /*
29085  * - LGPL
29086  *
29087  * FieldLabel
29088  * 
29089  */
29090
29091 /**
29092  * @class Roo.bootstrap.FieldLabel
29093  * @extends Roo.bootstrap.Component
29094  * Bootstrap FieldLabel class
29095  * @cfg {String} html contents of the element
29096  * @cfg {String} tag tag of the element default label
29097  * @cfg {String} cls class of the element
29098  * @cfg {String} target label target 
29099  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29100  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29101  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29102  * @cfg {String} iconTooltip default "This field is required"
29103  * 
29104  * @constructor
29105  * Create a new FieldLabel
29106  * @param {Object} config The config object
29107  */
29108
29109 Roo.bootstrap.FieldLabel = function(config){
29110     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29111     
29112     this.addEvents({
29113             /**
29114              * @event invalid
29115              * Fires after the field has been marked as invalid.
29116              * @param {Roo.form.FieldLabel} this
29117              * @param {String} msg The validation message
29118              */
29119             invalid : true,
29120             /**
29121              * @event valid
29122              * Fires after the field has been validated with no errors.
29123              * @param {Roo.form.FieldLabel} this
29124              */
29125             valid : true
29126         });
29127 };
29128
29129 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29130     
29131     tag: 'label',
29132     cls: '',
29133     html: '',
29134     target: '',
29135     allowBlank : true,
29136     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29137     validClass : 'text-success fa fa-lg fa-check',
29138     iconTooltip : 'This field is required',
29139     
29140     getAutoCreate : function(){
29141         
29142         var cfg = {
29143             tag : this.tag,
29144             cls : 'roo-bootstrap-field-label ' + this.cls,
29145             for : this.target,
29146             cn : [
29147                 {
29148                     tag : 'i',
29149                     cls : '',
29150                     tooltip : this.iconTooltip
29151                 },
29152                 {
29153                     tag : 'span',
29154                     html : this.html
29155                 }
29156             ] 
29157         };
29158         
29159         return cfg;
29160     },
29161     
29162     initEvents: function() 
29163     {
29164         Roo.bootstrap.Element.superclass.initEvents.call(this);
29165         
29166         this.iconEl = this.el.select('i', true).first();
29167         
29168         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29169         
29170         Roo.bootstrap.FieldLabel.register(this);
29171     },
29172     
29173     /**
29174      * Mark this field as valid
29175      */
29176     markValid : function()
29177     {
29178         this.iconEl.show();
29179         
29180         this.iconEl.removeClass(this.invalidClass);
29181         
29182         this.iconEl.addClass(this.validClass);
29183         
29184         this.fireEvent('valid', this);
29185     },
29186     
29187     /**
29188      * Mark this field as invalid
29189      * @param {String} msg The validation message
29190      */
29191     markInvalid : function(msg)
29192     {
29193         this.iconEl.show();
29194         
29195         this.iconEl.removeClass(this.validClass);
29196         
29197         this.iconEl.addClass(this.invalidClass);
29198         
29199         this.fireEvent('invalid', this, msg);
29200     }
29201     
29202    
29203 });
29204
29205 Roo.apply(Roo.bootstrap.FieldLabel, {
29206     
29207     groups: {},
29208     
29209      /**
29210     * register a FieldLabel Group
29211     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29212     */
29213     register : function(label)
29214     {
29215         if(this.groups.hasOwnProperty(label.target)){
29216             return;
29217         }
29218      
29219         this.groups[label.target] = label;
29220         
29221     },
29222     /**
29223     * fetch a FieldLabel Group based on the target
29224     * @param {string} target
29225     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29226     */
29227     get: function(target) {
29228         if (typeof(this.groups[target]) == 'undefined') {
29229             return false;
29230         }
29231         
29232         return this.groups[target] ;
29233     }
29234 });
29235
29236  
29237
29238  /*
29239  * - LGPL
29240  *
29241  * page DateSplitField.
29242  * 
29243  */
29244
29245
29246 /**
29247  * @class Roo.bootstrap.DateSplitField
29248  * @extends Roo.bootstrap.Component
29249  * Bootstrap DateSplitField class
29250  * @cfg {string} fieldLabel - the label associated
29251  * @cfg {Number} labelWidth set the width of label (0-12)
29252  * @cfg {String} labelAlign (top|left)
29253  * @cfg {Boolean} dayAllowBlank (true|false) default false
29254  * @cfg {Boolean} monthAllowBlank (true|false) default false
29255  * @cfg {Boolean} yearAllowBlank (true|false) default false
29256  * @cfg {string} dayPlaceholder 
29257  * @cfg {string} monthPlaceholder
29258  * @cfg {string} yearPlaceholder
29259  * @cfg {string} dayFormat default 'd'
29260  * @cfg {string} monthFormat default 'm'
29261  * @cfg {string} yearFormat default 'Y'
29262  * @cfg {Number} labellg set the width of label (1-12)
29263  * @cfg {Number} labelmd set the width of label (1-12)
29264  * @cfg {Number} labelsm set the width of label (1-12)
29265  * @cfg {Number} labelxs set the width of label (1-12)
29266
29267  *     
29268  * @constructor
29269  * Create a new DateSplitField
29270  * @param {Object} config The config object
29271  */
29272
29273 Roo.bootstrap.DateSplitField = function(config){
29274     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29275     
29276     this.addEvents({
29277         // raw events
29278          /**
29279          * @event years
29280          * getting the data of years
29281          * @param {Roo.bootstrap.DateSplitField} this
29282          * @param {Object} years
29283          */
29284         "years" : true,
29285         /**
29286          * @event days
29287          * getting the data of days
29288          * @param {Roo.bootstrap.DateSplitField} this
29289          * @param {Object} days
29290          */
29291         "days" : true,
29292         /**
29293          * @event invalid
29294          * Fires after the field has been marked as invalid.
29295          * @param {Roo.form.Field} this
29296          * @param {String} msg The validation message
29297          */
29298         invalid : true,
29299        /**
29300          * @event valid
29301          * Fires after the field has been validated with no errors.
29302          * @param {Roo.form.Field} this
29303          */
29304         valid : true
29305     });
29306 };
29307
29308 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29309     
29310     fieldLabel : '',
29311     labelAlign : 'top',
29312     labelWidth : 3,
29313     dayAllowBlank : false,
29314     monthAllowBlank : false,
29315     yearAllowBlank : false,
29316     dayPlaceholder : '',
29317     monthPlaceholder : '',
29318     yearPlaceholder : '',
29319     dayFormat : 'd',
29320     monthFormat : 'm',
29321     yearFormat : 'Y',
29322     isFormField : true,
29323     labellg : 0,
29324     labelmd : 0,
29325     labelsm : 0,
29326     labelxs : 0,
29327     
29328     getAutoCreate : function()
29329     {
29330         var cfg = {
29331             tag : 'div',
29332             cls : 'row roo-date-split-field-group',
29333             cn : [
29334                 {
29335                     tag : 'input',
29336                     type : 'hidden',
29337                     cls : 'form-hidden-field roo-date-split-field-group-value',
29338                     name : this.name
29339                 }
29340             ]
29341         };
29342         
29343         var labelCls = 'col-md-12';
29344         var contentCls = 'col-md-4';
29345         
29346         if(this.fieldLabel){
29347             
29348             var label = {
29349                 tag : 'div',
29350                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29351                 cn : [
29352                     {
29353                         tag : 'label',
29354                         html : this.fieldLabel
29355                     }
29356                 ]
29357             };
29358             
29359             if(this.labelAlign == 'left'){
29360             
29361                 if(this.labelWidth > 12){
29362                     label.style = "width: " + this.labelWidth + 'px';
29363                 }
29364
29365                 if(this.labelWidth < 13 && this.labelmd == 0){
29366                     this.labelmd = this.labelWidth;
29367                 }
29368
29369                 if(this.labellg > 0){
29370                     labelCls = ' col-lg-' + this.labellg;
29371                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29372                 }
29373
29374                 if(this.labelmd > 0){
29375                     labelCls = ' col-md-' + this.labelmd;
29376                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29377                 }
29378
29379                 if(this.labelsm > 0){
29380                     labelCls = ' col-sm-' + this.labelsm;
29381                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29382                 }
29383
29384                 if(this.labelxs > 0){
29385                     labelCls = ' col-xs-' + this.labelxs;
29386                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29387                 }
29388             }
29389             
29390             label.cls += ' ' + labelCls;
29391             
29392             cfg.cn.push(label);
29393         }
29394         
29395         Roo.each(['day', 'month', 'year'], function(t){
29396             cfg.cn.push({
29397                 tag : 'div',
29398                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29399             });
29400         }, this);
29401         
29402         return cfg;
29403     },
29404     
29405     inputEl: function ()
29406     {
29407         return this.el.select('.roo-date-split-field-group-value', true).first();
29408     },
29409     
29410     onRender : function(ct, position) 
29411     {
29412         var _this = this;
29413         
29414         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29415         
29416         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29417         
29418         this.dayField = new Roo.bootstrap.ComboBox({
29419             allowBlank : this.dayAllowBlank,
29420             alwaysQuery : true,
29421             displayField : 'value',
29422             editable : false,
29423             fieldLabel : '',
29424             forceSelection : true,
29425             mode : 'local',
29426             placeholder : this.dayPlaceholder,
29427             selectOnFocus : true,
29428             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29429             triggerAction : 'all',
29430             typeAhead : true,
29431             valueField : 'value',
29432             store : new Roo.data.SimpleStore({
29433                 data : (function() {    
29434                     var days = [];
29435                     _this.fireEvent('days', _this, days);
29436                     return days;
29437                 })(),
29438                 fields : [ 'value' ]
29439             }),
29440             listeners : {
29441                 select : function (_self, record, index)
29442                 {
29443                     _this.setValue(_this.getValue());
29444                 }
29445             }
29446         });
29447
29448         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29449         
29450         this.monthField = new Roo.bootstrap.MonthField({
29451             after : '<i class=\"fa fa-calendar\"></i>',
29452             allowBlank : this.monthAllowBlank,
29453             placeholder : this.monthPlaceholder,
29454             readOnly : true,
29455             listeners : {
29456                 render : function (_self)
29457                 {
29458                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29459                         e.preventDefault();
29460                         _self.focus();
29461                     });
29462                 },
29463                 select : function (_self, oldvalue, newvalue)
29464                 {
29465                     _this.setValue(_this.getValue());
29466                 }
29467             }
29468         });
29469         
29470         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29471         
29472         this.yearField = new Roo.bootstrap.ComboBox({
29473             allowBlank : this.yearAllowBlank,
29474             alwaysQuery : true,
29475             displayField : 'value',
29476             editable : false,
29477             fieldLabel : '',
29478             forceSelection : true,
29479             mode : 'local',
29480             placeholder : this.yearPlaceholder,
29481             selectOnFocus : true,
29482             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29483             triggerAction : 'all',
29484             typeAhead : true,
29485             valueField : 'value',
29486             store : new Roo.data.SimpleStore({
29487                 data : (function() {
29488                     var years = [];
29489                     _this.fireEvent('years', _this, years);
29490                     return years;
29491                 })(),
29492                 fields : [ 'value' ]
29493             }),
29494             listeners : {
29495                 select : function (_self, record, index)
29496                 {
29497                     _this.setValue(_this.getValue());
29498                 }
29499             }
29500         });
29501
29502         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29503     },
29504     
29505     setValue : function(v, format)
29506     {
29507         this.inputEl.dom.value = v;
29508         
29509         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29510         
29511         var d = Date.parseDate(v, f);
29512         
29513         if(!d){
29514             this.validate();
29515             return;
29516         }
29517         
29518         this.setDay(d.format(this.dayFormat));
29519         this.setMonth(d.format(this.monthFormat));
29520         this.setYear(d.format(this.yearFormat));
29521         
29522         this.validate();
29523         
29524         return;
29525     },
29526     
29527     setDay : function(v)
29528     {
29529         this.dayField.setValue(v);
29530         this.inputEl.dom.value = this.getValue();
29531         this.validate();
29532         return;
29533     },
29534     
29535     setMonth : function(v)
29536     {
29537         this.monthField.setValue(v, true);
29538         this.inputEl.dom.value = this.getValue();
29539         this.validate();
29540         return;
29541     },
29542     
29543     setYear : function(v)
29544     {
29545         this.yearField.setValue(v);
29546         this.inputEl.dom.value = this.getValue();
29547         this.validate();
29548         return;
29549     },
29550     
29551     getDay : function()
29552     {
29553         return this.dayField.getValue();
29554     },
29555     
29556     getMonth : function()
29557     {
29558         return this.monthField.getValue();
29559     },
29560     
29561     getYear : function()
29562     {
29563         return this.yearField.getValue();
29564     },
29565     
29566     getValue : function()
29567     {
29568         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29569         
29570         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29571         
29572         return date;
29573     },
29574     
29575     reset : function()
29576     {
29577         this.setDay('');
29578         this.setMonth('');
29579         this.setYear('');
29580         this.inputEl.dom.value = '';
29581         this.validate();
29582         return;
29583     },
29584     
29585     validate : function()
29586     {
29587         var d = this.dayField.validate();
29588         var m = this.monthField.validate();
29589         var y = this.yearField.validate();
29590         
29591         var valid = true;
29592         
29593         if(
29594                 (!this.dayAllowBlank && !d) ||
29595                 (!this.monthAllowBlank && !m) ||
29596                 (!this.yearAllowBlank && !y)
29597         ){
29598             valid = false;
29599         }
29600         
29601         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29602             return valid;
29603         }
29604         
29605         if(valid){
29606             this.markValid();
29607             return valid;
29608         }
29609         
29610         this.markInvalid();
29611         
29612         return valid;
29613     },
29614     
29615     markValid : function()
29616     {
29617         
29618         var label = this.el.select('label', true).first();
29619         var icon = this.el.select('i.fa-star', true).first();
29620
29621         if(label && icon){
29622             icon.remove();
29623         }
29624         
29625         this.fireEvent('valid', this);
29626     },
29627     
29628      /**
29629      * Mark this field as invalid
29630      * @param {String} msg The validation message
29631      */
29632     markInvalid : function(msg)
29633     {
29634         
29635         var label = this.el.select('label', true).first();
29636         var icon = this.el.select('i.fa-star', true).first();
29637
29638         if(label && !icon){
29639             this.el.select('.roo-date-split-field-label', true).createChild({
29640                 tag : 'i',
29641                 cls : 'text-danger fa fa-lg fa-star',
29642                 tooltip : 'This field is required',
29643                 style : 'margin-right:5px;'
29644             }, label, true);
29645         }
29646         
29647         this.fireEvent('invalid', this, msg);
29648     },
29649     
29650     clearInvalid : function()
29651     {
29652         var label = this.el.select('label', true).first();
29653         var icon = this.el.select('i.fa-star', true).first();
29654
29655         if(label && icon){
29656             icon.remove();
29657         }
29658         
29659         this.fireEvent('valid', this);
29660     },
29661     
29662     getName: function()
29663     {
29664         return this.name;
29665     }
29666     
29667 });
29668
29669  /**
29670  *
29671  * This is based on 
29672  * http://masonry.desandro.com
29673  *
29674  * The idea is to render all the bricks based on vertical width...
29675  *
29676  * The original code extends 'outlayer' - we might need to use that....
29677  * 
29678  */
29679
29680
29681 /**
29682  * @class Roo.bootstrap.LayoutMasonry
29683  * @extends Roo.bootstrap.Component
29684  * Bootstrap Layout Masonry class
29685  * 
29686  * @constructor
29687  * Create a new Element
29688  * @param {Object} config The config object
29689  */
29690
29691 Roo.bootstrap.LayoutMasonry = function(config){
29692     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29693     
29694     this.bricks = [];
29695     
29696 };
29697
29698 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29699     
29700     /**
29701      * @cfg {Boolean} isLayoutInstant = no animation?
29702      */   
29703     isLayoutInstant : false, // needed?
29704    
29705     /**
29706      * @cfg {Number} boxWidth  width of the columns
29707      */   
29708     boxWidth : 450,
29709     
29710       /**
29711      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29712      */   
29713     boxHeight : 0,
29714     
29715     /**
29716      * @cfg {Number} padWidth padding below box..
29717      */   
29718     padWidth : 10, 
29719     
29720     /**
29721      * @cfg {Number} gutter gutter width..
29722      */   
29723     gutter : 10,
29724     
29725      /**
29726      * @cfg {Number} maxCols maximum number of columns
29727      */   
29728     
29729     maxCols: 0,
29730     
29731     /**
29732      * @cfg {Boolean} isAutoInitial defalut true
29733      */   
29734     isAutoInitial : true, 
29735     
29736     containerWidth: 0,
29737     
29738     /**
29739      * @cfg {Boolean} isHorizontal defalut false
29740      */   
29741     isHorizontal : false, 
29742
29743     currentSize : null,
29744     
29745     tag: 'div',
29746     
29747     cls: '',
29748     
29749     bricks: null, //CompositeElement
29750     
29751     cols : 1,
29752     
29753     _isLayoutInited : false,
29754     
29755 //    isAlternative : false, // only use for vertical layout...
29756     
29757     /**
29758      * @cfg {Number} alternativePadWidth padding below box..
29759      */   
29760     alternativePadWidth : 50, 
29761     
29762     getAutoCreate : function(){
29763         
29764         var cfg = {
29765             tag: this.tag,
29766             cls: 'blog-masonary-wrapper ' + this.cls,
29767             cn : {
29768                 cls : 'mas-boxes masonary'
29769             }
29770         };
29771         
29772         return cfg;
29773     },
29774     
29775     getChildContainer: function( )
29776     {
29777         if (this.boxesEl) {
29778             return this.boxesEl;
29779         }
29780         
29781         this.boxesEl = this.el.select('.mas-boxes').first();
29782         
29783         return this.boxesEl;
29784     },
29785     
29786     
29787     initEvents : function()
29788     {
29789         var _this = this;
29790         
29791         if(this.isAutoInitial){
29792             Roo.log('hook children rendered');
29793             this.on('childrenrendered', function() {
29794                 Roo.log('children rendered');
29795                 _this.initial();
29796             } ,this);
29797         }
29798     },
29799     
29800     initial : function()
29801     {
29802         this.currentSize = this.el.getBox(true);
29803         
29804         Roo.EventManager.onWindowResize(this.resize, this); 
29805
29806         if(!this.isAutoInitial){
29807             this.layout();
29808             return;
29809         }
29810         
29811         this.layout();
29812         
29813         return;
29814         //this.layout.defer(500,this);
29815         
29816     },
29817     
29818     resize : function()
29819     {
29820         Roo.log('resize');
29821         
29822         var cs = this.el.getBox(true);
29823         
29824         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29825             Roo.log("no change in with or X");
29826             return;
29827         }
29828         
29829         this.currentSize = cs;
29830         
29831         this.layout();
29832         
29833     },
29834     
29835     layout : function()
29836     {   
29837         this._resetLayout();
29838         
29839         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29840         
29841         this.layoutItems( isInstant );
29842       
29843         this._isLayoutInited = true;
29844         
29845     },
29846     
29847     _resetLayout : function()
29848     {
29849         if(this.isHorizontal){
29850             this.horizontalMeasureColumns();
29851             return;
29852         }
29853         
29854         this.verticalMeasureColumns();
29855         
29856     },
29857     
29858     verticalMeasureColumns : function()
29859     {
29860         this.getContainerWidth();
29861         
29862 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29863 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29864 //            return;
29865 //        }
29866         
29867         var boxWidth = this.boxWidth + this.padWidth;
29868         
29869         if(this.containerWidth < this.boxWidth){
29870             boxWidth = this.containerWidth
29871         }
29872         
29873         var containerWidth = this.containerWidth;
29874         
29875         var cols = Math.floor(containerWidth / boxWidth);
29876         
29877         this.cols = Math.max( cols, 1 );
29878         
29879         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29880         
29881         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29882         
29883         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29884         
29885         this.colWidth = boxWidth + avail - this.padWidth;
29886         
29887         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29888         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29889     },
29890     
29891     horizontalMeasureColumns : function()
29892     {
29893         this.getContainerWidth();
29894         
29895         var boxWidth = this.boxWidth;
29896         
29897         if(this.containerWidth < boxWidth){
29898             boxWidth = this.containerWidth;
29899         }
29900         
29901         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29902         
29903         this.el.setHeight(boxWidth);
29904         
29905     },
29906     
29907     getContainerWidth : function()
29908     {
29909         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29910     },
29911     
29912     layoutItems : function( isInstant )
29913     {
29914         var items = Roo.apply([], this.bricks);
29915         
29916         if(this.isHorizontal){
29917             this._horizontalLayoutItems( items , isInstant );
29918             return;
29919         }
29920         
29921 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29922 //            this._verticalAlternativeLayoutItems( items , isInstant );
29923 //            return;
29924 //        }
29925         
29926         this._verticalLayoutItems( items , isInstant );
29927         
29928     },
29929     
29930     _verticalLayoutItems : function ( items , isInstant)
29931     {
29932         if ( !items || !items.length ) {
29933             return;
29934         }
29935         
29936         var standard = [
29937             ['xs', 'xs', 'xs', 'tall'],
29938             ['xs', 'xs', 'tall'],
29939             ['xs', 'xs', 'sm'],
29940             ['xs', 'xs', 'xs'],
29941             ['xs', 'tall'],
29942             ['xs', 'sm'],
29943             ['xs', 'xs'],
29944             ['xs'],
29945             
29946             ['sm', 'xs', 'xs'],
29947             ['sm', 'xs'],
29948             ['sm'],
29949             
29950             ['tall', 'xs', 'xs', 'xs'],
29951             ['tall', 'xs', 'xs'],
29952             ['tall', 'xs'],
29953             ['tall']
29954             
29955         ];
29956         
29957         var queue = [];
29958         
29959         var boxes = [];
29960         
29961         var box = [];
29962         
29963         Roo.each(items, function(item, k){
29964             
29965             switch (item.size) {
29966                 // these layouts take up a full box,
29967                 case 'md' :
29968                 case 'md-left' :
29969                 case 'md-right' :
29970                 case 'wide' :
29971                     
29972                     if(box.length){
29973                         boxes.push(box);
29974                         box = [];
29975                     }
29976                     
29977                     boxes.push([item]);
29978                     
29979                     break;
29980                     
29981                 case 'xs' :
29982                 case 'sm' :
29983                 case 'tall' :
29984                     
29985                     box.push(item);
29986                     
29987                     break;
29988                 default :
29989                     break;
29990                     
29991             }
29992             
29993         }, this);
29994         
29995         if(box.length){
29996             boxes.push(box);
29997             box = [];
29998         }
29999         
30000         var filterPattern = function(box, length)
30001         {
30002             if(!box.length){
30003                 return;
30004             }
30005             
30006             var match = false;
30007             
30008             var pattern = box.slice(0, length);
30009             
30010             var format = [];
30011             
30012             Roo.each(pattern, function(i){
30013                 format.push(i.size);
30014             }, this);
30015             
30016             Roo.each(standard, function(s){
30017                 
30018                 if(String(s) != String(format)){
30019                     return;
30020                 }
30021                 
30022                 match = true;
30023                 return false;
30024                 
30025             }, this);
30026             
30027             if(!match && length == 1){
30028                 return;
30029             }
30030             
30031             if(!match){
30032                 filterPattern(box, length - 1);
30033                 return;
30034             }
30035                 
30036             queue.push(pattern);
30037
30038             box = box.slice(length, box.length);
30039
30040             filterPattern(box, 4);
30041
30042             return;
30043             
30044         }
30045         
30046         Roo.each(boxes, function(box, k){
30047             
30048             if(!box.length){
30049                 return;
30050             }
30051             
30052             if(box.length == 1){
30053                 queue.push(box);
30054                 return;
30055             }
30056             
30057             filterPattern(box, 4);
30058             
30059         }, this);
30060         
30061         this._processVerticalLayoutQueue( queue, isInstant );
30062         
30063     },
30064     
30065 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30066 //    {
30067 //        if ( !items || !items.length ) {
30068 //            return;
30069 //        }
30070 //
30071 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30072 //        
30073 //    },
30074     
30075     _horizontalLayoutItems : function ( items , isInstant)
30076     {
30077         if ( !items || !items.length || items.length < 3) {
30078             return;
30079         }
30080         
30081         items.reverse();
30082         
30083         var eItems = items.slice(0, 3);
30084         
30085         items = items.slice(3, items.length);
30086         
30087         var standard = [
30088             ['xs', 'xs', 'xs', 'wide'],
30089             ['xs', 'xs', 'wide'],
30090             ['xs', 'xs', 'sm'],
30091             ['xs', 'xs', 'xs'],
30092             ['xs', 'wide'],
30093             ['xs', 'sm'],
30094             ['xs', 'xs'],
30095             ['xs'],
30096             
30097             ['sm', 'xs', 'xs'],
30098             ['sm', 'xs'],
30099             ['sm'],
30100             
30101             ['wide', 'xs', 'xs', 'xs'],
30102             ['wide', 'xs', 'xs'],
30103             ['wide', 'xs'],
30104             ['wide'],
30105             
30106             ['wide-thin']
30107         ];
30108         
30109         var queue = [];
30110         
30111         var boxes = [];
30112         
30113         var box = [];
30114         
30115         Roo.each(items, function(item, k){
30116             
30117             switch (item.size) {
30118                 case 'md' :
30119                 case 'md-left' :
30120                 case 'md-right' :
30121                 case 'tall' :
30122                     
30123                     if(box.length){
30124                         boxes.push(box);
30125                         box = [];
30126                     }
30127                     
30128                     boxes.push([item]);
30129                     
30130                     break;
30131                     
30132                 case 'xs' :
30133                 case 'sm' :
30134                 case 'wide' :
30135                 case 'wide-thin' :
30136                     
30137                     box.push(item);
30138                     
30139                     break;
30140                 default :
30141                     break;
30142                     
30143             }
30144             
30145         }, this);
30146         
30147         if(box.length){
30148             boxes.push(box);
30149             box = [];
30150         }
30151         
30152         var filterPattern = function(box, length)
30153         {
30154             if(!box.length){
30155                 return;
30156             }
30157             
30158             var match = false;
30159             
30160             var pattern = box.slice(0, length);
30161             
30162             var format = [];
30163             
30164             Roo.each(pattern, function(i){
30165                 format.push(i.size);
30166             }, this);
30167             
30168             Roo.each(standard, function(s){
30169                 
30170                 if(String(s) != String(format)){
30171                     return;
30172                 }
30173                 
30174                 match = true;
30175                 return false;
30176                 
30177             }, this);
30178             
30179             if(!match && length == 1){
30180                 return;
30181             }
30182             
30183             if(!match){
30184                 filterPattern(box, length - 1);
30185                 return;
30186             }
30187                 
30188             queue.push(pattern);
30189
30190             box = box.slice(length, box.length);
30191
30192             filterPattern(box, 4);
30193
30194             return;
30195             
30196         }
30197         
30198         Roo.each(boxes, function(box, k){
30199             
30200             if(!box.length){
30201                 return;
30202             }
30203             
30204             if(box.length == 1){
30205                 queue.push(box);
30206                 return;
30207             }
30208             
30209             filterPattern(box, 4);
30210             
30211         }, this);
30212         
30213         
30214         var prune = [];
30215         
30216         var pos = this.el.getBox(true);
30217         
30218         var minX = pos.x;
30219         
30220         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30221         
30222         var hit_end = false;
30223         
30224         Roo.each(queue, function(box){
30225             
30226             if(hit_end){
30227                 
30228                 Roo.each(box, function(b){
30229                 
30230                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30231                     b.el.hide();
30232
30233                 }, this);
30234
30235                 return;
30236             }
30237             
30238             var mx = 0;
30239             
30240             Roo.each(box, function(b){
30241                 
30242                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30243                 b.el.show();
30244
30245                 mx = Math.max(mx, b.x);
30246                 
30247             }, this);
30248             
30249             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30250             
30251             if(maxX < minX){
30252                 
30253                 Roo.each(box, function(b){
30254                 
30255                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30256                     b.el.hide();
30257                     
30258                 }, this);
30259                 
30260                 hit_end = true;
30261                 
30262                 return;
30263             }
30264             
30265             prune.push(box);
30266             
30267         }, this);
30268         
30269         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30270     },
30271     
30272     /** Sets position of item in DOM
30273     * @param {Element} item
30274     * @param {Number} x - horizontal position
30275     * @param {Number} y - vertical position
30276     * @param {Boolean} isInstant - disables transitions
30277     */
30278     _processVerticalLayoutQueue : function( queue, isInstant )
30279     {
30280         var pos = this.el.getBox(true);
30281         var x = pos.x;
30282         var y = pos.y;
30283         var maxY = [];
30284         
30285         for (var i = 0; i < this.cols; i++){
30286             maxY[i] = pos.y;
30287         }
30288         
30289         Roo.each(queue, function(box, k){
30290             
30291             var col = k % this.cols;
30292             
30293             Roo.each(box, function(b,kk){
30294                 
30295                 b.el.position('absolute');
30296                 
30297                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30298                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30299                 
30300                 if(b.size == 'md-left' || b.size == 'md-right'){
30301                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30302                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30303                 }
30304                 
30305                 b.el.setWidth(width);
30306                 b.el.setHeight(height);
30307                 // iframe?
30308                 b.el.select('iframe',true).setSize(width,height);
30309                 
30310             }, this);
30311             
30312             for (var i = 0; i < this.cols; i++){
30313                 
30314                 if(maxY[i] < maxY[col]){
30315                     col = i;
30316                     continue;
30317                 }
30318                 
30319                 col = Math.min(col, i);
30320                 
30321             }
30322             
30323             x = pos.x + col * (this.colWidth + this.padWidth);
30324             
30325             y = maxY[col];
30326             
30327             var positions = [];
30328             
30329             switch (box.length){
30330                 case 1 :
30331                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30332                     break;
30333                 case 2 :
30334                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30335                     break;
30336                 case 3 :
30337                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30338                     break;
30339                 case 4 :
30340                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30341                     break;
30342                 default :
30343                     break;
30344             }
30345             
30346             Roo.each(box, function(b,kk){
30347                 
30348                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30349                 
30350                 var sz = b.el.getSize();
30351                 
30352                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30353                 
30354             }, this);
30355             
30356         }, this);
30357         
30358         var mY = 0;
30359         
30360         for (var i = 0; i < this.cols; i++){
30361             mY = Math.max(mY, maxY[i]);
30362         }
30363         
30364         this.el.setHeight(mY - pos.y);
30365         
30366     },
30367     
30368 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30369 //    {
30370 //        var pos = this.el.getBox(true);
30371 //        var x = pos.x;
30372 //        var y = pos.y;
30373 //        var maxX = pos.right;
30374 //        
30375 //        var maxHeight = 0;
30376 //        
30377 //        Roo.each(items, function(item, k){
30378 //            
30379 //            var c = k % 2;
30380 //            
30381 //            item.el.position('absolute');
30382 //                
30383 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30384 //
30385 //            item.el.setWidth(width);
30386 //
30387 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30388 //
30389 //            item.el.setHeight(height);
30390 //            
30391 //            if(c == 0){
30392 //                item.el.setXY([x, y], isInstant ? false : true);
30393 //            } else {
30394 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30395 //            }
30396 //            
30397 //            y = y + height + this.alternativePadWidth;
30398 //            
30399 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30400 //            
30401 //        }, this);
30402 //        
30403 //        this.el.setHeight(maxHeight);
30404 //        
30405 //    },
30406     
30407     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30408     {
30409         var pos = this.el.getBox(true);
30410         
30411         var minX = pos.x;
30412         var minY = pos.y;
30413         
30414         var maxX = pos.right;
30415         
30416         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30417         
30418         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30419         
30420         Roo.each(queue, function(box, k){
30421             
30422             Roo.each(box, function(b, kk){
30423                 
30424                 b.el.position('absolute');
30425                 
30426                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30427                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30428                 
30429                 if(b.size == 'md-left' || b.size == 'md-right'){
30430                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30431                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30432                 }
30433                 
30434                 b.el.setWidth(width);
30435                 b.el.setHeight(height);
30436                 
30437             }, this);
30438             
30439             if(!box.length){
30440                 return;
30441             }
30442             
30443             var positions = [];
30444             
30445             switch (box.length){
30446                 case 1 :
30447                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30448                     break;
30449                 case 2 :
30450                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30451                     break;
30452                 case 3 :
30453                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30454                     break;
30455                 case 4 :
30456                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30457                     break;
30458                 default :
30459                     break;
30460             }
30461             
30462             Roo.each(box, function(b,kk){
30463                 
30464                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30465                 
30466                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30467                 
30468             }, this);
30469             
30470         }, this);
30471         
30472     },
30473     
30474     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30475     {
30476         Roo.each(eItems, function(b,k){
30477             
30478             b.size = (k == 0) ? 'sm' : 'xs';
30479             b.x = (k == 0) ? 2 : 1;
30480             b.y = (k == 0) ? 2 : 1;
30481             
30482             b.el.position('absolute');
30483             
30484             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30485                 
30486             b.el.setWidth(width);
30487             
30488             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30489             
30490             b.el.setHeight(height);
30491             
30492         }, this);
30493
30494         var positions = [];
30495         
30496         positions.push({
30497             x : maxX - this.unitWidth * 2 - this.gutter,
30498             y : minY
30499         });
30500         
30501         positions.push({
30502             x : maxX - this.unitWidth,
30503             y : minY + (this.unitWidth + this.gutter) * 2
30504         });
30505         
30506         positions.push({
30507             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30508             y : minY
30509         });
30510         
30511         Roo.each(eItems, function(b,k){
30512             
30513             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30514
30515         }, this);
30516         
30517     },
30518     
30519     getVerticalOneBoxColPositions : function(x, y, box)
30520     {
30521         var pos = [];
30522         
30523         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30524         
30525         if(box[0].size == 'md-left'){
30526             rand = 0;
30527         }
30528         
30529         if(box[0].size == 'md-right'){
30530             rand = 1;
30531         }
30532         
30533         pos.push({
30534             x : x + (this.unitWidth + this.gutter) * rand,
30535             y : y
30536         });
30537         
30538         return pos;
30539     },
30540     
30541     getVerticalTwoBoxColPositions : function(x, y, box)
30542     {
30543         var pos = [];
30544         
30545         if(box[0].size == 'xs'){
30546             
30547             pos.push({
30548                 x : x,
30549                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30550             });
30551
30552             pos.push({
30553                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30554                 y : y
30555             });
30556             
30557             return pos;
30558             
30559         }
30560         
30561         pos.push({
30562             x : x,
30563             y : y
30564         });
30565
30566         pos.push({
30567             x : x + (this.unitWidth + this.gutter) * 2,
30568             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30569         });
30570         
30571         return pos;
30572         
30573     },
30574     
30575     getVerticalThreeBoxColPositions : function(x, y, box)
30576     {
30577         var pos = [];
30578         
30579         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30580             
30581             pos.push({
30582                 x : x,
30583                 y : y
30584             });
30585
30586             pos.push({
30587                 x : x + (this.unitWidth + this.gutter) * 1,
30588                 y : y
30589             });
30590             
30591             pos.push({
30592                 x : x + (this.unitWidth + this.gutter) * 2,
30593                 y : y
30594             });
30595             
30596             return pos;
30597             
30598         }
30599         
30600         if(box[0].size == 'xs' && box[1].size == 'xs'){
30601             
30602             pos.push({
30603                 x : x,
30604                 y : y
30605             });
30606
30607             pos.push({
30608                 x : x,
30609                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30610             });
30611             
30612             pos.push({
30613                 x : x + (this.unitWidth + this.gutter) * 1,
30614                 y : y
30615             });
30616             
30617             return pos;
30618             
30619         }
30620         
30621         pos.push({
30622             x : x,
30623             y : y
30624         });
30625
30626         pos.push({
30627             x : x + (this.unitWidth + this.gutter) * 2,
30628             y : y
30629         });
30630
30631         pos.push({
30632             x : x + (this.unitWidth + this.gutter) * 2,
30633             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30634         });
30635             
30636         return pos;
30637         
30638     },
30639     
30640     getVerticalFourBoxColPositions : function(x, y, box)
30641     {
30642         var pos = [];
30643         
30644         if(box[0].size == 'xs'){
30645             
30646             pos.push({
30647                 x : x,
30648                 y : y
30649             });
30650
30651             pos.push({
30652                 x : x,
30653                 y : y + (this.unitHeight + this.gutter) * 1
30654             });
30655             
30656             pos.push({
30657                 x : x,
30658                 y : y + (this.unitHeight + this.gutter) * 2
30659             });
30660             
30661             pos.push({
30662                 x : x + (this.unitWidth + this.gutter) * 1,
30663                 y : y
30664             });
30665             
30666             return pos;
30667             
30668         }
30669         
30670         pos.push({
30671             x : x,
30672             y : y
30673         });
30674
30675         pos.push({
30676             x : x + (this.unitWidth + this.gutter) * 2,
30677             y : y
30678         });
30679
30680         pos.push({
30681             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30682             y : y + (this.unitHeight + this.gutter) * 1
30683         });
30684
30685         pos.push({
30686             x : x + (this.unitWidth + this.gutter) * 2,
30687             y : y + (this.unitWidth + this.gutter) * 2
30688         });
30689
30690         return pos;
30691         
30692     },
30693     
30694     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30695     {
30696         var pos = [];
30697         
30698         if(box[0].size == 'md-left'){
30699             pos.push({
30700                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30701                 y : minY
30702             });
30703             
30704             return pos;
30705         }
30706         
30707         if(box[0].size == 'md-right'){
30708             pos.push({
30709                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30710                 y : minY + (this.unitWidth + this.gutter) * 1
30711             });
30712             
30713             return pos;
30714         }
30715         
30716         var rand = Math.floor(Math.random() * (4 - box[0].y));
30717         
30718         pos.push({
30719             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30720             y : minY + (this.unitWidth + this.gutter) * rand
30721         });
30722         
30723         return pos;
30724         
30725     },
30726     
30727     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30728     {
30729         var pos = [];
30730         
30731         if(box[0].size == 'xs'){
30732             
30733             pos.push({
30734                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30735                 y : minY
30736             });
30737
30738             pos.push({
30739                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30740                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30741             });
30742             
30743             return pos;
30744             
30745         }
30746         
30747         pos.push({
30748             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30749             y : minY
30750         });
30751
30752         pos.push({
30753             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30754             y : minY + (this.unitWidth + this.gutter) * 2
30755         });
30756         
30757         return pos;
30758         
30759     },
30760     
30761     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30762     {
30763         var pos = [];
30764         
30765         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30766             
30767             pos.push({
30768                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30769                 y : minY
30770             });
30771
30772             pos.push({
30773                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30774                 y : minY + (this.unitWidth + this.gutter) * 1
30775             });
30776             
30777             pos.push({
30778                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30779                 y : minY + (this.unitWidth + this.gutter) * 2
30780             });
30781             
30782             return pos;
30783             
30784         }
30785         
30786         if(box[0].size == 'xs' && box[1].size == 'xs'){
30787             
30788             pos.push({
30789                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30790                 y : minY
30791             });
30792
30793             pos.push({
30794                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30795                 y : minY
30796             });
30797             
30798             pos.push({
30799                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30800                 y : minY + (this.unitWidth + this.gutter) * 1
30801             });
30802             
30803             return pos;
30804             
30805         }
30806         
30807         pos.push({
30808             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30809             y : minY
30810         });
30811
30812         pos.push({
30813             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30814             y : minY + (this.unitWidth + this.gutter) * 2
30815         });
30816
30817         pos.push({
30818             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30819             y : minY + (this.unitWidth + this.gutter) * 2
30820         });
30821             
30822         return pos;
30823         
30824     },
30825     
30826     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30827     {
30828         var pos = [];
30829         
30830         if(box[0].size == 'xs'){
30831             
30832             pos.push({
30833                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30834                 y : minY
30835             });
30836
30837             pos.push({
30838                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30839                 y : minY
30840             });
30841             
30842             pos.push({
30843                 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),
30844                 y : minY
30845             });
30846             
30847             pos.push({
30848                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30849                 y : minY + (this.unitWidth + this.gutter) * 1
30850             });
30851             
30852             return pos;
30853             
30854         }
30855         
30856         pos.push({
30857             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30858             y : minY
30859         });
30860         
30861         pos.push({
30862             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30863             y : minY + (this.unitWidth + this.gutter) * 2
30864         });
30865         
30866         pos.push({
30867             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30868             y : minY + (this.unitWidth + this.gutter) * 2
30869         });
30870         
30871         pos.push({
30872             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),
30873             y : minY + (this.unitWidth + this.gutter) * 2
30874         });
30875
30876         return pos;
30877         
30878     }
30879     
30880 });
30881
30882  
30883
30884  /**
30885  *
30886  * This is based on 
30887  * http://masonry.desandro.com
30888  *
30889  * The idea is to render all the bricks based on vertical width...
30890  *
30891  * The original code extends 'outlayer' - we might need to use that....
30892  * 
30893  */
30894
30895
30896 /**
30897  * @class Roo.bootstrap.LayoutMasonryAuto
30898  * @extends Roo.bootstrap.Component
30899  * Bootstrap Layout Masonry class
30900  * 
30901  * @constructor
30902  * Create a new Element
30903  * @param {Object} config The config object
30904  */
30905
30906 Roo.bootstrap.LayoutMasonryAuto = function(config){
30907     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30908 };
30909
30910 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30911     
30912       /**
30913      * @cfg {Boolean} isFitWidth  - resize the width..
30914      */   
30915     isFitWidth : false,  // options..
30916     /**
30917      * @cfg {Boolean} isOriginLeft = left align?
30918      */   
30919     isOriginLeft : true,
30920     /**
30921      * @cfg {Boolean} isOriginTop = top align?
30922      */   
30923     isOriginTop : false,
30924     /**
30925      * @cfg {Boolean} isLayoutInstant = no animation?
30926      */   
30927     isLayoutInstant : false, // needed?
30928     /**
30929      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30930      */   
30931     isResizingContainer : true,
30932     /**
30933      * @cfg {Number} columnWidth  width of the columns 
30934      */   
30935     
30936     columnWidth : 0,
30937     
30938     /**
30939      * @cfg {Number} maxCols maximum number of columns
30940      */   
30941     
30942     maxCols: 0,
30943     /**
30944      * @cfg {Number} padHeight padding below box..
30945      */   
30946     
30947     padHeight : 10, 
30948     
30949     /**
30950      * @cfg {Boolean} isAutoInitial defalut true
30951      */   
30952     
30953     isAutoInitial : true, 
30954     
30955     // private?
30956     gutter : 0,
30957     
30958     containerWidth: 0,
30959     initialColumnWidth : 0,
30960     currentSize : null,
30961     
30962     colYs : null, // array.
30963     maxY : 0,
30964     padWidth: 10,
30965     
30966     
30967     tag: 'div',
30968     cls: '',
30969     bricks: null, //CompositeElement
30970     cols : 0, // array?
30971     // element : null, // wrapped now this.el
30972     _isLayoutInited : null, 
30973     
30974     
30975     getAutoCreate : function(){
30976         
30977         var cfg = {
30978             tag: this.tag,
30979             cls: 'blog-masonary-wrapper ' + this.cls,
30980             cn : {
30981                 cls : 'mas-boxes masonary'
30982             }
30983         };
30984         
30985         return cfg;
30986     },
30987     
30988     getChildContainer: function( )
30989     {
30990         if (this.boxesEl) {
30991             return this.boxesEl;
30992         }
30993         
30994         this.boxesEl = this.el.select('.mas-boxes').first();
30995         
30996         return this.boxesEl;
30997     },
30998     
30999     
31000     initEvents : function()
31001     {
31002         var _this = this;
31003         
31004         if(this.isAutoInitial){
31005             Roo.log('hook children rendered');
31006             this.on('childrenrendered', function() {
31007                 Roo.log('children rendered');
31008                 _this.initial();
31009             } ,this);
31010         }
31011         
31012     },
31013     
31014     initial : function()
31015     {
31016         this.reloadItems();
31017
31018         this.currentSize = this.el.getBox(true);
31019
31020         /// was window resize... - let's see if this works..
31021         Roo.EventManager.onWindowResize(this.resize, this); 
31022
31023         if(!this.isAutoInitial){
31024             this.layout();
31025             return;
31026         }
31027         
31028         this.layout.defer(500,this);
31029     },
31030     
31031     reloadItems: function()
31032     {
31033         this.bricks = this.el.select('.masonry-brick', true);
31034         
31035         this.bricks.each(function(b) {
31036             //Roo.log(b.getSize());
31037             if (!b.attr('originalwidth')) {
31038                 b.attr('originalwidth',  b.getSize().width);
31039             }
31040             
31041         });
31042         
31043         Roo.log(this.bricks.elements.length);
31044     },
31045     
31046     resize : function()
31047     {
31048         Roo.log('resize');
31049         var cs = this.el.getBox(true);
31050         
31051         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31052             Roo.log("no change in with or X");
31053             return;
31054         }
31055         this.currentSize = cs;
31056         this.layout();
31057     },
31058     
31059     layout : function()
31060     {
31061          Roo.log('layout');
31062         this._resetLayout();
31063         //this._manageStamps();
31064       
31065         // don't animate first layout
31066         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31067         this.layoutItems( isInstant );
31068       
31069         // flag for initalized
31070         this._isLayoutInited = true;
31071     },
31072     
31073     layoutItems : function( isInstant )
31074     {
31075         //var items = this._getItemsForLayout( this.items );
31076         // original code supports filtering layout items.. we just ignore it..
31077         
31078         this._layoutItems( this.bricks , isInstant );
31079       
31080         this._postLayout();
31081     },
31082     _layoutItems : function ( items , isInstant)
31083     {
31084        //this.fireEvent( 'layout', this, items );
31085     
31086
31087         if ( !items || !items.elements.length ) {
31088           // no items, emit event with empty array
31089             return;
31090         }
31091
31092         var queue = [];
31093         items.each(function(item) {
31094             Roo.log("layout item");
31095             Roo.log(item);
31096             // get x/y object from method
31097             var position = this._getItemLayoutPosition( item );
31098             // enqueue
31099             position.item = item;
31100             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31101             queue.push( position );
31102         }, this);
31103       
31104         this._processLayoutQueue( queue );
31105     },
31106     /** Sets position of item in DOM
31107     * @param {Element} item
31108     * @param {Number} x - horizontal position
31109     * @param {Number} y - vertical position
31110     * @param {Boolean} isInstant - disables transitions
31111     */
31112     _processLayoutQueue : function( queue )
31113     {
31114         for ( var i=0, len = queue.length; i < len; i++ ) {
31115             var obj = queue[i];
31116             obj.item.position('absolute');
31117             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31118         }
31119     },
31120       
31121     
31122     /**
31123     * Any logic you want to do after each layout,
31124     * i.e. size the container
31125     */
31126     _postLayout : function()
31127     {
31128         this.resizeContainer();
31129     },
31130     
31131     resizeContainer : function()
31132     {
31133         if ( !this.isResizingContainer ) {
31134             return;
31135         }
31136         var size = this._getContainerSize();
31137         if ( size ) {
31138             this.el.setSize(size.width,size.height);
31139             this.boxesEl.setSize(size.width,size.height);
31140         }
31141     },
31142     
31143     
31144     
31145     _resetLayout : function()
31146     {
31147         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31148         this.colWidth = this.el.getWidth();
31149         //this.gutter = this.el.getWidth(); 
31150         
31151         this.measureColumns();
31152
31153         // reset column Y
31154         var i = this.cols;
31155         this.colYs = [];
31156         while (i--) {
31157             this.colYs.push( 0 );
31158         }
31159     
31160         this.maxY = 0;
31161     },
31162
31163     measureColumns : function()
31164     {
31165         this.getContainerWidth();
31166       // if columnWidth is 0, default to outerWidth of first item
31167         if ( !this.columnWidth ) {
31168             var firstItem = this.bricks.first();
31169             Roo.log(firstItem);
31170             this.columnWidth  = this.containerWidth;
31171             if (firstItem && firstItem.attr('originalwidth') ) {
31172                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31173             }
31174             // columnWidth fall back to item of first element
31175             Roo.log("set column width?");
31176                         this.initialColumnWidth = this.columnWidth  ;
31177
31178             // if first elem has no width, default to size of container
31179             
31180         }
31181         
31182         
31183         if (this.initialColumnWidth) {
31184             this.columnWidth = this.initialColumnWidth;
31185         }
31186         
31187         
31188             
31189         // column width is fixed at the top - however if container width get's smaller we should
31190         // reduce it...
31191         
31192         // this bit calcs how man columns..
31193             
31194         var columnWidth = this.columnWidth += this.gutter;
31195       
31196         // calculate columns
31197         var containerWidth = this.containerWidth + this.gutter;
31198         
31199         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31200         // fix rounding errors, typically with gutters
31201         var excess = columnWidth - containerWidth % columnWidth;
31202         
31203         
31204         // if overshoot is less than a pixel, round up, otherwise floor it
31205         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31206         cols = Math[ mathMethod ]( cols );
31207         this.cols = Math.max( cols, 1 );
31208         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31209         
31210          // padding positioning..
31211         var totalColWidth = this.cols * this.columnWidth;
31212         var padavail = this.containerWidth - totalColWidth;
31213         // so for 2 columns - we need 3 'pads'
31214         
31215         var padNeeded = (1+this.cols) * this.padWidth;
31216         
31217         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31218         
31219         this.columnWidth += padExtra
31220         //this.padWidth = Math.floor(padavail /  ( this.cols));
31221         
31222         // adjust colum width so that padding is fixed??
31223         
31224         // we have 3 columns ... total = width * 3
31225         // we have X left over... that should be used by 
31226         
31227         //if (this.expandC) {
31228             
31229         //}
31230         
31231         
31232         
31233     },
31234     
31235     getContainerWidth : function()
31236     {
31237        /* // container is parent if fit width
31238         var container = this.isFitWidth ? this.element.parentNode : this.element;
31239         // check that this.size and size are there
31240         // IE8 triggers resize on body size change, so they might not be
31241         
31242         var size = getSize( container );  //FIXME
31243         this.containerWidth = size && size.innerWidth; //FIXME
31244         */
31245          
31246         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31247         
31248     },
31249     
31250     _getItemLayoutPosition : function( item )  // what is item?
31251     {
31252         // we resize the item to our columnWidth..
31253       
31254         item.setWidth(this.columnWidth);
31255         item.autoBoxAdjust  = false;
31256         
31257         var sz = item.getSize();
31258  
31259         // how many columns does this brick span
31260         var remainder = this.containerWidth % this.columnWidth;
31261         
31262         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31263         // round if off by 1 pixel, otherwise use ceil
31264         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31265         colSpan = Math.min( colSpan, this.cols );
31266         
31267         // normally this should be '1' as we dont' currently allow multi width columns..
31268         
31269         var colGroup = this._getColGroup( colSpan );
31270         // get the minimum Y value from the columns
31271         var minimumY = Math.min.apply( Math, colGroup );
31272         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31273         
31274         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31275          
31276         // position the brick
31277         var position = {
31278             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31279             y: this.currentSize.y + minimumY + this.padHeight
31280         };
31281         
31282         Roo.log(position);
31283         // apply setHeight to necessary columns
31284         var setHeight = minimumY + sz.height + this.padHeight;
31285         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31286         
31287         var setSpan = this.cols + 1 - colGroup.length;
31288         for ( var i = 0; i < setSpan; i++ ) {
31289           this.colYs[ shortColIndex + i ] = setHeight ;
31290         }
31291       
31292         return position;
31293     },
31294     
31295     /**
31296      * @param {Number} colSpan - number of columns the element spans
31297      * @returns {Array} colGroup
31298      */
31299     _getColGroup : function( colSpan )
31300     {
31301         if ( colSpan < 2 ) {
31302           // if brick spans only one column, use all the column Ys
31303           return this.colYs;
31304         }
31305       
31306         var colGroup = [];
31307         // how many different places could this brick fit horizontally
31308         var groupCount = this.cols + 1 - colSpan;
31309         // for each group potential horizontal position
31310         for ( var i = 0; i < groupCount; i++ ) {
31311           // make an array of colY values for that one group
31312           var groupColYs = this.colYs.slice( i, i + colSpan );
31313           // and get the max value of the array
31314           colGroup[i] = Math.max.apply( Math, groupColYs );
31315         }
31316         return colGroup;
31317     },
31318     /*
31319     _manageStamp : function( stamp )
31320     {
31321         var stampSize =  stamp.getSize();
31322         var offset = stamp.getBox();
31323         // get the columns that this stamp affects
31324         var firstX = this.isOriginLeft ? offset.x : offset.right;
31325         var lastX = firstX + stampSize.width;
31326         var firstCol = Math.floor( firstX / this.columnWidth );
31327         firstCol = Math.max( 0, firstCol );
31328         
31329         var lastCol = Math.floor( lastX / this.columnWidth );
31330         // lastCol should not go over if multiple of columnWidth #425
31331         lastCol -= lastX % this.columnWidth ? 0 : 1;
31332         lastCol = Math.min( this.cols - 1, lastCol );
31333         
31334         // set colYs to bottom of the stamp
31335         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31336             stampSize.height;
31337             
31338         for ( var i = firstCol; i <= lastCol; i++ ) {
31339           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31340         }
31341     },
31342     */
31343     
31344     _getContainerSize : function()
31345     {
31346         this.maxY = Math.max.apply( Math, this.colYs );
31347         var size = {
31348             height: this.maxY
31349         };
31350       
31351         if ( this.isFitWidth ) {
31352             size.width = this._getContainerFitWidth();
31353         }
31354       
31355         return size;
31356     },
31357     
31358     _getContainerFitWidth : function()
31359     {
31360         var unusedCols = 0;
31361         // count unused columns
31362         var i = this.cols;
31363         while ( --i ) {
31364           if ( this.colYs[i] !== 0 ) {
31365             break;
31366           }
31367           unusedCols++;
31368         }
31369         // fit container to columns that have been used
31370         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31371     },
31372     
31373     needsResizeLayout : function()
31374     {
31375         var previousWidth = this.containerWidth;
31376         this.getContainerWidth();
31377         return previousWidth !== this.containerWidth;
31378     }
31379  
31380 });
31381
31382  
31383
31384  /*
31385  * - LGPL
31386  *
31387  * element
31388  * 
31389  */
31390
31391 /**
31392  * @class Roo.bootstrap.MasonryBrick
31393  * @extends Roo.bootstrap.Component
31394  * Bootstrap MasonryBrick class
31395  * 
31396  * @constructor
31397  * Create a new MasonryBrick
31398  * @param {Object} config The config object
31399  */
31400
31401 Roo.bootstrap.MasonryBrick = function(config){
31402     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31403     
31404     this.addEvents({
31405         // raw events
31406         /**
31407          * @event click
31408          * When a MasonryBrick is clcik
31409          * @param {Roo.bootstrap.MasonryBrick} this
31410          * @param {Roo.EventObject} e
31411          */
31412         "click" : true
31413     });
31414 };
31415
31416 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31417     
31418     /**
31419      * @cfg {String} title
31420      */   
31421     title : '',
31422     /**
31423      * @cfg {String} html
31424      */   
31425     html : '',
31426     /**
31427      * @cfg {String} bgimage
31428      */   
31429     bgimage : '',
31430     /**
31431      * @cfg {String} videourl
31432      */   
31433     videourl : '',
31434     /**
31435      * @cfg {String} cls
31436      */   
31437     cls : '',
31438     /**
31439      * @cfg {String} href
31440      */   
31441     href : '',
31442     /**
31443      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
31444      */   
31445     size : 'xs',
31446     
31447     /**
31448      * @cfg {String} placetitle (center|bottom)
31449      */   
31450     placetitle : '',
31451     
31452     /**
31453      * @cfg {Boolean} isFitContainer defalut true
31454      */   
31455     isFitContainer : true, 
31456     
31457     /**
31458      * @cfg {Boolean} preventDefault defalut false
31459      */   
31460     preventDefault : false, 
31461     
31462     /**
31463      * @cfg {Boolean} inverse defalut false
31464      */   
31465     maskInverse : false, 
31466     
31467     getAutoCreate : function()
31468     {
31469         if(!this.isFitContainer){
31470             return this.getSplitAutoCreate();
31471         }
31472         
31473         var cls = 'masonry-brick masonry-brick-full';
31474         
31475         if(this.href.length){
31476             cls += ' masonry-brick-link';
31477         }
31478         
31479         if(this.bgimage.length){
31480             cls += ' masonry-brick-image';
31481         }
31482         
31483         if(this.maskInverse){
31484             cls += ' mask-inverse';
31485         }
31486         
31487         if(!this.html.length && !this.maskInverse){
31488             cls += ' enable-mask';
31489         }
31490         
31491         if(this.size){
31492             cls += ' masonry-' + this.size + '-brick';
31493         }
31494         
31495         if(this.placetitle.length){
31496             
31497             switch (this.placetitle) {
31498                 case 'center' :
31499                     cls += ' masonry-center-title';
31500                     break;
31501                 case 'bottom' :
31502                     cls += ' masonry-bottom-title';
31503                     break;
31504                 default:
31505                     break;
31506             }
31507             
31508         } else {
31509             if(!this.html.length && !this.bgimage.length){
31510                 cls += ' masonry-center-title';
31511             }
31512
31513             if(!this.html.length && this.bgimage.length){
31514                 cls += ' masonry-bottom-title';
31515             }
31516         }
31517         
31518         if(this.cls){
31519             cls += ' ' + this.cls;
31520         }
31521         
31522         var cfg = {
31523             tag: (this.href.length) ? 'a' : 'div',
31524             cls: cls,
31525             cn: [
31526                 {
31527                     tag: 'div',
31528                     cls: 'masonry-brick-paragraph',
31529                     cn: []
31530                 }
31531             ]
31532         };
31533         
31534         if(this.href.length){
31535             cfg.href = this.href;
31536         }
31537         
31538         var cn = cfg.cn[0].cn;
31539         
31540         if(this.title.length){
31541             cn.push({
31542                 tag: 'h4',
31543                 cls: 'masonry-brick-title',
31544                 html: this.title
31545             });
31546         }
31547         
31548         if(this.html.length){
31549             cn.push({
31550                 tag: 'p',
31551                 cls: 'masonry-brick-text',
31552                 html: this.html
31553             });
31554         }  
31555         if (!this.title.length && !this.html.length) {
31556             cfg.cn[0].cls += ' hide';
31557         }
31558         
31559         if(this.bgimage.length){
31560             cfg.cn.push({
31561                 tag: 'img',
31562                 cls: 'masonry-brick-image-view',
31563                 src: this.bgimage
31564             });
31565         }
31566         
31567         if(this.videourl.length){
31568             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31569             // youtube support only?
31570             cfg.cn.push({
31571                 tag: 'iframe',
31572                 cls: 'masonry-brick-image-view',
31573                 src: vurl,
31574                 frameborder : 0,
31575                 allowfullscreen : true
31576             });
31577             
31578             
31579         }
31580         
31581         cfg.cn.push({
31582             tag: 'div',
31583             cls: 'masonry-brick-mask'
31584         });
31585         
31586         return cfg;
31587         
31588     },
31589     
31590     getSplitAutoCreate : function()
31591     {
31592         var cls = 'masonry-brick masonry-brick-split';
31593         
31594         if(this.href.length){
31595             cls += ' masonry-brick-link';
31596         }
31597         
31598         if(this.bgimage.length){
31599             cls += ' masonry-brick-image';
31600         }
31601         
31602         if(this.size){
31603             cls += ' masonry-' + this.size + '-brick';
31604         }
31605         
31606         switch (this.placetitle) {
31607             case 'center' :
31608                 cls += ' masonry-center-title';
31609                 break;
31610             case 'bottom' :
31611                 cls += ' masonry-bottom-title';
31612                 break;
31613             default:
31614                 if(!this.bgimage.length){
31615                     cls += ' masonry-center-title';
31616                 }
31617
31618                 if(this.bgimage.length){
31619                     cls += ' masonry-bottom-title';
31620                 }
31621                 break;
31622         }
31623         
31624         if(this.cls){
31625             cls += ' ' + this.cls;
31626         }
31627         
31628         var cfg = {
31629             tag: (this.href.length) ? 'a' : 'div',
31630             cls: cls,
31631             cn: [
31632                 {
31633                     tag: 'div',
31634                     cls: 'masonry-brick-split-head',
31635                     cn: [
31636                         {
31637                             tag: 'div',
31638                             cls: 'masonry-brick-paragraph',
31639                             cn: []
31640                         }
31641                     ]
31642                 },
31643                 {
31644                     tag: 'div',
31645                     cls: 'masonry-brick-split-body',
31646                     cn: []
31647                 }
31648             ]
31649         };
31650         
31651         if(this.href.length){
31652             cfg.href = this.href;
31653         }
31654         
31655         if(this.title.length){
31656             cfg.cn[0].cn[0].cn.push({
31657                 tag: 'h4',
31658                 cls: 'masonry-brick-title',
31659                 html: this.title
31660             });
31661         }
31662         
31663         if(this.html.length){
31664             cfg.cn[1].cn.push({
31665                 tag: 'p',
31666                 cls: 'masonry-brick-text',
31667                 html: this.html
31668             });
31669         }
31670
31671         if(this.bgimage.length){
31672             cfg.cn[0].cn.push({
31673                 tag: 'img',
31674                 cls: 'masonry-brick-image-view',
31675                 src: this.bgimage
31676             });
31677         }
31678         
31679         if(this.videourl.length){
31680             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31681             // youtube support only?
31682             cfg.cn[0].cn.cn.push({
31683                 tag: 'iframe',
31684                 cls: 'masonry-brick-image-view',
31685                 src: vurl,
31686                 frameborder : 0,
31687                 allowfullscreen : true
31688             });
31689         }
31690         
31691         return cfg;
31692     },
31693     
31694     initEvents: function() 
31695     {
31696         switch (this.size) {
31697             case 'xs' :
31698                 this.x = 1;
31699                 this.y = 1;
31700                 break;
31701             case 'sm' :
31702                 this.x = 2;
31703                 this.y = 2;
31704                 break;
31705             case 'md' :
31706             case 'md-left' :
31707             case 'md-right' :
31708                 this.x = 3;
31709                 this.y = 3;
31710                 break;
31711             case 'tall' :
31712                 this.x = 2;
31713                 this.y = 3;
31714                 break;
31715             case 'wide' :
31716                 this.x = 3;
31717                 this.y = 2;
31718                 break;
31719             case 'wide-thin' :
31720                 this.x = 3;
31721                 this.y = 1;
31722                 break;
31723                         
31724             default :
31725                 break;
31726         }
31727         
31728         if(Roo.isTouch){
31729             this.el.on('touchstart', this.onTouchStart, this);
31730             this.el.on('touchmove', this.onTouchMove, this);
31731             this.el.on('touchend', this.onTouchEnd, this);
31732             this.el.on('contextmenu', this.onContextMenu, this);
31733         } else {
31734             this.el.on('mouseenter'  ,this.enter, this);
31735             this.el.on('mouseleave', this.leave, this);
31736             this.el.on('click', this.onClick, this);
31737         }
31738         
31739         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31740             this.parent().bricks.push(this);   
31741         }
31742         
31743     },
31744     
31745     onClick: function(e, el)
31746     {
31747         var time = this.endTimer - this.startTimer;
31748         
31749         if(Roo.isTouch){
31750             if(time > 1000){
31751                 e.preventDefault();
31752                 return;
31753             }
31754         }
31755         
31756         if(!this.preventDefault){
31757             return;
31758         }
31759         
31760         e.preventDefault();
31761         this.fireEvent('click', this);
31762     },
31763     
31764     enter: function(e, el)
31765     {
31766         e.preventDefault();
31767         
31768         if(!this.isFitContainer || this.maskInverse){
31769             return;
31770         }
31771         
31772         if(this.bgimage.length && this.html.length){
31773             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31774         }
31775     },
31776     
31777     leave: function(e, el)
31778     {
31779         e.preventDefault();
31780         
31781         if(!this.isFitContainer || this.maskInverse){
31782             return;
31783         }
31784         
31785         if(this.bgimage.length && this.html.length){
31786             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31787         }
31788     },
31789     
31790     onTouchStart: function(e, el)
31791     {
31792 //        e.preventDefault();
31793         
31794         this.touchmoved = false;
31795         
31796         if(!this.isFitContainer){
31797             return;
31798         }
31799         
31800         if(!this.bgimage.length || !this.html.length){
31801             return;
31802         }
31803         
31804         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31805         
31806         this.timer = new Date().getTime();
31807         
31808     },
31809     
31810     onTouchMove: function(e, el)
31811     {
31812         this.touchmoved = true;
31813     },
31814     
31815     onContextMenu : function(e,el)
31816     {
31817         e.preventDefault();
31818         e.stopPropagation();
31819         return false;
31820     },
31821     
31822     onTouchEnd: function(e, el)
31823     {
31824 //        e.preventDefault();
31825         
31826         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31827         
31828             this.leave(e,el);
31829             
31830             return;
31831         }
31832         
31833         if(!this.bgimage.length || !this.html.length){
31834             
31835             if(this.href.length){
31836                 window.location.href = this.href;
31837             }
31838             
31839             return;
31840         }
31841         
31842         if(!this.isFitContainer){
31843             return;
31844         }
31845         
31846         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31847         
31848         window.location.href = this.href;
31849     }
31850     
31851 });
31852
31853  
31854
31855  /*
31856  * - LGPL
31857  *
31858  * element
31859  * 
31860  */
31861
31862 /**
31863  * @class Roo.bootstrap.Brick
31864  * @extends Roo.bootstrap.Component
31865  * Bootstrap Brick class
31866  * 
31867  * @constructor
31868  * Create a new Brick
31869  * @param {Object} config The config object
31870  */
31871
31872 Roo.bootstrap.Brick = function(config){
31873     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31874     
31875     this.addEvents({
31876         // raw events
31877         /**
31878          * @event click
31879          * When a Brick is click
31880          * @param {Roo.bootstrap.Brick} this
31881          * @param {Roo.EventObject} e
31882          */
31883         "click" : true
31884     });
31885 };
31886
31887 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31888     
31889     /**
31890      * @cfg {String} title
31891      */   
31892     title : '',
31893     /**
31894      * @cfg {String} html
31895      */   
31896     html : '',
31897     /**
31898      * @cfg {String} bgimage
31899      */   
31900     bgimage : '',
31901     /**
31902      * @cfg {String} cls
31903      */   
31904     cls : '',
31905     /**
31906      * @cfg {String} href
31907      */   
31908     href : '',
31909     /**
31910      * @cfg {String} video
31911      */   
31912     video : '',
31913     /**
31914      * @cfg {Boolean} square
31915      */   
31916     square : true,
31917     
31918     getAutoCreate : function()
31919     {
31920         var cls = 'roo-brick';
31921         
31922         if(this.href.length){
31923             cls += ' roo-brick-link';
31924         }
31925         
31926         if(this.bgimage.length){
31927             cls += ' roo-brick-image';
31928         }
31929         
31930         if(!this.html.length && !this.bgimage.length){
31931             cls += ' roo-brick-center-title';
31932         }
31933         
31934         if(!this.html.length && this.bgimage.length){
31935             cls += ' roo-brick-bottom-title';
31936         }
31937         
31938         if(this.cls){
31939             cls += ' ' + this.cls;
31940         }
31941         
31942         var cfg = {
31943             tag: (this.href.length) ? 'a' : 'div',
31944             cls: cls,
31945             cn: [
31946                 {
31947                     tag: 'div',
31948                     cls: 'roo-brick-paragraph',
31949                     cn: []
31950                 }
31951             ]
31952         };
31953         
31954         if(this.href.length){
31955             cfg.href = this.href;
31956         }
31957         
31958         var cn = cfg.cn[0].cn;
31959         
31960         if(this.title.length){
31961             cn.push({
31962                 tag: 'h4',
31963                 cls: 'roo-brick-title',
31964                 html: this.title
31965             });
31966         }
31967         
31968         if(this.html.length){
31969             cn.push({
31970                 tag: 'p',
31971                 cls: 'roo-brick-text',
31972                 html: this.html
31973             });
31974         } else {
31975             cn.cls += ' hide';
31976         }
31977         
31978         if(this.bgimage.length){
31979             cfg.cn.push({
31980                 tag: 'img',
31981                 cls: 'roo-brick-image-view',
31982                 src: this.bgimage
31983             });
31984         }
31985         
31986         return cfg;
31987     },
31988     
31989     initEvents: function() 
31990     {
31991         if(this.title.length || this.html.length){
31992             this.el.on('mouseenter'  ,this.enter, this);
31993             this.el.on('mouseleave', this.leave, this);
31994         }
31995         
31996         
31997         Roo.EventManager.onWindowResize(this.resize, this); 
31998         
31999         this.resize();
32000     },
32001     
32002     resize : function()
32003     {
32004         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32005         
32006         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32007         
32008         if(this.bgimage.length){
32009             var image = this.el.select('.roo-brick-image-view', true).first();
32010             image.setWidth(paragraph.getWidth());
32011             image.setHeight(paragraph.getWidth());
32012             
32013             this.el.setHeight(paragraph.getWidth());
32014             
32015         }
32016         
32017     },
32018     
32019     enter: function(e, el)
32020     {
32021         e.preventDefault();
32022         
32023         if(this.bgimage.length){
32024             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32025             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32026         }
32027     },
32028     
32029     leave: function(e, el)
32030     {
32031         e.preventDefault();
32032         
32033         if(this.bgimage.length){
32034             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32035             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32036         }
32037     }
32038     
32039 });
32040
32041  
32042
32043  /*
32044  * - LGPL
32045  *
32046  * Input
32047  * 
32048  */
32049
32050 /**
32051  * @class Roo.bootstrap.NumberField
32052  * @extends Roo.bootstrap.Input
32053  * Bootstrap NumberField class
32054  * 
32055  * 
32056  * 
32057  * 
32058  * @constructor
32059  * Create a new NumberField
32060  * @param {Object} config The config object
32061  */
32062
32063 Roo.bootstrap.NumberField = function(config){
32064     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32065 };
32066
32067 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32068     
32069     /**
32070      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32071      */
32072     allowDecimals : true,
32073     /**
32074      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32075      */
32076     decimalSeparator : ".",
32077     /**
32078      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32079      */
32080     decimalPrecision : 2,
32081     /**
32082      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32083      */
32084     allowNegative : true,
32085     /**
32086      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32087      */
32088     minValue : Number.NEGATIVE_INFINITY,
32089     /**
32090      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32091      */
32092     maxValue : Number.MAX_VALUE,
32093     /**
32094      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32095      */
32096     minText : "The minimum value for this field is {0}",
32097     /**
32098      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32099      */
32100     maxText : "The maximum value for this field is {0}",
32101     /**
32102      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32103      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32104      */
32105     nanText : "{0} is not a valid number",
32106     /**
32107      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32108      */
32109     castInt : true,
32110
32111     // private
32112     initEvents : function()
32113     {   
32114         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32115         
32116         var allowed = "0123456789";
32117         
32118         if(this.allowDecimals){
32119             allowed += this.decimalSeparator;
32120         }
32121         
32122         if(this.allowNegative){
32123             allowed += "-";
32124         }
32125         
32126         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32127         
32128         var keyPress = function(e){
32129             
32130             var k = e.getKey();
32131             
32132             var c = e.getCharCode();
32133             
32134             if(
32135                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32136                     allowed.indexOf(String.fromCharCode(c)) === -1
32137             ){
32138                 e.stopEvent();
32139                 return;
32140             }
32141             
32142             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32143                 return;
32144             }
32145             
32146             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32147                 e.stopEvent();
32148             }
32149         };
32150         
32151         this.el.on("keypress", keyPress, this);
32152     },
32153     
32154     validateValue : function(value)
32155     {
32156         
32157         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32158             return false;
32159         }
32160         
32161         var num = this.parseValue(value);
32162         
32163         if(isNaN(num)){
32164             this.markInvalid(String.format(this.nanText, value));
32165             return false;
32166         }
32167         
32168         if(num < this.minValue){
32169             this.markInvalid(String.format(this.minText, this.minValue));
32170             return false;
32171         }
32172         
32173         if(num > this.maxValue){
32174             this.markInvalid(String.format(this.maxText, this.maxValue));
32175             return false;
32176         }
32177         
32178         return true;
32179     },
32180
32181     getValue : function()
32182     {
32183         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32184     },
32185
32186     parseValue : function(value)
32187     {
32188         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32189         return isNaN(value) ? '' : value;
32190     },
32191
32192     fixPrecision : function(value)
32193     {
32194         var nan = isNaN(value);
32195         
32196         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32197             return nan ? '' : value;
32198         }
32199         return parseFloat(value).toFixed(this.decimalPrecision);
32200     },
32201
32202     setValue : function(v)
32203     {
32204         v = this.fixPrecision(v);
32205         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32206     },
32207
32208     decimalPrecisionFcn : function(v)
32209     {
32210         return Math.floor(v);
32211     },
32212
32213     beforeBlur : function()
32214     {
32215         if(!this.castInt){
32216             return;
32217         }
32218         
32219         var v = this.parseValue(this.getRawValue());
32220         if(v){
32221             this.setValue(v);
32222         }
32223     }
32224     
32225 });
32226
32227  
32228
32229 /*
32230 * Licence: LGPL
32231 */
32232
32233 /**
32234  * @class Roo.bootstrap.DocumentSlider
32235  * @extends Roo.bootstrap.Component
32236  * Bootstrap DocumentSlider class
32237  * 
32238  * @constructor
32239  * Create a new DocumentViewer
32240  * @param {Object} config The config object
32241  */
32242
32243 Roo.bootstrap.DocumentSlider = function(config){
32244     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32245     
32246     this.files = [];
32247     
32248     this.addEvents({
32249         /**
32250          * @event initial
32251          * Fire after initEvent
32252          * @param {Roo.bootstrap.DocumentSlider} this
32253          */
32254         "initial" : true,
32255         /**
32256          * @event update
32257          * Fire after update
32258          * @param {Roo.bootstrap.DocumentSlider} this
32259          */
32260         "update" : true,
32261         /**
32262          * @event click
32263          * Fire after click
32264          * @param {Roo.bootstrap.DocumentSlider} this
32265          */
32266         "click" : true
32267     });
32268 };
32269
32270 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32271     
32272     files : false,
32273     
32274     indicator : 0,
32275     
32276     getAutoCreate : function()
32277     {
32278         var cfg = {
32279             tag : 'div',
32280             cls : 'roo-document-slider',
32281             cn : [
32282                 {
32283                     tag : 'div',
32284                     cls : 'roo-document-slider-header',
32285                     cn : [
32286                         {
32287                             tag : 'div',
32288                             cls : 'roo-document-slider-header-title'
32289                         }
32290                     ]
32291                 },
32292                 {
32293                     tag : 'div',
32294                     cls : 'roo-document-slider-body',
32295                     cn : [
32296                         {
32297                             tag : 'div',
32298                             cls : 'roo-document-slider-prev',
32299                             cn : [
32300                                 {
32301                                     tag : 'i',
32302                                     cls : 'fa fa-chevron-left'
32303                                 }
32304                             ]
32305                         },
32306                         {
32307                             tag : 'div',
32308                             cls : 'roo-document-slider-thumb',
32309                             cn : [
32310                                 {
32311                                     tag : 'img',
32312                                     cls : 'roo-document-slider-image'
32313                                 }
32314                             ]
32315                         },
32316                         {
32317                             tag : 'div',
32318                             cls : 'roo-document-slider-next',
32319                             cn : [
32320                                 {
32321                                     tag : 'i',
32322                                     cls : 'fa fa-chevron-right'
32323                                 }
32324                             ]
32325                         }
32326                     ]
32327                 }
32328             ]
32329         };
32330         
32331         return cfg;
32332     },
32333     
32334     initEvents : function()
32335     {
32336         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
32337         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
32338         
32339         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
32340         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
32341         
32342         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
32343         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32344         
32345         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
32346         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32347         
32348         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
32349         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32350         
32351         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
32352         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32353         
32354         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
32355         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32356         
32357         this.thumbEl.on('click', this.onClick, this);
32358         
32359         this.prevIndicator.on('click', this.prev, this);
32360         
32361         this.nextIndicator.on('click', this.next, this);
32362         
32363     },
32364     
32365     initial : function()
32366     {
32367         if(this.files.length){
32368             this.indicator = 1;
32369             this.update()
32370         }
32371         
32372         this.fireEvent('initial', this);
32373     },
32374     
32375     update : function()
32376     {
32377         this.imageEl.attr('src', this.files[this.indicator - 1]);
32378         
32379         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
32380         
32381         this.prevIndicator.show();
32382         
32383         if(this.indicator == 1){
32384             this.prevIndicator.hide();
32385         }
32386         
32387         this.nextIndicator.show();
32388         
32389         if(this.indicator == this.files.length){
32390             this.nextIndicator.hide();
32391         }
32392         
32393         this.thumbEl.scrollTo('top');
32394         
32395         this.fireEvent('update', this);
32396     },
32397     
32398     onClick : function(e)
32399     {
32400         e.preventDefault();
32401         
32402         this.fireEvent('click', this);
32403     },
32404     
32405     prev : function(e)
32406     {
32407         e.preventDefault();
32408         
32409         this.indicator = Math.max(1, this.indicator - 1);
32410         
32411         this.update();
32412     },
32413     
32414     next : function(e)
32415     {
32416         e.preventDefault();
32417         
32418         this.indicator = Math.min(this.files.length, this.indicator + 1);
32419         
32420         this.update();
32421     }
32422 });
32423 /*
32424  * - LGPL
32425  *
32426  * RadioSet
32427  *
32428  *
32429  */
32430
32431 /**
32432  * @class Roo.bootstrap.RadioSet
32433  * @extends Roo.bootstrap.Input
32434  * Bootstrap RadioSet class
32435  * @cfg {String} indicatorpos (left|right) default left
32436  * @cfg {Boolean} inline (true|false) inline the element (default true)
32437  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
32438  * @constructor
32439  * Create a new RadioSet
32440  * @param {Object} config The config object
32441  */
32442
32443 Roo.bootstrap.RadioSet = function(config){
32444     
32445     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
32446     
32447     this.radioes = [];
32448     
32449     Roo.bootstrap.RadioSet.register(this);
32450     
32451     this.addEvents({
32452         /**
32453         * @event check
32454         * Fires when the element is checked or unchecked.
32455         * @param {Roo.bootstrap.RadioSet} this This radio
32456         * @param {Roo.bootstrap.Radio} item The checked item
32457         */
32458        check : true
32459     });
32460     
32461 };
32462
32463 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
32464
32465     radioes : false,
32466     
32467     inline : true,
32468     
32469     weight : '',
32470     
32471     indicatorpos : 'left',
32472     
32473     getAutoCreate : function()
32474     {
32475         var label = {
32476             tag : 'label',
32477             cls : 'roo-radio-set-label',
32478             cn : [
32479                 {
32480                     tag : 'span',
32481                     html : this.fieldLabel
32482                 }
32483             ]
32484         };
32485         
32486         if(this.indicatorpos == 'left'){
32487             label.cn.unshift({
32488                 tag : 'i',
32489                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
32490                 tooltip : 'This field is required'
32491             });
32492         } else {
32493             label.cn.push({
32494                 tag : 'i',
32495                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
32496                 tooltip : 'This field is required'
32497             });
32498         }
32499         
32500         var items = {
32501             tag : 'div',
32502             cls : 'roo-radio-set-items'
32503         };
32504         
32505         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
32506         
32507         if (align === 'left' && this.fieldLabel.length) {
32508             
32509             items = {
32510                 cls : "roo-radio-set-right", 
32511                 cn: [
32512                     items
32513                 ]
32514             };
32515             
32516             if(this.labelWidth > 12){
32517                 label.style = "width: " + this.labelWidth + 'px';
32518             }
32519             
32520             if(this.labelWidth < 13 && this.labelmd == 0){
32521                 this.labelmd = this.labelWidth;
32522             }
32523             
32524             if(this.labellg > 0){
32525                 label.cls += ' col-lg-' + this.labellg;
32526                 items.cls += ' col-lg-' + (12 - this.labellg);
32527             }
32528             
32529             if(this.labelmd > 0){
32530                 label.cls += ' col-md-' + this.labelmd;
32531                 items.cls += ' col-md-' + (12 - this.labelmd);
32532             }
32533             
32534             if(this.labelsm > 0){
32535                 label.cls += ' col-sm-' + this.labelsm;
32536                 items.cls += ' col-sm-' + (12 - this.labelsm);
32537             }
32538             
32539             if(this.labelxs > 0){
32540                 label.cls += ' col-xs-' + this.labelxs;
32541                 items.cls += ' col-xs-' + (12 - this.labelxs);
32542             }
32543         }
32544         
32545         var cfg = {
32546             tag : 'div',
32547             cls : 'roo-radio-set',
32548             cn : [
32549                 {
32550                     tag : 'input',
32551                     cls : 'roo-radio-set-input',
32552                     type : 'hidden',
32553                     name : this.name,
32554                     value : this.value ? this.value :  ''
32555                 },
32556                 label,
32557                 items
32558             ]
32559         };
32560         
32561         if(this.weight.length){
32562             cfg.cls += ' roo-radio-' + this.weight;
32563         }
32564         
32565         if(this.inline) {
32566             cfg.cls += ' roo-radio-set-inline';
32567         }
32568         
32569         return cfg;
32570         
32571     },
32572
32573     initEvents : function()
32574     {
32575         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
32576         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
32577         
32578         if(!this.fieldLabel.length){
32579             this.labelEl.hide();
32580         }
32581         
32582         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
32583         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
32584         
32585         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
32586         this.indicatorEl().hide();
32587         
32588         this.originalValue = this.getValue();
32589         
32590     },
32591     
32592     inputEl: function ()
32593     {
32594         return this.el.select('.roo-radio-set-input', true).first();
32595     },
32596     
32597     getChildContainer : function()
32598     {
32599         return this.itemsEl;
32600     },
32601     
32602     register : function(item)
32603     {
32604         this.radioes.push(item);
32605         
32606     },
32607     
32608     validate : function()
32609     {   
32610         var valid = false;
32611         
32612         Roo.each(this.radioes, function(i){
32613             if(!i.checked){
32614                 return;
32615             }
32616             
32617             valid = true;
32618             return false;
32619         });
32620         
32621         if(this.allowBlank) {
32622             return true;
32623         }
32624         
32625         if(this.disabled || valid){
32626             this.markValid();
32627             return true;
32628         }
32629         
32630         this.markInvalid();
32631         return false;
32632         
32633     },
32634     
32635     markValid : function()
32636     {
32637         if(this.labelEl.isVisible(true)){
32638             this.indicatorEl().hide();
32639         }
32640         
32641         this.el.removeClass([this.invalidClass, this.validClass]);
32642         this.el.addClass(this.validClass);
32643         
32644         this.fireEvent('valid', this);
32645     },
32646     
32647     markInvalid : function(msg)
32648     {
32649         if(this.allowBlank || this.disabled){
32650             return;
32651         }
32652         
32653         if(this.labelEl.isVisible(true)){
32654             this.indicatorEl().show();
32655         }
32656         
32657         this.el.removeClass([this.invalidClass, this.validClass]);
32658         this.el.addClass(this.invalidClass);
32659         
32660         this.fireEvent('invalid', this, msg);
32661         
32662     },
32663     
32664     setValue : function(v, suppressEvent)
32665     {   
32666         Roo.each(this.radioes, function(i){
32667             
32668             i.checked = false;
32669             i.el.removeClass('checked');
32670             
32671             if(i.value === v || i.value.toString() === v.toString()){
32672                 i.checked = true;
32673                 i.el.addClass('checked');
32674                 
32675                 if(suppressEvent !== true){
32676                     this.fireEvent('check', this, i);
32677                 }
32678             }
32679             
32680         }, this);
32681         
32682         Roo.bootstrap.RadioSet.superclass.setValue.call(this, v);
32683         
32684     },
32685     
32686     clearInvalid : function(){
32687         
32688         if(!this.el || this.preventMark){
32689             return;
32690         }
32691         
32692         if(this.labelEl.isVisible(true)){
32693             this.indicatorEl().hide();
32694         }
32695         
32696         this.el.removeClass([this.invalidClass]);
32697         
32698         this.fireEvent('valid', this);
32699     }
32700     
32701 });
32702
32703 Roo.apply(Roo.bootstrap.RadioSet, {
32704     
32705     groups: {},
32706     
32707     register : function(set)
32708     {
32709         this.groups[set.name] = set;
32710     },
32711     
32712     get: function(name) 
32713     {
32714         if (typeof(this.groups[name]) == 'undefined') {
32715             return false;
32716         }
32717         
32718         return this.groups[name] ;
32719     }
32720     
32721 });
32722 /*
32723  * Based on:
32724  * Ext JS Library 1.1.1
32725  * Copyright(c) 2006-2007, Ext JS, LLC.
32726  *
32727  * Originally Released Under LGPL - original licence link has changed is not relivant.
32728  *
32729  * Fork - LGPL
32730  * <script type="text/javascript">
32731  */
32732
32733
32734 /**
32735  * @class Roo.bootstrap.SplitBar
32736  * @extends Roo.util.Observable
32737  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32738  * <br><br>
32739  * Usage:
32740  * <pre><code>
32741 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32742                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32743 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32744 split.minSize = 100;
32745 split.maxSize = 600;
32746 split.animate = true;
32747 split.on('moved', splitterMoved);
32748 </code></pre>
32749  * @constructor
32750  * Create a new SplitBar
32751  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
32752  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32753  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32754  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
32755                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32756                         position of the SplitBar).
32757  */
32758 Roo.bootstrap.SplitBar = function(cfg){
32759     
32760     /** @private */
32761     
32762     //{
32763     //  dragElement : elm
32764     //  resizingElement: el,
32765         // optional..
32766     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32767     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
32768         // existingProxy ???
32769     //}
32770     
32771     this.el = Roo.get(cfg.dragElement, true);
32772     this.el.dom.unselectable = "on";
32773     /** @private */
32774     this.resizingEl = Roo.get(cfg.resizingElement, true);
32775
32776     /**
32777      * @private
32778      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32779      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32780      * @type Number
32781      */
32782     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32783     
32784     /**
32785      * The minimum size of the resizing element. (Defaults to 0)
32786      * @type Number
32787      */
32788     this.minSize = 0;
32789     
32790     /**
32791      * The maximum size of the resizing element. (Defaults to 2000)
32792      * @type Number
32793      */
32794     this.maxSize = 2000;
32795     
32796     /**
32797      * Whether to animate the transition to the new size
32798      * @type Boolean
32799      */
32800     this.animate = false;
32801     
32802     /**
32803      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32804      * @type Boolean
32805      */
32806     this.useShim = false;
32807     
32808     /** @private */
32809     this.shim = null;
32810     
32811     if(!cfg.existingProxy){
32812         /** @private */
32813         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32814     }else{
32815         this.proxy = Roo.get(cfg.existingProxy).dom;
32816     }
32817     /** @private */
32818     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32819     
32820     /** @private */
32821     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32822     
32823     /** @private */
32824     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32825     
32826     /** @private */
32827     this.dragSpecs = {};
32828     
32829     /**
32830      * @private The adapter to use to positon and resize elements
32831      */
32832     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32833     this.adapter.init(this);
32834     
32835     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32836         /** @private */
32837         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32838         this.el.addClass("roo-splitbar-h");
32839     }else{
32840         /** @private */
32841         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32842         this.el.addClass("roo-splitbar-v");
32843     }
32844     
32845     this.addEvents({
32846         /**
32847          * @event resize
32848          * Fires when the splitter is moved (alias for {@link #event-moved})
32849          * @param {Roo.bootstrap.SplitBar} this
32850          * @param {Number} newSize the new width or height
32851          */
32852         "resize" : true,
32853         /**
32854          * @event moved
32855          * Fires when the splitter is moved
32856          * @param {Roo.bootstrap.SplitBar} this
32857          * @param {Number} newSize the new width or height
32858          */
32859         "moved" : true,
32860         /**
32861          * @event beforeresize
32862          * Fires before the splitter is dragged
32863          * @param {Roo.bootstrap.SplitBar} this
32864          */
32865         "beforeresize" : true,
32866
32867         "beforeapply" : true
32868     });
32869
32870     Roo.util.Observable.call(this);
32871 };
32872
32873 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32874     onStartProxyDrag : function(x, y){
32875         this.fireEvent("beforeresize", this);
32876         if(!this.overlay){
32877             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32878             o.unselectable();
32879             o.enableDisplayMode("block");
32880             // all splitbars share the same overlay
32881             Roo.bootstrap.SplitBar.prototype.overlay = o;
32882         }
32883         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32884         this.overlay.show();
32885         Roo.get(this.proxy).setDisplayed("block");
32886         var size = this.adapter.getElementSize(this);
32887         this.activeMinSize = this.getMinimumSize();;
32888         this.activeMaxSize = this.getMaximumSize();;
32889         var c1 = size - this.activeMinSize;
32890         var c2 = Math.max(this.activeMaxSize - size, 0);
32891         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32892             this.dd.resetConstraints();
32893             this.dd.setXConstraint(
32894                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32895                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32896             );
32897             this.dd.setYConstraint(0, 0);
32898         }else{
32899             this.dd.resetConstraints();
32900             this.dd.setXConstraint(0, 0);
32901             this.dd.setYConstraint(
32902                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32903                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32904             );
32905          }
32906         this.dragSpecs.startSize = size;
32907         this.dragSpecs.startPoint = [x, y];
32908         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32909     },
32910     
32911     /** 
32912      * @private Called after the drag operation by the DDProxy
32913      */
32914     onEndProxyDrag : function(e){
32915         Roo.get(this.proxy).setDisplayed(false);
32916         var endPoint = Roo.lib.Event.getXY(e);
32917         if(this.overlay){
32918             this.overlay.hide();
32919         }
32920         var newSize;
32921         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32922             newSize = this.dragSpecs.startSize + 
32923                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32924                     endPoint[0] - this.dragSpecs.startPoint[0] :
32925                     this.dragSpecs.startPoint[0] - endPoint[0]
32926                 );
32927         }else{
32928             newSize = this.dragSpecs.startSize + 
32929                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32930                     endPoint[1] - this.dragSpecs.startPoint[1] :
32931                     this.dragSpecs.startPoint[1] - endPoint[1]
32932                 );
32933         }
32934         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32935         if(newSize != this.dragSpecs.startSize){
32936             if(this.fireEvent('beforeapply', this, newSize) !== false){
32937                 this.adapter.setElementSize(this, newSize);
32938                 this.fireEvent("moved", this, newSize);
32939                 this.fireEvent("resize", this, newSize);
32940             }
32941         }
32942     },
32943     
32944     /**
32945      * Get the adapter this SplitBar uses
32946      * @return The adapter object
32947      */
32948     getAdapter : function(){
32949         return this.adapter;
32950     },
32951     
32952     /**
32953      * Set the adapter this SplitBar uses
32954      * @param {Object} adapter A SplitBar adapter object
32955      */
32956     setAdapter : function(adapter){
32957         this.adapter = adapter;
32958         this.adapter.init(this);
32959     },
32960     
32961     /**
32962      * Gets the minimum size for the resizing element
32963      * @return {Number} The minimum size
32964      */
32965     getMinimumSize : function(){
32966         return this.minSize;
32967     },
32968     
32969     /**
32970      * Sets the minimum size for the resizing element
32971      * @param {Number} minSize The minimum size
32972      */
32973     setMinimumSize : function(minSize){
32974         this.minSize = minSize;
32975     },
32976     
32977     /**
32978      * Gets the maximum size for the resizing element
32979      * @return {Number} The maximum size
32980      */
32981     getMaximumSize : function(){
32982         return this.maxSize;
32983     },
32984     
32985     /**
32986      * Sets the maximum size for the resizing element
32987      * @param {Number} maxSize The maximum size
32988      */
32989     setMaximumSize : function(maxSize){
32990         this.maxSize = maxSize;
32991     },
32992     
32993     /**
32994      * Sets the initialize size for the resizing element
32995      * @param {Number} size The initial size
32996      */
32997     setCurrentSize : function(size){
32998         var oldAnimate = this.animate;
32999         this.animate = false;
33000         this.adapter.setElementSize(this, size);
33001         this.animate = oldAnimate;
33002     },
33003     
33004     /**
33005      * Destroy this splitbar. 
33006      * @param {Boolean} removeEl True to remove the element
33007      */
33008     destroy : function(removeEl){
33009         if(this.shim){
33010             this.shim.remove();
33011         }
33012         this.dd.unreg();
33013         this.proxy.parentNode.removeChild(this.proxy);
33014         if(removeEl){
33015             this.el.remove();
33016         }
33017     }
33018 });
33019
33020 /**
33021  * @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.
33022  */
33023 Roo.bootstrap.SplitBar.createProxy = function(dir){
33024     var proxy = new Roo.Element(document.createElement("div"));
33025     proxy.unselectable();
33026     var cls = 'roo-splitbar-proxy';
33027     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33028     document.body.appendChild(proxy.dom);
33029     return proxy.dom;
33030 };
33031
33032 /** 
33033  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33034  * Default Adapter. It assumes the splitter and resizing element are not positioned
33035  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33036  */
33037 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33038 };
33039
33040 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33041     // do nothing for now
33042     init : function(s){
33043     
33044     },
33045     /**
33046      * Called before drag operations to get the current size of the resizing element. 
33047      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33048      */
33049      getElementSize : function(s){
33050         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33051             return s.resizingEl.getWidth();
33052         }else{
33053             return s.resizingEl.getHeight();
33054         }
33055     },
33056     
33057     /**
33058      * Called after drag operations to set the size of the resizing element.
33059      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33060      * @param {Number} newSize The new size to set
33061      * @param {Function} onComplete A function to be invoked when resizing is complete
33062      */
33063     setElementSize : function(s, newSize, onComplete){
33064         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33065             if(!s.animate){
33066                 s.resizingEl.setWidth(newSize);
33067                 if(onComplete){
33068                     onComplete(s, newSize);
33069                 }
33070             }else{
33071                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33072             }
33073         }else{
33074             
33075             if(!s.animate){
33076                 s.resizingEl.setHeight(newSize);
33077                 if(onComplete){
33078                     onComplete(s, newSize);
33079                 }
33080             }else{
33081                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33082             }
33083         }
33084     }
33085 };
33086
33087 /** 
33088  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33089  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33090  * Adapter that  moves the splitter element to align with the resized sizing element. 
33091  * Used with an absolute positioned SplitBar.
33092  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33093  * document.body, make sure you assign an id to the body element.
33094  */
33095 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33096     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33097     this.container = Roo.get(container);
33098 };
33099
33100 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33101     init : function(s){
33102         this.basic.init(s);
33103     },
33104     
33105     getElementSize : function(s){
33106         return this.basic.getElementSize(s);
33107     },
33108     
33109     setElementSize : function(s, newSize, onComplete){
33110         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33111     },
33112     
33113     moveSplitter : function(s){
33114         var yes = Roo.bootstrap.SplitBar;
33115         switch(s.placement){
33116             case yes.LEFT:
33117                 s.el.setX(s.resizingEl.getRight());
33118                 break;
33119             case yes.RIGHT:
33120                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33121                 break;
33122             case yes.TOP:
33123                 s.el.setY(s.resizingEl.getBottom());
33124                 break;
33125             case yes.BOTTOM:
33126                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33127                 break;
33128         }
33129     }
33130 };
33131
33132 /**
33133  * Orientation constant - Create a vertical SplitBar
33134  * @static
33135  * @type Number
33136  */
33137 Roo.bootstrap.SplitBar.VERTICAL = 1;
33138
33139 /**
33140  * Orientation constant - Create a horizontal SplitBar
33141  * @static
33142  * @type Number
33143  */
33144 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33145
33146 /**
33147  * Placement constant - The resizing element is to the left of the splitter element
33148  * @static
33149  * @type Number
33150  */
33151 Roo.bootstrap.SplitBar.LEFT = 1;
33152
33153 /**
33154  * Placement constant - The resizing element is to the right of the splitter element
33155  * @static
33156  * @type Number
33157  */
33158 Roo.bootstrap.SplitBar.RIGHT = 2;
33159
33160 /**
33161  * Placement constant - The resizing element is positioned above the splitter element
33162  * @static
33163  * @type Number
33164  */
33165 Roo.bootstrap.SplitBar.TOP = 3;
33166
33167 /**
33168  * Placement constant - The resizing element is positioned under splitter element
33169  * @static
33170  * @type Number
33171  */
33172 Roo.bootstrap.SplitBar.BOTTOM = 4;
33173 Roo.namespace("Roo.bootstrap.layout");/*
33174  * Based on:
33175  * Ext JS Library 1.1.1
33176  * Copyright(c) 2006-2007, Ext JS, LLC.
33177  *
33178  * Originally Released Under LGPL - original licence link has changed is not relivant.
33179  *
33180  * Fork - LGPL
33181  * <script type="text/javascript">
33182  */
33183
33184 /**
33185  * @class Roo.bootstrap.layout.Manager
33186  * @extends Roo.bootstrap.Component
33187  * Base class for layout managers.
33188  */
33189 Roo.bootstrap.layout.Manager = function(config)
33190 {
33191     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33192
33193
33194
33195
33196
33197     /** false to disable window resize monitoring @type Boolean */
33198     this.monitorWindowResize = true;
33199     this.regions = {};
33200     this.addEvents({
33201         /**
33202          * @event layout
33203          * Fires when a layout is performed.
33204          * @param {Roo.LayoutManager} this
33205          */
33206         "layout" : true,
33207         /**
33208          * @event regionresized
33209          * Fires when the user resizes a region.
33210          * @param {Roo.LayoutRegion} region The resized region
33211          * @param {Number} newSize The new size (width for east/west, height for north/south)
33212          */
33213         "regionresized" : true,
33214         /**
33215          * @event regioncollapsed
33216          * Fires when a region is collapsed.
33217          * @param {Roo.LayoutRegion} region The collapsed region
33218          */
33219         "regioncollapsed" : true,
33220         /**
33221          * @event regionexpanded
33222          * Fires when a region is expanded.
33223          * @param {Roo.LayoutRegion} region The expanded region
33224          */
33225         "regionexpanded" : true
33226     });
33227     this.updating = false;
33228
33229     if (config.el) {
33230         this.el = Roo.get(config.el);
33231         this.initEvents();
33232     }
33233
33234 };
33235
33236 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33237
33238
33239     regions : null,
33240
33241     monitorWindowResize : true,
33242
33243
33244     updating : false,
33245
33246
33247     onRender : function(ct, position)
33248     {
33249         if(!this.el){
33250             this.el = Roo.get(ct);
33251             this.initEvents();
33252         }
33253         //this.fireEvent('render',this);
33254     },
33255
33256
33257     initEvents: function()
33258     {
33259
33260
33261         // ie scrollbar fix
33262         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33263             document.body.scroll = "no";
33264         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33265             this.el.position('relative');
33266         }
33267         this.id = this.el.id;
33268         this.el.addClass("roo-layout-container");
33269         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33270         if(this.el.dom != document.body ) {
33271             this.el.on('resize', this.layout,this);
33272             this.el.on('show', this.layout,this);
33273         }
33274
33275     },
33276
33277     /**
33278      * Returns true if this layout is currently being updated
33279      * @return {Boolean}
33280      */
33281     isUpdating : function(){
33282         return this.updating;
33283     },
33284
33285     /**
33286      * Suspend the LayoutManager from doing auto-layouts while
33287      * making multiple add or remove calls
33288      */
33289     beginUpdate : function(){
33290         this.updating = true;
33291     },
33292
33293     /**
33294      * Restore auto-layouts and optionally disable the manager from performing a layout
33295      * @param {Boolean} noLayout true to disable a layout update
33296      */
33297     endUpdate : function(noLayout){
33298         this.updating = false;
33299         if(!noLayout){
33300             this.layout();
33301         }
33302     },
33303
33304     layout: function(){
33305         // abstract...
33306     },
33307
33308     onRegionResized : function(region, newSize){
33309         this.fireEvent("regionresized", region, newSize);
33310         this.layout();
33311     },
33312
33313     onRegionCollapsed : function(region){
33314         this.fireEvent("regioncollapsed", region);
33315     },
33316
33317     onRegionExpanded : function(region){
33318         this.fireEvent("regionexpanded", region);
33319     },
33320
33321     /**
33322      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33323      * performs box-model adjustments.
33324      * @return {Object} The size as an object {width: (the width), height: (the height)}
33325      */
33326     getViewSize : function()
33327     {
33328         var size;
33329         if(this.el.dom != document.body){
33330             size = this.el.getSize();
33331         }else{
33332             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33333         }
33334         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33335         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33336         return size;
33337     },
33338
33339     /**
33340      * Returns the Element this layout is bound to.
33341      * @return {Roo.Element}
33342      */
33343     getEl : function(){
33344         return this.el;
33345     },
33346
33347     /**
33348      * Returns the specified region.
33349      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33350      * @return {Roo.LayoutRegion}
33351      */
33352     getRegion : function(target){
33353         return this.regions[target.toLowerCase()];
33354     },
33355
33356     onWindowResize : function(){
33357         if(this.monitorWindowResize){
33358             this.layout();
33359         }
33360     }
33361 });
33362 /*
33363  * Based on:
33364  * Ext JS Library 1.1.1
33365  * Copyright(c) 2006-2007, Ext JS, LLC.
33366  *
33367  * Originally Released Under LGPL - original licence link has changed is not relivant.
33368  *
33369  * Fork - LGPL
33370  * <script type="text/javascript">
33371  */
33372 /**
33373  * @class Roo.bootstrap.layout.Border
33374  * @extends Roo.bootstrap.layout.Manager
33375  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33376  * please see: examples/bootstrap/nested.html<br><br>
33377  
33378 <b>The container the layout is rendered into can be either the body element or any other element.
33379 If it is not the body element, the container needs to either be an absolute positioned element,
33380 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33381 the container size if it is not the body element.</b>
33382
33383 * @constructor
33384 * Create a new Border
33385 * @param {Object} config Configuration options
33386  */
33387 Roo.bootstrap.layout.Border = function(config){
33388     config = config || {};
33389     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
33390     
33391     
33392     
33393     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33394         if(config[region]){
33395             config[region].region = region;
33396             this.addRegion(config[region]);
33397         }
33398     },this);
33399     
33400 };
33401
33402 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
33403
33404 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
33405     /**
33406      * Creates and adds a new region if it doesn't already exist.
33407      * @param {String} target The target region key (north, south, east, west or center).
33408      * @param {Object} config The regions config object
33409      * @return {BorderLayoutRegion} The new region
33410      */
33411     addRegion : function(config)
33412     {
33413         if(!this.regions[config.region]){
33414             var r = this.factory(config);
33415             this.bindRegion(r);
33416         }
33417         return this.regions[config.region];
33418     },
33419
33420     // private (kinda)
33421     bindRegion : function(r){
33422         this.regions[r.config.region] = r;
33423         
33424         r.on("visibilitychange",    this.layout, this);
33425         r.on("paneladded",          this.layout, this);
33426         r.on("panelremoved",        this.layout, this);
33427         r.on("invalidated",         this.layout, this);
33428         r.on("resized",             this.onRegionResized, this);
33429         r.on("collapsed",           this.onRegionCollapsed, this);
33430         r.on("expanded",            this.onRegionExpanded, this);
33431     },
33432
33433     /**
33434      * Performs a layout update.
33435      */
33436     layout : function()
33437     {
33438         if(this.updating) {
33439             return;
33440         }
33441         
33442         // render all the rebions if they have not been done alreayd?
33443         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33444             if(this.regions[region] && !this.regions[region].bodyEl){
33445                 this.regions[region].onRender(this.el)
33446             }
33447         },this);
33448         
33449         var size = this.getViewSize();
33450         var w = size.width;
33451         var h = size.height;
33452         var centerW = w;
33453         var centerH = h;
33454         var centerY = 0;
33455         var centerX = 0;
33456         //var x = 0, y = 0;
33457
33458         var rs = this.regions;
33459         var north = rs["north"];
33460         var south = rs["south"]; 
33461         var west = rs["west"];
33462         var east = rs["east"];
33463         var center = rs["center"];
33464         //if(this.hideOnLayout){ // not supported anymore
33465             //c.el.setStyle("display", "none");
33466         //}
33467         if(north && north.isVisible()){
33468             var b = north.getBox();
33469             var m = north.getMargins();
33470             b.width = w - (m.left+m.right);
33471             b.x = m.left;
33472             b.y = m.top;
33473             centerY = b.height + b.y + m.bottom;
33474             centerH -= centerY;
33475             north.updateBox(this.safeBox(b));
33476         }
33477         if(south && south.isVisible()){
33478             var b = south.getBox();
33479             var m = south.getMargins();
33480             b.width = w - (m.left+m.right);
33481             b.x = m.left;
33482             var totalHeight = (b.height + m.top + m.bottom);
33483             b.y = h - totalHeight + m.top;
33484             centerH -= totalHeight;
33485             south.updateBox(this.safeBox(b));
33486         }
33487         if(west && west.isVisible()){
33488             var b = west.getBox();
33489             var m = west.getMargins();
33490             b.height = centerH - (m.top+m.bottom);
33491             b.x = m.left;
33492             b.y = centerY + m.top;
33493             var totalWidth = (b.width + m.left + m.right);
33494             centerX += totalWidth;
33495             centerW -= totalWidth;
33496             west.updateBox(this.safeBox(b));
33497         }
33498         if(east && east.isVisible()){
33499             var b = east.getBox();
33500             var m = east.getMargins();
33501             b.height = centerH - (m.top+m.bottom);
33502             var totalWidth = (b.width + m.left + m.right);
33503             b.x = w - totalWidth + m.left;
33504             b.y = centerY + m.top;
33505             centerW -= totalWidth;
33506             east.updateBox(this.safeBox(b));
33507         }
33508         if(center){
33509             var m = center.getMargins();
33510             var centerBox = {
33511                 x: centerX + m.left,
33512                 y: centerY + m.top,
33513                 width: centerW - (m.left+m.right),
33514                 height: centerH - (m.top+m.bottom)
33515             };
33516             //if(this.hideOnLayout){
33517                 //center.el.setStyle("display", "block");
33518             //}
33519             center.updateBox(this.safeBox(centerBox));
33520         }
33521         this.el.repaint();
33522         this.fireEvent("layout", this);
33523     },
33524
33525     // private
33526     safeBox : function(box){
33527         box.width = Math.max(0, box.width);
33528         box.height = Math.max(0, box.height);
33529         return box;
33530     },
33531
33532     /**
33533      * Adds a ContentPanel (or subclass) to this layout.
33534      * @param {String} target The target region key (north, south, east, west or center).
33535      * @param {Roo.ContentPanel} panel The panel to add
33536      * @return {Roo.ContentPanel} The added panel
33537      */
33538     add : function(target, panel){
33539          
33540         target = target.toLowerCase();
33541         return this.regions[target].add(panel);
33542     },
33543
33544     /**
33545      * Remove a ContentPanel (or subclass) to this layout.
33546      * @param {String} target The target region key (north, south, east, west or center).
33547      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33548      * @return {Roo.ContentPanel} The removed panel
33549      */
33550     remove : function(target, panel){
33551         target = target.toLowerCase();
33552         return this.regions[target].remove(panel);
33553     },
33554
33555     /**
33556      * Searches all regions for a panel with the specified id
33557      * @param {String} panelId
33558      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33559      */
33560     findPanel : function(panelId){
33561         var rs = this.regions;
33562         for(var target in rs){
33563             if(typeof rs[target] != "function"){
33564                 var p = rs[target].getPanel(panelId);
33565                 if(p){
33566                     return p;
33567                 }
33568             }
33569         }
33570         return null;
33571     },
33572
33573     /**
33574      * Searches all regions for a panel with the specified id and activates (shows) it.
33575      * @param {String/ContentPanel} panelId The panels id or the panel itself
33576      * @return {Roo.ContentPanel} The shown panel or null
33577      */
33578     showPanel : function(panelId) {
33579       var rs = this.regions;
33580       for(var target in rs){
33581          var r = rs[target];
33582          if(typeof r != "function"){
33583             if(r.hasPanel(panelId)){
33584                return r.showPanel(panelId);
33585             }
33586          }
33587       }
33588       return null;
33589    },
33590
33591    /**
33592      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33593      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33594      */
33595    /*
33596     restoreState : function(provider){
33597         if(!provider){
33598             provider = Roo.state.Manager;
33599         }
33600         var sm = new Roo.LayoutStateManager();
33601         sm.init(this, provider);
33602     },
33603 */
33604  
33605  
33606     /**
33607      * Adds a xtype elements to the layout.
33608      * <pre><code>
33609
33610 layout.addxtype({
33611        xtype : 'ContentPanel',
33612        region: 'west',
33613        items: [ .... ]
33614    }
33615 );
33616
33617 layout.addxtype({
33618         xtype : 'NestedLayoutPanel',
33619         region: 'west',
33620         layout: {
33621            center: { },
33622            west: { }   
33623         },
33624         items : [ ... list of content panels or nested layout panels.. ]
33625    }
33626 );
33627 </code></pre>
33628      * @param {Object} cfg Xtype definition of item to add.
33629      */
33630     addxtype : function(cfg)
33631     {
33632         // basically accepts a pannel...
33633         // can accept a layout region..!?!?
33634         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33635         
33636         
33637         // theory?  children can only be panels??
33638         
33639         //if (!cfg.xtype.match(/Panel$/)) {
33640         //    return false;
33641         //}
33642         var ret = false;
33643         
33644         if (typeof(cfg.region) == 'undefined') {
33645             Roo.log("Failed to add Panel, region was not set");
33646             Roo.log(cfg);
33647             return false;
33648         }
33649         var region = cfg.region;
33650         delete cfg.region;
33651         
33652           
33653         var xitems = [];
33654         if (cfg.items) {
33655             xitems = cfg.items;
33656             delete cfg.items;
33657         }
33658         var nb = false;
33659         
33660         switch(cfg.xtype) 
33661         {
33662             case 'Content':  // ContentPanel (el, cfg)
33663             case 'Scroll':  // ContentPanel (el, cfg)
33664             case 'View': 
33665                 cfg.autoCreate = true;
33666                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33667                 //} else {
33668                 //    var el = this.el.createChild();
33669                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33670                 //}
33671                 
33672                 this.add(region, ret);
33673                 break;
33674             
33675             /*
33676             case 'TreePanel': // our new panel!
33677                 cfg.el = this.el.createChild();
33678                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33679                 this.add(region, ret);
33680                 break;
33681             */
33682             
33683             case 'Nest': 
33684                 // create a new Layout (which is  a Border Layout...
33685                 
33686                 var clayout = cfg.layout;
33687                 clayout.el  = this.el.createChild();
33688                 clayout.items   = clayout.items  || [];
33689                 
33690                 delete cfg.layout;
33691                 
33692                 // replace this exitems with the clayout ones..
33693                 xitems = clayout.items;
33694                  
33695                 // force background off if it's in center...
33696                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33697                     cfg.background = false;
33698                 }
33699                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
33700                 
33701                 
33702                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33703                 //console.log('adding nested layout panel '  + cfg.toSource());
33704                 this.add(region, ret);
33705                 nb = {}; /// find first...
33706                 break;
33707             
33708             case 'Grid':
33709                 
33710                 // needs grid and region
33711                 
33712                 //var el = this.getRegion(region).el.createChild();
33713                 /*
33714                  *var el = this.el.createChild();
33715                 // create the grid first...
33716                 cfg.grid.container = el;
33717                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33718                 */
33719                 
33720                 if (region == 'center' && this.active ) {
33721                     cfg.background = false;
33722                 }
33723                 
33724                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33725                 
33726                 this.add(region, ret);
33727                 /*
33728                 if (cfg.background) {
33729                     // render grid on panel activation (if panel background)
33730                     ret.on('activate', function(gp) {
33731                         if (!gp.grid.rendered) {
33732                     //        gp.grid.render(el);
33733                         }
33734                     });
33735                 } else {
33736                   //  cfg.grid.render(el);
33737                 }
33738                 */
33739                 break;
33740            
33741            
33742             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33743                 // it was the old xcomponent building that caused this before.
33744                 // espeically if border is the top element in the tree.
33745                 ret = this;
33746                 break; 
33747                 
33748                     
33749                 
33750                 
33751                 
33752             default:
33753                 /*
33754                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33755                     
33756                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33757                     this.add(region, ret);
33758                 } else {
33759                 */
33760                     Roo.log(cfg);
33761                     throw "Can not add '" + cfg.xtype + "' to Border";
33762                     return null;
33763              
33764                                 
33765              
33766         }
33767         this.beginUpdate();
33768         // add children..
33769         var region = '';
33770         var abn = {};
33771         Roo.each(xitems, function(i)  {
33772             region = nb && i.region ? i.region : false;
33773             
33774             var add = ret.addxtype(i);
33775            
33776             if (region) {
33777                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33778                 if (!i.background) {
33779                     abn[region] = nb[region] ;
33780                 }
33781             }
33782             
33783         });
33784         this.endUpdate();
33785
33786         // make the last non-background panel active..
33787         //if (nb) { Roo.log(abn); }
33788         if (nb) {
33789             
33790             for(var r in abn) {
33791                 region = this.getRegion(r);
33792                 if (region) {
33793                     // tried using nb[r], but it does not work..
33794                      
33795                     region.showPanel(abn[r]);
33796                    
33797                 }
33798             }
33799         }
33800         return ret;
33801         
33802     },
33803     
33804     
33805 // private
33806     factory : function(cfg)
33807     {
33808         
33809         var validRegions = Roo.bootstrap.layout.Border.regions;
33810
33811         var target = cfg.region;
33812         cfg.mgr = this;
33813         
33814         var r = Roo.bootstrap.layout;
33815         Roo.log(target);
33816         switch(target){
33817             case "north":
33818                 return new r.North(cfg);
33819             case "south":
33820                 return new r.South(cfg);
33821             case "east":
33822                 return new r.East(cfg);
33823             case "west":
33824                 return new r.West(cfg);
33825             case "center":
33826                 return new r.Center(cfg);
33827         }
33828         throw 'Layout region "'+target+'" not supported.';
33829     }
33830     
33831     
33832 });
33833  /*
33834  * Based on:
33835  * Ext JS Library 1.1.1
33836  * Copyright(c) 2006-2007, Ext JS, LLC.
33837  *
33838  * Originally Released Under LGPL - original licence link has changed is not relivant.
33839  *
33840  * Fork - LGPL
33841  * <script type="text/javascript">
33842  */
33843  
33844 /**
33845  * @class Roo.bootstrap.layout.Basic
33846  * @extends Roo.util.Observable
33847  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33848  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33849  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33850  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33851  * @cfg {string}   region  the region that it inhabits..
33852  * @cfg {bool}   skipConfig skip config?
33853  * 
33854
33855  */
33856 Roo.bootstrap.layout.Basic = function(config){
33857     
33858     this.mgr = config.mgr;
33859     
33860     this.position = config.region;
33861     
33862     var skipConfig = config.skipConfig;
33863     
33864     this.events = {
33865         /**
33866          * @scope Roo.BasicLayoutRegion
33867          */
33868         
33869         /**
33870          * @event beforeremove
33871          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33872          * @param {Roo.LayoutRegion} this
33873          * @param {Roo.ContentPanel} panel The panel
33874          * @param {Object} e The cancel event object
33875          */
33876         "beforeremove" : true,
33877         /**
33878          * @event invalidated
33879          * Fires when the layout for this region is changed.
33880          * @param {Roo.LayoutRegion} this
33881          */
33882         "invalidated" : true,
33883         /**
33884          * @event visibilitychange
33885          * Fires when this region is shown or hidden 
33886          * @param {Roo.LayoutRegion} this
33887          * @param {Boolean} visibility true or false
33888          */
33889         "visibilitychange" : true,
33890         /**
33891          * @event paneladded
33892          * Fires when a panel is added. 
33893          * @param {Roo.LayoutRegion} this
33894          * @param {Roo.ContentPanel} panel The panel
33895          */
33896         "paneladded" : true,
33897         /**
33898          * @event panelremoved
33899          * Fires when a panel is removed. 
33900          * @param {Roo.LayoutRegion} this
33901          * @param {Roo.ContentPanel} panel The panel
33902          */
33903         "panelremoved" : true,
33904         /**
33905          * @event beforecollapse
33906          * Fires when this region before collapse.
33907          * @param {Roo.LayoutRegion} this
33908          */
33909         "beforecollapse" : true,
33910         /**
33911          * @event collapsed
33912          * Fires when this region is collapsed.
33913          * @param {Roo.LayoutRegion} this
33914          */
33915         "collapsed" : true,
33916         /**
33917          * @event expanded
33918          * Fires when this region is expanded.
33919          * @param {Roo.LayoutRegion} this
33920          */
33921         "expanded" : true,
33922         /**
33923          * @event slideshow
33924          * Fires when this region is slid into view.
33925          * @param {Roo.LayoutRegion} this
33926          */
33927         "slideshow" : true,
33928         /**
33929          * @event slidehide
33930          * Fires when this region slides out of view. 
33931          * @param {Roo.LayoutRegion} this
33932          */
33933         "slidehide" : true,
33934         /**
33935          * @event panelactivated
33936          * Fires when a panel is activated. 
33937          * @param {Roo.LayoutRegion} this
33938          * @param {Roo.ContentPanel} panel The activated panel
33939          */
33940         "panelactivated" : true,
33941         /**
33942          * @event resized
33943          * Fires when the user resizes this region. 
33944          * @param {Roo.LayoutRegion} this
33945          * @param {Number} newSize The new size (width for east/west, height for north/south)
33946          */
33947         "resized" : true
33948     };
33949     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33950     this.panels = new Roo.util.MixedCollection();
33951     this.panels.getKey = this.getPanelId.createDelegate(this);
33952     this.box = null;
33953     this.activePanel = null;
33954     // ensure listeners are added...
33955     
33956     if (config.listeners || config.events) {
33957         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33958             listeners : config.listeners || {},
33959             events : config.events || {}
33960         });
33961     }
33962     
33963     if(skipConfig !== true){
33964         this.applyConfig(config);
33965     }
33966 };
33967
33968 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33969 {
33970     getPanelId : function(p){
33971         return p.getId();
33972     },
33973     
33974     applyConfig : function(config){
33975         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33976         this.config = config;
33977         
33978     },
33979     
33980     /**
33981      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33982      * the width, for horizontal (north, south) the height.
33983      * @param {Number} newSize The new width or height
33984      */
33985     resizeTo : function(newSize){
33986         var el = this.el ? this.el :
33987                  (this.activePanel ? this.activePanel.getEl() : null);
33988         if(el){
33989             switch(this.position){
33990                 case "east":
33991                 case "west":
33992                     el.setWidth(newSize);
33993                     this.fireEvent("resized", this, newSize);
33994                 break;
33995                 case "north":
33996                 case "south":
33997                     el.setHeight(newSize);
33998                     this.fireEvent("resized", this, newSize);
33999                 break;                
34000             }
34001         }
34002     },
34003     
34004     getBox : function(){
34005         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34006     },
34007     
34008     getMargins : function(){
34009         return this.margins;
34010     },
34011     
34012     updateBox : function(box){
34013         this.box = box;
34014         var el = this.activePanel.getEl();
34015         el.dom.style.left = box.x + "px";
34016         el.dom.style.top = box.y + "px";
34017         this.activePanel.setSize(box.width, box.height);
34018     },
34019     
34020     /**
34021      * Returns the container element for this region.
34022      * @return {Roo.Element}
34023      */
34024     getEl : function(){
34025         return this.activePanel;
34026     },
34027     
34028     /**
34029      * Returns true if this region is currently visible.
34030      * @return {Boolean}
34031      */
34032     isVisible : function(){
34033         return this.activePanel ? true : false;
34034     },
34035     
34036     setActivePanel : function(panel){
34037         panel = this.getPanel(panel);
34038         if(this.activePanel && this.activePanel != panel){
34039             this.activePanel.setActiveState(false);
34040             this.activePanel.getEl().setLeftTop(-10000,-10000);
34041         }
34042         this.activePanel = panel;
34043         panel.setActiveState(true);
34044         if(this.box){
34045             panel.setSize(this.box.width, this.box.height);
34046         }
34047         this.fireEvent("panelactivated", this, panel);
34048         this.fireEvent("invalidated");
34049     },
34050     
34051     /**
34052      * Show the specified panel.
34053      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34054      * @return {Roo.ContentPanel} The shown panel or null
34055      */
34056     showPanel : function(panel){
34057         panel = this.getPanel(panel);
34058         if(panel){
34059             this.setActivePanel(panel);
34060         }
34061         return panel;
34062     },
34063     
34064     /**
34065      * Get the active panel for this region.
34066      * @return {Roo.ContentPanel} The active panel or null
34067      */
34068     getActivePanel : function(){
34069         return this.activePanel;
34070     },
34071     
34072     /**
34073      * Add the passed ContentPanel(s)
34074      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34075      * @return {Roo.ContentPanel} The panel added (if only one was added)
34076      */
34077     add : function(panel){
34078         if(arguments.length > 1){
34079             for(var i = 0, len = arguments.length; i < len; i++) {
34080                 this.add(arguments[i]);
34081             }
34082             return null;
34083         }
34084         if(this.hasPanel(panel)){
34085             this.showPanel(panel);
34086             return panel;
34087         }
34088         var el = panel.getEl();
34089         if(el.dom.parentNode != this.mgr.el.dom){
34090             this.mgr.el.dom.appendChild(el.dom);
34091         }
34092         if(panel.setRegion){
34093             panel.setRegion(this);
34094         }
34095         this.panels.add(panel);
34096         el.setStyle("position", "absolute");
34097         if(!panel.background){
34098             this.setActivePanel(panel);
34099             if(this.config.initialSize && this.panels.getCount()==1){
34100                 this.resizeTo(this.config.initialSize);
34101             }
34102         }
34103         this.fireEvent("paneladded", this, panel);
34104         return panel;
34105     },
34106     
34107     /**
34108      * Returns true if the panel is in this region.
34109      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34110      * @return {Boolean}
34111      */
34112     hasPanel : function(panel){
34113         if(typeof panel == "object"){ // must be panel obj
34114             panel = panel.getId();
34115         }
34116         return this.getPanel(panel) ? true : false;
34117     },
34118     
34119     /**
34120      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34121      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34122      * @param {Boolean} preservePanel Overrides the config preservePanel option
34123      * @return {Roo.ContentPanel} The panel that was removed
34124      */
34125     remove : function(panel, preservePanel){
34126         panel = this.getPanel(panel);
34127         if(!panel){
34128             return null;
34129         }
34130         var e = {};
34131         this.fireEvent("beforeremove", this, panel, e);
34132         if(e.cancel === true){
34133             return null;
34134         }
34135         var panelId = panel.getId();
34136         this.panels.removeKey(panelId);
34137         return panel;
34138     },
34139     
34140     /**
34141      * Returns the panel specified or null if it's not in this region.
34142      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34143      * @return {Roo.ContentPanel}
34144      */
34145     getPanel : function(id){
34146         if(typeof id == "object"){ // must be panel obj
34147             return id;
34148         }
34149         return this.panels.get(id);
34150     },
34151     
34152     /**
34153      * Returns this regions position (north/south/east/west/center).
34154      * @return {String} 
34155      */
34156     getPosition: function(){
34157         return this.position;    
34158     }
34159 });/*
34160  * Based on:
34161  * Ext JS Library 1.1.1
34162  * Copyright(c) 2006-2007, Ext JS, LLC.
34163  *
34164  * Originally Released Under LGPL - original licence link has changed is not relivant.
34165  *
34166  * Fork - LGPL
34167  * <script type="text/javascript">
34168  */
34169  
34170 /**
34171  * @class Roo.bootstrap.layout.Region
34172  * @extends Roo.bootstrap.layout.Basic
34173  * This class represents a region in a layout manager.
34174  
34175  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34176  * @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})
34177  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34178  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34179  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34180  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34181  * @cfg {String}    title           The title for the region (overrides panel titles)
34182  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34183  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34184  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34185  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34186  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34187  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34188  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34189  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34190  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34191  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34192
34193  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34194  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34195  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34196  * @cfg {Number}    width           For East/West panels
34197  * @cfg {Number}    height          For North/South panels
34198  * @cfg {Boolean}   split           To show the splitter
34199  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34200  * 
34201  * @cfg {string}   cls             Extra CSS classes to add to region
34202  * 
34203  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34204  * @cfg {string}   region  the region that it inhabits..
34205  *
34206
34207  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34208  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34209
34210  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34211  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34212  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34213  */
34214 Roo.bootstrap.layout.Region = function(config)
34215 {
34216     this.applyConfig(config);
34217
34218     var mgr = config.mgr;
34219     var pos = config.region;
34220     config.skipConfig = true;
34221     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34222     
34223     if (mgr.el) {
34224         this.onRender(mgr.el);   
34225     }
34226      
34227     this.visible = true;
34228     this.collapsed = false;
34229     this.unrendered_panels = [];
34230 };
34231
34232 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34233
34234     position: '', // set by wrapper (eg. north/south etc..)
34235     unrendered_panels : null,  // unrendered panels.
34236     createBody : function(){
34237         /** This region's body element 
34238         * @type Roo.Element */
34239         this.bodyEl = this.el.createChild({
34240                 tag: "div",
34241                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34242         });
34243     },
34244
34245     onRender: function(ctr, pos)
34246     {
34247         var dh = Roo.DomHelper;
34248         /** This region's container element 
34249         * @type Roo.Element */
34250         this.el = dh.append(ctr.dom, {
34251                 tag: "div",
34252                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34253             }, true);
34254         /** This region's title element 
34255         * @type Roo.Element */
34256     
34257         this.titleEl = dh.append(this.el.dom,
34258             {
34259                     tag: "div",
34260                     unselectable: "on",
34261                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34262                     children:[
34263                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34264                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34265                     ]}, true);
34266         
34267         this.titleEl.enableDisplayMode();
34268         /** This region's title text element 
34269         * @type HTMLElement */
34270         this.titleTextEl = this.titleEl.dom.firstChild;
34271         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34272         /*
34273         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34274         this.closeBtn.enableDisplayMode();
34275         this.closeBtn.on("click", this.closeClicked, this);
34276         this.closeBtn.hide();
34277     */
34278         this.createBody(this.config);
34279         if(this.config.hideWhenEmpty){
34280             this.hide();
34281             this.on("paneladded", this.validateVisibility, this);
34282             this.on("panelremoved", this.validateVisibility, this);
34283         }
34284         if(this.autoScroll){
34285             this.bodyEl.setStyle("overflow", "auto");
34286         }else{
34287             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34288         }
34289         //if(c.titlebar !== false){
34290             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34291                 this.titleEl.hide();
34292             }else{
34293                 this.titleEl.show();
34294                 if(this.config.title){
34295                     this.titleTextEl.innerHTML = this.config.title;
34296                 }
34297             }
34298         //}
34299         if(this.config.collapsed){
34300             this.collapse(true);
34301         }
34302         if(this.config.hidden){
34303             this.hide();
34304         }
34305         
34306         if (this.unrendered_panels && this.unrendered_panels.length) {
34307             for (var i =0;i< this.unrendered_panels.length; i++) {
34308                 this.add(this.unrendered_panels[i]);
34309             }
34310             this.unrendered_panels = null;
34311             
34312         }
34313         
34314     },
34315     
34316     applyConfig : function(c)
34317     {
34318         /*
34319          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34320             var dh = Roo.DomHelper;
34321             if(c.titlebar !== false){
34322                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34323                 this.collapseBtn.on("click", this.collapse, this);
34324                 this.collapseBtn.enableDisplayMode();
34325                 /*
34326                 if(c.showPin === true || this.showPin){
34327                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34328                     this.stickBtn.enableDisplayMode();
34329                     this.stickBtn.on("click", this.expand, this);
34330                     this.stickBtn.hide();
34331                 }
34332                 
34333             }
34334             */
34335             /** This region's collapsed element
34336             * @type Roo.Element */
34337             /*
34338              *
34339             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34340                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34341             ]}, true);
34342             
34343             if(c.floatable !== false){
34344                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34345                this.collapsedEl.on("click", this.collapseClick, this);
34346             }
34347
34348             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34349                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34350                    id: "message", unselectable: "on", style:{"float":"left"}});
34351                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34352              }
34353             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34354             this.expandBtn.on("click", this.expand, this);
34355             
34356         }
34357         
34358         if(this.collapseBtn){
34359             this.collapseBtn.setVisible(c.collapsible == true);
34360         }
34361         
34362         this.cmargins = c.cmargins || this.cmargins ||
34363                          (this.position == "west" || this.position == "east" ?
34364                              {top: 0, left: 2, right:2, bottom: 0} :
34365                              {top: 2, left: 0, right:0, bottom: 2});
34366         */
34367         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34368         
34369         
34370         this.bottomTabs = c.tabPosition != "top";
34371         
34372         this.autoScroll = c.autoScroll || false;
34373         
34374         
34375        
34376         
34377         this.duration = c.duration || .30;
34378         this.slideDuration = c.slideDuration || .45;
34379         this.config = c;
34380        
34381     },
34382     /**
34383      * Returns true if this region is currently visible.
34384      * @return {Boolean}
34385      */
34386     isVisible : function(){
34387         return this.visible;
34388     },
34389
34390     /**
34391      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34392      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34393      */
34394     //setCollapsedTitle : function(title){
34395     //    title = title || "&#160;";
34396      //   if(this.collapsedTitleTextEl){
34397       //      this.collapsedTitleTextEl.innerHTML = title;
34398        // }
34399     //},
34400
34401     getBox : function(){
34402         var b;
34403       //  if(!this.collapsed){
34404             b = this.el.getBox(false, true);
34405        // }else{
34406           //  b = this.collapsedEl.getBox(false, true);
34407         //}
34408         return b;
34409     },
34410
34411     getMargins : function(){
34412         return this.margins;
34413         //return this.collapsed ? this.cmargins : this.margins;
34414     },
34415 /*
34416     highlight : function(){
34417         this.el.addClass("x-layout-panel-dragover");
34418     },
34419
34420     unhighlight : function(){
34421         this.el.removeClass("x-layout-panel-dragover");
34422     },
34423 */
34424     updateBox : function(box)
34425     {
34426         if (!this.bodyEl) {
34427             return; // not rendered yet..
34428         }
34429         
34430         this.box = box;
34431         if(!this.collapsed){
34432             this.el.dom.style.left = box.x + "px";
34433             this.el.dom.style.top = box.y + "px";
34434             this.updateBody(box.width, box.height);
34435         }else{
34436             this.collapsedEl.dom.style.left = box.x + "px";
34437             this.collapsedEl.dom.style.top = box.y + "px";
34438             this.collapsedEl.setSize(box.width, box.height);
34439         }
34440         if(this.tabs){
34441             this.tabs.autoSizeTabs();
34442         }
34443     },
34444
34445     updateBody : function(w, h)
34446     {
34447         if(w !== null){
34448             this.el.setWidth(w);
34449             w -= this.el.getBorderWidth("rl");
34450             if(this.config.adjustments){
34451                 w += this.config.adjustments[0];
34452             }
34453         }
34454         if(h !== null && h > 0){
34455             this.el.setHeight(h);
34456             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34457             h -= this.el.getBorderWidth("tb");
34458             if(this.config.adjustments){
34459                 h += this.config.adjustments[1];
34460             }
34461             this.bodyEl.setHeight(h);
34462             if(this.tabs){
34463                 h = this.tabs.syncHeight(h);
34464             }
34465         }
34466         if(this.panelSize){
34467             w = w !== null ? w : this.panelSize.width;
34468             h = h !== null ? h : this.panelSize.height;
34469         }
34470         if(this.activePanel){
34471             var el = this.activePanel.getEl();
34472             w = w !== null ? w : el.getWidth();
34473             h = h !== null ? h : el.getHeight();
34474             this.panelSize = {width: w, height: h};
34475             this.activePanel.setSize(w, h);
34476         }
34477         if(Roo.isIE && this.tabs){
34478             this.tabs.el.repaint();
34479         }
34480     },
34481
34482     /**
34483      * Returns the container element for this region.
34484      * @return {Roo.Element}
34485      */
34486     getEl : function(){
34487         return this.el;
34488     },
34489
34490     /**
34491      * Hides this region.
34492      */
34493     hide : function(){
34494         //if(!this.collapsed){
34495             this.el.dom.style.left = "-2000px";
34496             this.el.hide();
34497         //}else{
34498          //   this.collapsedEl.dom.style.left = "-2000px";
34499          //   this.collapsedEl.hide();
34500        // }
34501         this.visible = false;
34502         this.fireEvent("visibilitychange", this, false);
34503     },
34504
34505     /**
34506      * Shows this region if it was previously hidden.
34507      */
34508     show : function(){
34509         //if(!this.collapsed){
34510             this.el.show();
34511         //}else{
34512         //    this.collapsedEl.show();
34513        // }
34514         this.visible = true;
34515         this.fireEvent("visibilitychange", this, true);
34516     },
34517 /*
34518     closeClicked : function(){
34519         if(this.activePanel){
34520             this.remove(this.activePanel);
34521         }
34522     },
34523
34524     collapseClick : function(e){
34525         if(this.isSlid){
34526            e.stopPropagation();
34527            this.slideIn();
34528         }else{
34529            e.stopPropagation();
34530            this.slideOut();
34531         }
34532     },
34533 */
34534     /**
34535      * Collapses this region.
34536      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34537      */
34538     /*
34539     collapse : function(skipAnim, skipCheck = false){
34540         if(this.collapsed) {
34541             return;
34542         }
34543         
34544         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34545             
34546             this.collapsed = true;
34547             if(this.split){
34548                 this.split.el.hide();
34549             }
34550             if(this.config.animate && skipAnim !== true){
34551                 this.fireEvent("invalidated", this);
34552                 this.animateCollapse();
34553             }else{
34554                 this.el.setLocation(-20000,-20000);
34555                 this.el.hide();
34556                 this.collapsedEl.show();
34557                 this.fireEvent("collapsed", this);
34558                 this.fireEvent("invalidated", this);
34559             }
34560         }
34561         
34562     },
34563 */
34564     animateCollapse : function(){
34565         // overridden
34566     },
34567
34568     /**
34569      * Expands this region if it was previously collapsed.
34570      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34571      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34572      */
34573     /*
34574     expand : function(e, skipAnim){
34575         if(e) {
34576             e.stopPropagation();
34577         }
34578         if(!this.collapsed || this.el.hasActiveFx()) {
34579             return;
34580         }
34581         if(this.isSlid){
34582             this.afterSlideIn();
34583             skipAnim = true;
34584         }
34585         this.collapsed = false;
34586         if(this.config.animate && skipAnim !== true){
34587             this.animateExpand();
34588         }else{
34589             this.el.show();
34590             if(this.split){
34591                 this.split.el.show();
34592             }
34593             this.collapsedEl.setLocation(-2000,-2000);
34594             this.collapsedEl.hide();
34595             this.fireEvent("invalidated", this);
34596             this.fireEvent("expanded", this);
34597         }
34598     },
34599 */
34600     animateExpand : function(){
34601         // overridden
34602     },
34603
34604     initTabs : function()
34605     {
34606         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
34607         
34608         var ts = new Roo.bootstrap.panel.Tabs({
34609                 el: this.bodyEl.dom,
34610                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
34611                 disableTooltips: this.config.disableTabTips,
34612                 toolbar : this.config.toolbar
34613             });
34614         
34615         if(this.config.hideTabs){
34616             ts.stripWrap.setDisplayed(false);
34617         }
34618         this.tabs = ts;
34619         ts.resizeTabs = this.config.resizeTabs === true;
34620         ts.minTabWidth = this.config.minTabWidth || 40;
34621         ts.maxTabWidth = this.config.maxTabWidth || 250;
34622         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34623         ts.monitorResize = false;
34624         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
34625         ts.bodyEl.addClass('roo-layout-tabs-body');
34626         this.panels.each(this.initPanelAsTab, this);
34627     },
34628
34629     initPanelAsTab : function(panel){
34630         var ti = this.tabs.addTab(
34631             panel.getEl().id,
34632             panel.getTitle(),
34633             null,
34634             this.config.closeOnTab && panel.isClosable(),
34635             panel.tpl
34636         );
34637         if(panel.tabTip !== undefined){
34638             ti.setTooltip(panel.tabTip);
34639         }
34640         ti.on("activate", function(){
34641               this.setActivePanel(panel);
34642         }, this);
34643         
34644         if(this.config.closeOnTab){
34645             ti.on("beforeclose", function(t, e){
34646                 e.cancel = true;
34647                 this.remove(panel);
34648             }, this);
34649         }
34650         
34651         panel.tabItem = ti;
34652         
34653         return ti;
34654     },
34655
34656     updatePanelTitle : function(panel, title)
34657     {
34658         if(this.activePanel == panel){
34659             this.updateTitle(title);
34660         }
34661         if(this.tabs){
34662             var ti = this.tabs.getTab(panel.getEl().id);
34663             ti.setText(title);
34664             if(panel.tabTip !== undefined){
34665                 ti.setTooltip(panel.tabTip);
34666             }
34667         }
34668     },
34669
34670     updateTitle : function(title){
34671         if(this.titleTextEl && !this.config.title){
34672             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34673         }
34674     },
34675
34676     setActivePanel : function(panel)
34677     {
34678         panel = this.getPanel(panel);
34679         if(this.activePanel && this.activePanel != panel){
34680             this.activePanel.setActiveState(false);
34681         }
34682         this.activePanel = panel;
34683         panel.setActiveState(true);
34684         if(this.panelSize){
34685             panel.setSize(this.panelSize.width, this.panelSize.height);
34686         }
34687         if(this.closeBtn){
34688             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34689         }
34690         this.updateTitle(panel.getTitle());
34691         if(this.tabs){
34692             this.fireEvent("invalidated", this);
34693         }
34694         this.fireEvent("panelactivated", this, panel);
34695     },
34696
34697     /**
34698      * Shows the specified panel.
34699      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34700      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34701      */
34702     showPanel : function(panel)
34703     {
34704         panel = this.getPanel(panel);
34705         if(panel){
34706             if(this.tabs){
34707                 var tab = this.tabs.getTab(panel.getEl().id);
34708                 if(tab.isHidden()){
34709                     this.tabs.unhideTab(tab.id);
34710                 }
34711                 tab.activate();
34712             }else{
34713                 this.setActivePanel(panel);
34714             }
34715         }
34716         return panel;
34717     },
34718
34719     /**
34720      * Get the active panel for this region.
34721      * @return {Roo.ContentPanel} The active panel or null
34722      */
34723     getActivePanel : function(){
34724         return this.activePanel;
34725     },
34726
34727     validateVisibility : function(){
34728         if(this.panels.getCount() < 1){
34729             this.updateTitle("&#160;");
34730             this.closeBtn.hide();
34731             this.hide();
34732         }else{
34733             if(!this.isVisible()){
34734                 this.show();
34735             }
34736         }
34737     },
34738
34739     /**
34740      * Adds the passed ContentPanel(s) to this region.
34741      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34742      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34743      */
34744     add : function(panel)
34745     {
34746         if(arguments.length > 1){
34747             for(var i = 0, len = arguments.length; i < len; i++) {
34748                 this.add(arguments[i]);
34749             }
34750             return null;
34751         }
34752         
34753         // if we have not been rendered yet, then we can not really do much of this..
34754         if (!this.bodyEl) {
34755             this.unrendered_panels.push(panel);
34756             return panel;
34757         }
34758         
34759         
34760         
34761         
34762         if(this.hasPanel(panel)){
34763             this.showPanel(panel);
34764             return panel;
34765         }
34766         panel.setRegion(this);
34767         this.panels.add(panel);
34768        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34769             // sinle panel - no tab...?? would it not be better to render it with the tabs,
34770             // and hide them... ???
34771             this.bodyEl.dom.appendChild(panel.getEl().dom);
34772             if(panel.background !== true){
34773                 this.setActivePanel(panel);
34774             }
34775             this.fireEvent("paneladded", this, panel);
34776             return panel;
34777         }
34778         */
34779         if(!this.tabs){
34780             this.initTabs();
34781         }else{
34782             this.initPanelAsTab(panel);
34783         }
34784         
34785         
34786         if(panel.background !== true){
34787             this.tabs.activate(panel.getEl().id);
34788         }
34789         this.fireEvent("paneladded", this, panel);
34790         return panel;
34791     },
34792
34793     /**
34794      * Hides the tab for the specified panel.
34795      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34796      */
34797     hidePanel : function(panel){
34798         if(this.tabs && (panel = this.getPanel(panel))){
34799             this.tabs.hideTab(panel.getEl().id);
34800         }
34801     },
34802
34803     /**
34804      * Unhides the tab for a previously hidden panel.
34805      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34806      */
34807     unhidePanel : function(panel){
34808         if(this.tabs && (panel = this.getPanel(panel))){
34809             this.tabs.unhideTab(panel.getEl().id);
34810         }
34811     },
34812
34813     clearPanels : function(){
34814         while(this.panels.getCount() > 0){
34815              this.remove(this.panels.first());
34816         }
34817     },
34818
34819     /**
34820      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34821      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34822      * @param {Boolean} preservePanel Overrides the config preservePanel option
34823      * @return {Roo.ContentPanel} The panel that was removed
34824      */
34825     remove : function(panel, preservePanel)
34826     {
34827         panel = this.getPanel(panel);
34828         if(!panel){
34829             return null;
34830         }
34831         var e = {};
34832         this.fireEvent("beforeremove", this, panel, e);
34833         if(e.cancel === true){
34834             return null;
34835         }
34836         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34837         var panelId = panel.getId();
34838         this.panels.removeKey(panelId);
34839         if(preservePanel){
34840             document.body.appendChild(panel.getEl().dom);
34841         }
34842         if(this.tabs){
34843             this.tabs.removeTab(panel.getEl().id);
34844         }else if (!preservePanel){
34845             this.bodyEl.dom.removeChild(panel.getEl().dom);
34846         }
34847         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34848             var p = this.panels.first();
34849             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34850             tempEl.appendChild(p.getEl().dom);
34851             this.bodyEl.update("");
34852             this.bodyEl.dom.appendChild(p.getEl().dom);
34853             tempEl = null;
34854             this.updateTitle(p.getTitle());
34855             this.tabs = null;
34856             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34857             this.setActivePanel(p);
34858         }
34859         panel.setRegion(null);
34860         if(this.activePanel == panel){
34861             this.activePanel = null;
34862         }
34863         if(this.config.autoDestroy !== false && preservePanel !== true){
34864             try{panel.destroy();}catch(e){}
34865         }
34866         this.fireEvent("panelremoved", this, panel);
34867         return panel;
34868     },
34869
34870     /**
34871      * Returns the TabPanel component used by this region
34872      * @return {Roo.TabPanel}
34873      */
34874     getTabs : function(){
34875         return this.tabs;
34876     },
34877
34878     createTool : function(parentEl, className){
34879         var btn = Roo.DomHelper.append(parentEl, {
34880             tag: "div",
34881             cls: "x-layout-tools-button",
34882             children: [ {
34883                 tag: "div",
34884                 cls: "roo-layout-tools-button-inner " + className,
34885                 html: "&#160;"
34886             }]
34887         }, true);
34888         btn.addClassOnOver("roo-layout-tools-button-over");
34889         return btn;
34890     }
34891 });/*
34892  * Based on:
34893  * Ext JS Library 1.1.1
34894  * Copyright(c) 2006-2007, Ext JS, LLC.
34895  *
34896  * Originally Released Under LGPL - original licence link has changed is not relivant.
34897  *
34898  * Fork - LGPL
34899  * <script type="text/javascript">
34900  */
34901  
34902
34903
34904 /**
34905  * @class Roo.SplitLayoutRegion
34906  * @extends Roo.LayoutRegion
34907  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34908  */
34909 Roo.bootstrap.layout.Split = function(config){
34910     this.cursor = config.cursor;
34911     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34912 };
34913
34914 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34915 {
34916     splitTip : "Drag to resize.",
34917     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34918     useSplitTips : false,
34919
34920     applyConfig : function(config){
34921         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34922     },
34923     
34924     onRender : function(ctr,pos) {
34925         
34926         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34927         if(!this.config.split){
34928             return;
34929         }
34930         if(!this.split){
34931             
34932             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34933                             tag: "div",
34934                             id: this.el.id + "-split",
34935                             cls: "roo-layout-split roo-layout-split-"+this.position,
34936                             html: "&#160;"
34937             });
34938             /** The SplitBar for this region 
34939             * @type Roo.SplitBar */
34940             // does not exist yet...
34941             Roo.log([this.position, this.orientation]);
34942             
34943             this.split = new Roo.bootstrap.SplitBar({
34944                 dragElement : splitEl,
34945                 resizingElement: this.el,
34946                 orientation : this.orientation
34947             });
34948             
34949             this.split.on("moved", this.onSplitMove, this);
34950             this.split.useShim = this.config.useShim === true;
34951             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34952             if(this.useSplitTips){
34953                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34954             }
34955             //if(config.collapsible){
34956             //    this.split.el.on("dblclick", this.collapse,  this);
34957             //}
34958         }
34959         if(typeof this.config.minSize != "undefined"){
34960             this.split.minSize = this.config.minSize;
34961         }
34962         if(typeof this.config.maxSize != "undefined"){
34963             this.split.maxSize = this.config.maxSize;
34964         }
34965         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34966             this.hideSplitter();
34967         }
34968         
34969     },
34970
34971     getHMaxSize : function(){
34972          var cmax = this.config.maxSize || 10000;
34973          var center = this.mgr.getRegion("center");
34974          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34975     },
34976
34977     getVMaxSize : function(){
34978          var cmax = this.config.maxSize || 10000;
34979          var center = this.mgr.getRegion("center");
34980          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34981     },
34982
34983     onSplitMove : function(split, newSize){
34984         this.fireEvent("resized", this, newSize);
34985     },
34986     
34987     /** 
34988      * Returns the {@link Roo.SplitBar} for this region.
34989      * @return {Roo.SplitBar}
34990      */
34991     getSplitBar : function(){
34992         return this.split;
34993     },
34994     
34995     hide : function(){
34996         this.hideSplitter();
34997         Roo.bootstrap.layout.Split.superclass.hide.call(this);
34998     },
34999
35000     hideSplitter : function(){
35001         if(this.split){
35002             this.split.el.setLocation(-2000,-2000);
35003             this.split.el.hide();
35004         }
35005     },
35006
35007     show : function(){
35008         if(this.split){
35009             this.split.el.show();
35010         }
35011         Roo.bootstrap.layout.Split.superclass.show.call(this);
35012     },
35013     
35014     beforeSlide: function(){
35015         if(Roo.isGecko){// firefox overflow auto bug workaround
35016             this.bodyEl.clip();
35017             if(this.tabs) {
35018                 this.tabs.bodyEl.clip();
35019             }
35020             if(this.activePanel){
35021                 this.activePanel.getEl().clip();
35022                 
35023                 if(this.activePanel.beforeSlide){
35024                     this.activePanel.beforeSlide();
35025                 }
35026             }
35027         }
35028     },
35029     
35030     afterSlide : function(){
35031         if(Roo.isGecko){// firefox overflow auto bug workaround
35032             this.bodyEl.unclip();
35033             if(this.tabs) {
35034                 this.tabs.bodyEl.unclip();
35035             }
35036             if(this.activePanel){
35037                 this.activePanel.getEl().unclip();
35038                 if(this.activePanel.afterSlide){
35039                     this.activePanel.afterSlide();
35040                 }
35041             }
35042         }
35043     },
35044
35045     initAutoHide : function(){
35046         if(this.autoHide !== false){
35047             if(!this.autoHideHd){
35048                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35049                 this.autoHideHd = {
35050                     "mouseout": function(e){
35051                         if(!e.within(this.el, true)){
35052                             st.delay(500);
35053                         }
35054                     },
35055                     "mouseover" : function(e){
35056                         st.cancel();
35057                     },
35058                     scope : this
35059                 };
35060             }
35061             this.el.on(this.autoHideHd);
35062         }
35063     },
35064
35065     clearAutoHide : function(){
35066         if(this.autoHide !== false){
35067             this.el.un("mouseout", this.autoHideHd.mouseout);
35068             this.el.un("mouseover", this.autoHideHd.mouseover);
35069         }
35070     },
35071
35072     clearMonitor : function(){
35073         Roo.get(document).un("click", this.slideInIf, this);
35074     },
35075
35076     // these names are backwards but not changed for compat
35077     slideOut : function(){
35078         if(this.isSlid || this.el.hasActiveFx()){
35079             return;
35080         }
35081         this.isSlid = true;
35082         if(this.collapseBtn){
35083             this.collapseBtn.hide();
35084         }
35085         this.closeBtnState = this.closeBtn.getStyle('display');
35086         this.closeBtn.hide();
35087         if(this.stickBtn){
35088             this.stickBtn.show();
35089         }
35090         this.el.show();
35091         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35092         this.beforeSlide();
35093         this.el.setStyle("z-index", 10001);
35094         this.el.slideIn(this.getSlideAnchor(), {
35095             callback: function(){
35096                 this.afterSlide();
35097                 this.initAutoHide();
35098                 Roo.get(document).on("click", this.slideInIf, this);
35099                 this.fireEvent("slideshow", this);
35100             },
35101             scope: this,
35102             block: true
35103         });
35104     },
35105
35106     afterSlideIn : function(){
35107         this.clearAutoHide();
35108         this.isSlid = false;
35109         this.clearMonitor();
35110         this.el.setStyle("z-index", "");
35111         if(this.collapseBtn){
35112             this.collapseBtn.show();
35113         }
35114         this.closeBtn.setStyle('display', this.closeBtnState);
35115         if(this.stickBtn){
35116             this.stickBtn.hide();
35117         }
35118         this.fireEvent("slidehide", this);
35119     },
35120
35121     slideIn : function(cb){
35122         if(!this.isSlid || this.el.hasActiveFx()){
35123             Roo.callback(cb);
35124             return;
35125         }
35126         this.isSlid = false;
35127         this.beforeSlide();
35128         this.el.slideOut(this.getSlideAnchor(), {
35129             callback: function(){
35130                 this.el.setLeftTop(-10000, -10000);
35131                 this.afterSlide();
35132                 this.afterSlideIn();
35133                 Roo.callback(cb);
35134             },
35135             scope: this,
35136             block: true
35137         });
35138     },
35139     
35140     slideInIf : function(e){
35141         if(!e.within(this.el)){
35142             this.slideIn();
35143         }
35144     },
35145
35146     animateCollapse : function(){
35147         this.beforeSlide();
35148         this.el.setStyle("z-index", 20000);
35149         var anchor = this.getSlideAnchor();
35150         this.el.slideOut(anchor, {
35151             callback : function(){
35152                 this.el.setStyle("z-index", "");
35153                 this.collapsedEl.slideIn(anchor, {duration:.3});
35154                 this.afterSlide();
35155                 this.el.setLocation(-10000,-10000);
35156                 this.el.hide();
35157                 this.fireEvent("collapsed", this);
35158             },
35159             scope: this,
35160             block: true
35161         });
35162     },
35163
35164     animateExpand : function(){
35165         this.beforeSlide();
35166         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35167         this.el.setStyle("z-index", 20000);
35168         this.collapsedEl.hide({
35169             duration:.1
35170         });
35171         this.el.slideIn(this.getSlideAnchor(), {
35172             callback : function(){
35173                 this.el.setStyle("z-index", "");
35174                 this.afterSlide();
35175                 if(this.split){
35176                     this.split.el.show();
35177                 }
35178                 this.fireEvent("invalidated", this);
35179                 this.fireEvent("expanded", this);
35180             },
35181             scope: this,
35182             block: true
35183         });
35184     },
35185
35186     anchors : {
35187         "west" : "left",
35188         "east" : "right",
35189         "north" : "top",
35190         "south" : "bottom"
35191     },
35192
35193     sanchors : {
35194         "west" : "l",
35195         "east" : "r",
35196         "north" : "t",
35197         "south" : "b"
35198     },
35199
35200     canchors : {
35201         "west" : "tl-tr",
35202         "east" : "tr-tl",
35203         "north" : "tl-bl",
35204         "south" : "bl-tl"
35205     },
35206
35207     getAnchor : function(){
35208         return this.anchors[this.position];
35209     },
35210
35211     getCollapseAnchor : function(){
35212         return this.canchors[this.position];
35213     },
35214
35215     getSlideAnchor : function(){
35216         return this.sanchors[this.position];
35217     },
35218
35219     getAlignAdj : function(){
35220         var cm = this.cmargins;
35221         switch(this.position){
35222             case "west":
35223                 return [0, 0];
35224             break;
35225             case "east":
35226                 return [0, 0];
35227             break;
35228             case "north":
35229                 return [0, 0];
35230             break;
35231             case "south":
35232                 return [0, 0];
35233             break;
35234         }
35235     },
35236
35237     getExpandAdj : function(){
35238         var c = this.collapsedEl, cm = this.cmargins;
35239         switch(this.position){
35240             case "west":
35241                 return [-(cm.right+c.getWidth()+cm.left), 0];
35242             break;
35243             case "east":
35244                 return [cm.right+c.getWidth()+cm.left, 0];
35245             break;
35246             case "north":
35247                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35248             break;
35249             case "south":
35250                 return [0, cm.top+cm.bottom+c.getHeight()];
35251             break;
35252         }
35253     }
35254 });/*
35255  * Based on:
35256  * Ext JS Library 1.1.1
35257  * Copyright(c) 2006-2007, Ext JS, LLC.
35258  *
35259  * Originally Released Under LGPL - original licence link has changed is not relivant.
35260  *
35261  * Fork - LGPL
35262  * <script type="text/javascript">
35263  */
35264 /*
35265  * These classes are private internal classes
35266  */
35267 Roo.bootstrap.layout.Center = function(config){
35268     config.region = "center";
35269     Roo.bootstrap.layout.Region.call(this, config);
35270     this.visible = true;
35271     this.minWidth = config.minWidth || 20;
35272     this.minHeight = config.minHeight || 20;
35273 };
35274
35275 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35276     hide : function(){
35277         // center panel can't be hidden
35278     },
35279     
35280     show : function(){
35281         // center panel can't be hidden
35282     },
35283     
35284     getMinWidth: function(){
35285         return this.minWidth;
35286     },
35287     
35288     getMinHeight: function(){
35289         return this.minHeight;
35290     }
35291 });
35292
35293
35294
35295
35296  
35297
35298
35299
35300
35301
35302 Roo.bootstrap.layout.North = function(config)
35303 {
35304     config.region = 'north';
35305     config.cursor = 'n-resize';
35306     
35307     Roo.bootstrap.layout.Split.call(this, config);
35308     
35309     
35310     if(this.split){
35311         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35312         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35313         this.split.el.addClass("roo-layout-split-v");
35314     }
35315     var size = config.initialSize || config.height;
35316     if(typeof size != "undefined"){
35317         this.el.setHeight(size);
35318     }
35319 };
35320 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35321 {
35322     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35323     
35324     
35325     
35326     getBox : function(){
35327         if(this.collapsed){
35328             return this.collapsedEl.getBox();
35329         }
35330         var box = this.el.getBox();
35331         if(this.split){
35332             box.height += this.split.el.getHeight();
35333         }
35334         return box;
35335     },
35336     
35337     updateBox : function(box){
35338         if(this.split && !this.collapsed){
35339             box.height -= this.split.el.getHeight();
35340             this.split.el.setLeft(box.x);
35341             this.split.el.setTop(box.y+box.height);
35342             this.split.el.setWidth(box.width);
35343         }
35344         if(this.collapsed){
35345             this.updateBody(box.width, null);
35346         }
35347         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35348     }
35349 });
35350
35351
35352
35353
35354
35355 Roo.bootstrap.layout.South = function(config){
35356     config.region = 'south';
35357     config.cursor = 's-resize';
35358     Roo.bootstrap.layout.Split.call(this, config);
35359     if(this.split){
35360         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
35361         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35362         this.split.el.addClass("roo-layout-split-v");
35363     }
35364     var size = config.initialSize || config.height;
35365     if(typeof size != "undefined"){
35366         this.el.setHeight(size);
35367     }
35368 };
35369
35370 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
35371     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35372     getBox : function(){
35373         if(this.collapsed){
35374             return this.collapsedEl.getBox();
35375         }
35376         var box = this.el.getBox();
35377         if(this.split){
35378             var sh = this.split.el.getHeight();
35379             box.height += sh;
35380             box.y -= sh;
35381         }
35382         return box;
35383     },
35384     
35385     updateBox : function(box){
35386         if(this.split && !this.collapsed){
35387             var sh = this.split.el.getHeight();
35388             box.height -= sh;
35389             box.y += sh;
35390             this.split.el.setLeft(box.x);
35391             this.split.el.setTop(box.y-sh);
35392             this.split.el.setWidth(box.width);
35393         }
35394         if(this.collapsed){
35395             this.updateBody(box.width, null);
35396         }
35397         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35398     }
35399 });
35400
35401 Roo.bootstrap.layout.East = function(config){
35402     config.region = "east";
35403     config.cursor = "e-resize";
35404     Roo.bootstrap.layout.Split.call(this, config);
35405     if(this.split){
35406         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
35407         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35408         this.split.el.addClass("roo-layout-split-h");
35409     }
35410     var size = config.initialSize || config.width;
35411     if(typeof size != "undefined"){
35412         this.el.setWidth(size);
35413     }
35414 };
35415 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
35416     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35417     getBox : function(){
35418         if(this.collapsed){
35419             return this.collapsedEl.getBox();
35420         }
35421         var box = this.el.getBox();
35422         if(this.split){
35423             var sw = this.split.el.getWidth();
35424             box.width += sw;
35425             box.x -= sw;
35426         }
35427         return box;
35428     },
35429
35430     updateBox : function(box){
35431         if(this.split && !this.collapsed){
35432             var sw = this.split.el.getWidth();
35433             box.width -= sw;
35434             this.split.el.setLeft(box.x);
35435             this.split.el.setTop(box.y);
35436             this.split.el.setHeight(box.height);
35437             box.x += sw;
35438         }
35439         if(this.collapsed){
35440             this.updateBody(null, box.height);
35441         }
35442         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35443     }
35444 });
35445
35446 Roo.bootstrap.layout.West = function(config){
35447     config.region = "west";
35448     config.cursor = "w-resize";
35449     
35450     Roo.bootstrap.layout.Split.call(this, config);
35451     if(this.split){
35452         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
35453         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35454         this.split.el.addClass("roo-layout-split-h");
35455     }
35456     
35457 };
35458 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
35459     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35460     
35461     onRender: function(ctr, pos)
35462     {
35463         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
35464         var size = this.config.initialSize || this.config.width;
35465         if(typeof size != "undefined"){
35466             this.el.setWidth(size);
35467         }
35468     },
35469     
35470     getBox : function(){
35471         if(this.collapsed){
35472             return this.collapsedEl.getBox();
35473         }
35474         var box = this.el.getBox();
35475         if(this.split){
35476             box.width += this.split.el.getWidth();
35477         }
35478         return box;
35479     },
35480     
35481     updateBox : function(box){
35482         if(this.split && !this.collapsed){
35483             var sw = this.split.el.getWidth();
35484             box.width -= sw;
35485             this.split.el.setLeft(box.x+box.width);
35486             this.split.el.setTop(box.y);
35487             this.split.el.setHeight(box.height);
35488         }
35489         if(this.collapsed){
35490             this.updateBody(null, box.height);
35491         }
35492         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35493     }
35494 });
35495 Roo.namespace("Roo.bootstrap.panel");/*
35496  * Based on:
35497  * Ext JS Library 1.1.1
35498  * Copyright(c) 2006-2007, Ext JS, LLC.
35499  *
35500  * Originally Released Under LGPL - original licence link has changed is not relivant.
35501  *
35502  * Fork - LGPL
35503  * <script type="text/javascript">
35504  */
35505 /**
35506  * @class Roo.ContentPanel
35507  * @extends Roo.util.Observable
35508  * A basic ContentPanel element.
35509  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35510  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35511  * @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
35512  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35513  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35514  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35515  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35516  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35517  * @cfg {String} title          The title for this panel
35518  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35519  * @cfg {String} url            Calls {@link #setUrl} with this value
35520  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35521  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35522  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35523  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35524  * @cfg {Boolean} badges render the badges
35525
35526  * @constructor
35527  * Create a new ContentPanel.
35528  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35529  * @param {String/Object} config A string to set only the title or a config object
35530  * @param {String} content (optional) Set the HTML content for this panel
35531  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35532  */
35533 Roo.bootstrap.panel.Content = function( config){
35534     
35535     this.tpl = config.tpl || false;
35536     
35537     var el = config.el;
35538     var content = config.content;
35539
35540     if(config.autoCreate){ // xtype is available if this is called from factory
35541         el = Roo.id();
35542     }
35543     this.el = Roo.get(el);
35544     if(!this.el && config && config.autoCreate){
35545         if(typeof config.autoCreate == "object"){
35546             if(!config.autoCreate.id){
35547                 config.autoCreate.id = config.id||el;
35548             }
35549             this.el = Roo.DomHelper.append(document.body,
35550                         config.autoCreate, true);
35551         }else{
35552             var elcfg =  {   tag: "div",
35553                             cls: "roo-layout-inactive-content",
35554                             id: config.id||el
35555                             };
35556             if (config.html) {
35557                 elcfg.html = config.html;
35558                 
35559             }
35560                         
35561             this.el = Roo.DomHelper.append(document.body, elcfg , true);
35562         }
35563     } 
35564     this.closable = false;
35565     this.loaded = false;
35566     this.active = false;
35567    
35568       
35569     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
35570         
35571         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
35572         
35573         this.wrapEl = this.el; //this.el.wrap();
35574         var ti = [];
35575         if (config.toolbar.items) {
35576             ti = config.toolbar.items ;
35577             delete config.toolbar.items ;
35578         }
35579         
35580         var nitems = [];
35581         this.toolbar.render(this.wrapEl, 'before');
35582         for(var i =0;i < ti.length;i++) {
35583           //  Roo.log(['add child', items[i]]);
35584             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35585         }
35586         this.toolbar.items = nitems;
35587         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
35588         delete config.toolbar;
35589         
35590     }
35591     /*
35592     // xtype created footer. - not sure if will work as we normally have to render first..
35593     if (this.footer && !this.footer.el && this.footer.xtype) {
35594         if (!this.wrapEl) {
35595             this.wrapEl = this.el.wrap();
35596         }
35597     
35598         this.footer.container = this.wrapEl.createChild();
35599          
35600         this.footer = Roo.factory(this.footer, Roo);
35601         
35602     }
35603     */
35604     
35605      if(typeof config == "string"){
35606         this.title = config;
35607     }else{
35608         Roo.apply(this, config);
35609     }
35610     
35611     if(this.resizeEl){
35612         this.resizeEl = Roo.get(this.resizeEl, true);
35613     }else{
35614         this.resizeEl = this.el;
35615     }
35616     // handle view.xtype
35617     
35618  
35619     
35620     
35621     this.addEvents({
35622         /**
35623          * @event activate
35624          * Fires when this panel is activated. 
35625          * @param {Roo.ContentPanel} this
35626          */
35627         "activate" : true,
35628         /**
35629          * @event deactivate
35630          * Fires when this panel is activated. 
35631          * @param {Roo.ContentPanel} this
35632          */
35633         "deactivate" : true,
35634
35635         /**
35636          * @event resize
35637          * Fires when this panel is resized if fitToFrame is true.
35638          * @param {Roo.ContentPanel} this
35639          * @param {Number} width The width after any component adjustments
35640          * @param {Number} height The height after any component adjustments
35641          */
35642         "resize" : true,
35643         
35644          /**
35645          * @event render
35646          * Fires when this tab is created
35647          * @param {Roo.ContentPanel} this
35648          */
35649         "render" : true
35650         
35651         
35652         
35653     });
35654     
35655
35656     
35657     
35658     if(this.autoScroll){
35659         this.resizeEl.setStyle("overflow", "auto");
35660     } else {
35661         // fix randome scrolling
35662         //this.el.on('scroll', function() {
35663         //    Roo.log('fix random scolling');
35664         //    this.scrollTo('top',0); 
35665         //});
35666     }
35667     content = content || this.content;
35668     if(content){
35669         this.setContent(content);
35670     }
35671     if(config && config.url){
35672         this.setUrl(this.url, this.params, this.loadOnce);
35673     }
35674     
35675     
35676     
35677     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35678     
35679     if (this.view && typeof(this.view.xtype) != 'undefined') {
35680         this.view.el = this.el.appendChild(document.createElement("div"));
35681         this.view = Roo.factory(this.view); 
35682         this.view.render  &&  this.view.render(false, '');  
35683     }
35684     
35685     
35686     this.fireEvent('render', this);
35687 };
35688
35689 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35690     
35691     tabTip : '',
35692     
35693     setRegion : function(region){
35694         this.region = region;
35695         this.setActiveClass(region && !this.background);
35696     },
35697     
35698     
35699     setActiveClass: function(state)
35700     {
35701         if(state){
35702            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35703            this.el.setStyle('position','relative');
35704         }else{
35705            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35706            this.el.setStyle('position', 'absolute');
35707         } 
35708     },
35709     
35710     /**
35711      * Returns the toolbar for this Panel if one was configured. 
35712      * @return {Roo.Toolbar} 
35713      */
35714     getToolbar : function(){
35715         return this.toolbar;
35716     },
35717     
35718     setActiveState : function(active)
35719     {
35720         this.active = active;
35721         this.setActiveClass(active);
35722         if(!active){
35723             this.fireEvent("deactivate", this);
35724         }else{
35725             this.fireEvent("activate", this);
35726         }
35727     },
35728     /**
35729      * Updates this panel's element
35730      * @param {String} content The new content
35731      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35732     */
35733     setContent : function(content, loadScripts){
35734         this.el.update(content, loadScripts);
35735     },
35736
35737     ignoreResize : function(w, h){
35738         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35739             return true;
35740         }else{
35741             this.lastSize = {width: w, height: h};
35742             return false;
35743         }
35744     },
35745     /**
35746      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35747      * @return {Roo.UpdateManager} The UpdateManager
35748      */
35749     getUpdateManager : function(){
35750         return this.el.getUpdateManager();
35751     },
35752      /**
35753      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35754      * @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:
35755 <pre><code>
35756 panel.load({
35757     url: "your-url.php",
35758     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35759     callback: yourFunction,
35760     scope: yourObject, //(optional scope)
35761     discardUrl: false,
35762     nocache: false,
35763     text: "Loading...",
35764     timeout: 30,
35765     scripts: false
35766 });
35767 </code></pre>
35768      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35769      * 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.
35770      * @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}
35771      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35772      * @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.
35773      * @return {Roo.ContentPanel} this
35774      */
35775     load : function(){
35776         var um = this.el.getUpdateManager();
35777         um.update.apply(um, arguments);
35778         return this;
35779     },
35780
35781
35782     /**
35783      * 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.
35784      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35785      * @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)
35786      * @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)
35787      * @return {Roo.UpdateManager} The UpdateManager
35788      */
35789     setUrl : function(url, params, loadOnce){
35790         if(this.refreshDelegate){
35791             this.removeListener("activate", this.refreshDelegate);
35792         }
35793         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35794         this.on("activate", this.refreshDelegate);
35795         return this.el.getUpdateManager();
35796     },
35797     
35798     _handleRefresh : function(url, params, loadOnce){
35799         if(!loadOnce || !this.loaded){
35800             var updater = this.el.getUpdateManager();
35801             updater.update(url, params, this._setLoaded.createDelegate(this));
35802         }
35803     },
35804     
35805     _setLoaded : function(){
35806         this.loaded = true;
35807     }, 
35808     
35809     /**
35810      * Returns this panel's id
35811      * @return {String} 
35812      */
35813     getId : function(){
35814         return this.el.id;
35815     },
35816     
35817     /** 
35818      * Returns this panel's element - used by regiosn to add.
35819      * @return {Roo.Element} 
35820      */
35821     getEl : function(){
35822         return this.wrapEl || this.el;
35823     },
35824     
35825    
35826     
35827     adjustForComponents : function(width, height)
35828     {
35829         //Roo.log('adjustForComponents ');
35830         if(this.resizeEl != this.el){
35831             width -= this.el.getFrameWidth('lr');
35832             height -= this.el.getFrameWidth('tb');
35833         }
35834         if(this.toolbar){
35835             var te = this.toolbar.getEl();
35836             te.setWidth(width);
35837             height -= te.getHeight();
35838         }
35839         if(this.footer){
35840             var te = this.footer.getEl();
35841             te.setWidth(width);
35842             height -= te.getHeight();
35843         }
35844         
35845         
35846         if(this.adjustments){
35847             width += this.adjustments[0];
35848             height += this.adjustments[1];
35849         }
35850         return {"width": width, "height": height};
35851     },
35852     
35853     setSize : function(width, height){
35854         if(this.fitToFrame && !this.ignoreResize(width, height)){
35855             if(this.fitContainer && this.resizeEl != this.el){
35856                 this.el.setSize(width, height);
35857             }
35858             var size = this.adjustForComponents(width, height);
35859             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35860             this.fireEvent('resize', this, size.width, size.height);
35861         }
35862     },
35863     
35864     /**
35865      * Returns this panel's title
35866      * @return {String} 
35867      */
35868     getTitle : function(){
35869         
35870         if (typeof(this.title) != 'object') {
35871             return this.title;
35872         }
35873         
35874         var t = '';
35875         for (var k in this.title) {
35876             if (!this.title.hasOwnProperty(k)) {
35877                 continue;
35878             }
35879             
35880             if (k.indexOf('-') >= 0) {
35881                 var s = k.split('-');
35882                 for (var i = 0; i<s.length; i++) {
35883                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
35884                 }
35885             } else {
35886                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
35887             }
35888         }
35889         return t;
35890     },
35891     
35892     /**
35893      * Set this panel's title
35894      * @param {String} title
35895      */
35896     setTitle : function(title){
35897         this.title = title;
35898         if(this.region){
35899             this.region.updatePanelTitle(this, title);
35900         }
35901     },
35902     
35903     /**
35904      * Returns true is this panel was configured to be closable
35905      * @return {Boolean} 
35906      */
35907     isClosable : function(){
35908         return this.closable;
35909     },
35910     
35911     beforeSlide : function(){
35912         this.el.clip();
35913         this.resizeEl.clip();
35914     },
35915     
35916     afterSlide : function(){
35917         this.el.unclip();
35918         this.resizeEl.unclip();
35919     },
35920     
35921     /**
35922      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35923      *   Will fail silently if the {@link #setUrl} method has not been called.
35924      *   This does not activate the panel, just updates its content.
35925      */
35926     refresh : function(){
35927         if(this.refreshDelegate){
35928            this.loaded = false;
35929            this.refreshDelegate();
35930         }
35931     },
35932     
35933     /**
35934      * Destroys this panel
35935      */
35936     destroy : function(){
35937         this.el.removeAllListeners();
35938         var tempEl = document.createElement("span");
35939         tempEl.appendChild(this.el.dom);
35940         tempEl.innerHTML = "";
35941         this.el.remove();
35942         this.el = null;
35943     },
35944     
35945     /**
35946      * form - if the content panel contains a form - this is a reference to it.
35947      * @type {Roo.form.Form}
35948      */
35949     form : false,
35950     /**
35951      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35952      *    This contains a reference to it.
35953      * @type {Roo.View}
35954      */
35955     view : false,
35956     
35957       /**
35958      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35959      * <pre><code>
35960
35961 layout.addxtype({
35962        xtype : 'Form',
35963        items: [ .... ]
35964    }
35965 );
35966
35967 </code></pre>
35968      * @param {Object} cfg Xtype definition of item to add.
35969      */
35970     
35971     
35972     getChildContainer: function () {
35973         return this.getEl();
35974     }
35975     
35976     
35977     /*
35978         var  ret = new Roo.factory(cfg);
35979         return ret;
35980         
35981         
35982         // add form..
35983         if (cfg.xtype.match(/^Form$/)) {
35984             
35985             var el;
35986             //if (this.footer) {
35987             //    el = this.footer.container.insertSibling(false, 'before');
35988             //} else {
35989                 el = this.el.createChild();
35990             //}
35991
35992             this.form = new  Roo.form.Form(cfg);
35993             
35994             
35995             if ( this.form.allItems.length) {
35996                 this.form.render(el.dom);
35997             }
35998             return this.form;
35999         }
36000         // should only have one of theses..
36001         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36002             // views.. should not be just added - used named prop 'view''
36003             
36004             cfg.el = this.el.appendChild(document.createElement("div"));
36005             // factory?
36006             
36007             var ret = new Roo.factory(cfg);
36008              
36009              ret.render && ret.render(false, ''); // render blank..
36010             this.view = ret;
36011             return ret;
36012         }
36013         return false;
36014     }
36015     \*/
36016 });
36017  
36018 /**
36019  * @class Roo.bootstrap.panel.Grid
36020  * @extends Roo.bootstrap.panel.Content
36021  * @constructor
36022  * Create a new GridPanel.
36023  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36024  * @param {Object} config A the config object
36025   
36026  */
36027
36028
36029
36030 Roo.bootstrap.panel.Grid = function(config)
36031 {
36032     
36033       
36034     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36035         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36036
36037     config.el = this.wrapper;
36038     //this.el = this.wrapper;
36039     
36040       if (config.container) {
36041         // ctor'ed from a Border/panel.grid
36042         
36043         
36044         this.wrapper.setStyle("overflow", "hidden");
36045         this.wrapper.addClass('roo-grid-container');
36046
36047     }
36048     
36049     
36050     if(config.toolbar){
36051         var tool_el = this.wrapper.createChild();    
36052         this.toolbar = Roo.factory(config.toolbar);
36053         var ti = [];
36054         if (config.toolbar.items) {
36055             ti = config.toolbar.items ;
36056             delete config.toolbar.items ;
36057         }
36058         
36059         var nitems = [];
36060         this.toolbar.render(tool_el);
36061         for(var i =0;i < ti.length;i++) {
36062           //  Roo.log(['add child', items[i]]);
36063             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36064         }
36065         this.toolbar.items = nitems;
36066         
36067         delete config.toolbar;
36068     }
36069     
36070     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36071     config.grid.scrollBody = true;;
36072     config.grid.monitorWindowResize = false; // turn off autosizing
36073     config.grid.autoHeight = false;
36074     config.grid.autoWidth = false;
36075     
36076     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36077     
36078     if (config.background) {
36079         // render grid on panel activation (if panel background)
36080         this.on('activate', function(gp) {
36081             if (!gp.grid.rendered) {
36082                 gp.grid.render(this.wrapper);
36083                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36084             }
36085         });
36086             
36087     } else {
36088         this.grid.render(this.wrapper);
36089         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36090
36091     }
36092     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36093     // ??? needed ??? config.el = this.wrapper;
36094     
36095     
36096     
36097   
36098     // xtype created footer. - not sure if will work as we normally have to render first..
36099     if (this.footer && !this.footer.el && this.footer.xtype) {
36100         
36101         var ctr = this.grid.getView().getFooterPanel(true);
36102         this.footer.dataSource = this.grid.dataSource;
36103         this.footer = Roo.factory(this.footer, Roo);
36104         this.footer.render(ctr);
36105         
36106     }
36107     
36108     
36109     
36110     
36111      
36112 };
36113
36114 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36115     getId : function(){
36116         return this.grid.id;
36117     },
36118     
36119     /**
36120      * Returns the grid for this panel
36121      * @return {Roo.bootstrap.Table} 
36122      */
36123     getGrid : function(){
36124         return this.grid;    
36125     },
36126     
36127     setSize : function(width, height){
36128         if(!this.ignoreResize(width, height)){
36129             var grid = this.grid;
36130             var size = this.adjustForComponents(width, height);
36131             var gridel = grid.getGridEl();
36132             gridel.setSize(size.width, size.height);
36133             /*
36134             var thd = grid.getGridEl().select('thead',true).first();
36135             var tbd = grid.getGridEl().select('tbody', true).first();
36136             if (tbd) {
36137                 tbd.setSize(width, height - thd.getHeight());
36138             }
36139             */
36140             grid.autoSize();
36141         }
36142     },
36143      
36144     
36145     
36146     beforeSlide : function(){
36147         this.grid.getView().scroller.clip();
36148     },
36149     
36150     afterSlide : function(){
36151         this.grid.getView().scroller.unclip();
36152     },
36153     
36154     destroy : function(){
36155         this.grid.destroy();
36156         delete this.grid;
36157         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36158     }
36159 });
36160
36161 /**
36162  * @class Roo.bootstrap.panel.Nest
36163  * @extends Roo.bootstrap.panel.Content
36164  * @constructor
36165  * Create a new Panel, that can contain a layout.Border.
36166  * 
36167  * 
36168  * @param {Roo.BorderLayout} layout The layout for this panel
36169  * @param {String/Object} config A string to set only the title or a config object
36170  */
36171 Roo.bootstrap.panel.Nest = function(config)
36172 {
36173     // construct with only one argument..
36174     /* FIXME - implement nicer consturctors
36175     if (layout.layout) {
36176         config = layout;
36177         layout = config.layout;
36178         delete config.layout;
36179     }
36180     if (layout.xtype && !layout.getEl) {
36181         // then layout needs constructing..
36182         layout = Roo.factory(layout, Roo);
36183     }
36184     */
36185     
36186     config.el =  config.layout.getEl();
36187     
36188     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36189     
36190     config.layout.monitorWindowResize = false; // turn off autosizing
36191     this.layout = config.layout;
36192     this.layout.getEl().addClass("roo-layout-nested-layout");
36193     
36194     
36195     
36196     
36197 };
36198
36199 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36200
36201     setSize : function(width, height){
36202         if(!this.ignoreResize(width, height)){
36203             var size = this.adjustForComponents(width, height);
36204             var el = this.layout.getEl();
36205             if (size.height < 1) {
36206                 el.setWidth(size.width);   
36207             } else {
36208                 el.setSize(size.width, size.height);
36209             }
36210             var touch = el.dom.offsetWidth;
36211             this.layout.layout();
36212             // ie requires a double layout on the first pass
36213             if(Roo.isIE && !this.initialized){
36214                 this.initialized = true;
36215                 this.layout.layout();
36216             }
36217         }
36218     },
36219     
36220     // activate all subpanels if not currently active..
36221     
36222     setActiveState : function(active){
36223         this.active = active;
36224         this.setActiveClass(active);
36225         
36226         if(!active){
36227             this.fireEvent("deactivate", this);
36228             return;
36229         }
36230         
36231         this.fireEvent("activate", this);
36232         // not sure if this should happen before or after..
36233         if (!this.layout) {
36234             return; // should not happen..
36235         }
36236         var reg = false;
36237         for (var r in this.layout.regions) {
36238             reg = this.layout.getRegion(r);
36239             if (reg.getActivePanel()) {
36240                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36241                 reg.setActivePanel(reg.getActivePanel());
36242                 continue;
36243             }
36244             if (!reg.panels.length) {
36245                 continue;
36246             }
36247             reg.showPanel(reg.getPanel(0));
36248         }
36249         
36250         
36251         
36252         
36253     },
36254     
36255     /**
36256      * Returns the nested BorderLayout for this panel
36257      * @return {Roo.BorderLayout} 
36258      */
36259     getLayout : function(){
36260         return this.layout;
36261     },
36262     
36263      /**
36264      * Adds a xtype elements to the layout of the nested panel
36265      * <pre><code>
36266
36267 panel.addxtype({
36268        xtype : 'ContentPanel',
36269        region: 'west',
36270        items: [ .... ]
36271    }
36272 );
36273
36274 panel.addxtype({
36275         xtype : 'NestedLayoutPanel',
36276         region: 'west',
36277         layout: {
36278            center: { },
36279            west: { }   
36280         },
36281         items : [ ... list of content panels or nested layout panels.. ]
36282    }
36283 );
36284 </code></pre>
36285      * @param {Object} cfg Xtype definition of item to add.
36286      */
36287     addxtype : function(cfg) {
36288         return this.layout.addxtype(cfg);
36289     
36290     }
36291 });        /*
36292  * Based on:
36293  * Ext JS Library 1.1.1
36294  * Copyright(c) 2006-2007, Ext JS, LLC.
36295  *
36296  * Originally Released Under LGPL - original licence link has changed is not relivant.
36297  *
36298  * Fork - LGPL
36299  * <script type="text/javascript">
36300  */
36301 /**
36302  * @class Roo.TabPanel
36303  * @extends Roo.util.Observable
36304  * A lightweight tab container.
36305  * <br><br>
36306  * Usage:
36307  * <pre><code>
36308 // basic tabs 1, built from existing content
36309 var tabs = new Roo.TabPanel("tabs1");
36310 tabs.addTab("script", "View Script");
36311 tabs.addTab("markup", "View Markup");
36312 tabs.activate("script");
36313
36314 // more advanced tabs, built from javascript
36315 var jtabs = new Roo.TabPanel("jtabs");
36316 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36317
36318 // set up the UpdateManager
36319 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36320 var updater = tab2.getUpdateManager();
36321 updater.setDefaultUrl("ajax1.htm");
36322 tab2.on('activate', updater.refresh, updater, true);
36323
36324 // Use setUrl for Ajax loading
36325 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36326 tab3.setUrl("ajax2.htm", null, true);
36327
36328 // Disabled tab
36329 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36330 tab4.disable();
36331
36332 jtabs.activate("jtabs-1");
36333  * </code></pre>
36334  * @constructor
36335  * Create a new TabPanel.
36336  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
36337  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
36338  */
36339 Roo.bootstrap.panel.Tabs = function(config){
36340     /**
36341     * The container element for this TabPanel.
36342     * @type Roo.Element
36343     */
36344     this.el = Roo.get(config.el);
36345     delete config.el;
36346     if(config){
36347         if(typeof config == "boolean"){
36348             this.tabPosition = config ? "bottom" : "top";
36349         }else{
36350             Roo.apply(this, config);
36351         }
36352     }
36353     
36354     if(this.tabPosition == "bottom"){
36355         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36356         this.el.addClass("roo-tabs-bottom");
36357     }
36358     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
36359     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
36360     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
36361     if(Roo.isIE){
36362         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
36363     }
36364     if(this.tabPosition != "bottom"){
36365         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
36366          * @type Roo.Element
36367          */
36368         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36369         this.el.addClass("roo-tabs-top");
36370     }
36371     this.items = [];
36372
36373     this.bodyEl.setStyle("position", "relative");
36374
36375     this.active = null;
36376     this.activateDelegate = this.activate.createDelegate(this);
36377
36378     this.addEvents({
36379         /**
36380          * @event tabchange
36381          * Fires when the active tab changes
36382          * @param {Roo.TabPanel} this
36383          * @param {Roo.TabPanelItem} activePanel The new active tab
36384          */
36385         "tabchange": true,
36386         /**
36387          * @event beforetabchange
36388          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
36389          * @param {Roo.TabPanel} this
36390          * @param {Object} e Set cancel to true on this object to cancel the tab change
36391          * @param {Roo.TabPanelItem} tab The tab being changed to
36392          */
36393         "beforetabchange" : true
36394     });
36395
36396     Roo.EventManager.onWindowResize(this.onResize, this);
36397     this.cpad = this.el.getPadding("lr");
36398     this.hiddenCount = 0;
36399
36400
36401     // toolbar on the tabbar support...
36402     if (this.toolbar) {
36403         alert("no toolbar support yet");
36404         this.toolbar  = false;
36405         /*
36406         var tcfg = this.toolbar;
36407         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
36408         this.toolbar = new Roo.Toolbar(tcfg);
36409         if (Roo.isSafari) {
36410             var tbl = tcfg.container.child('table', true);
36411             tbl.setAttribute('width', '100%');
36412         }
36413         */
36414         
36415     }
36416    
36417
36418
36419     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
36420 };
36421
36422 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
36423     /*
36424      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
36425      */
36426     tabPosition : "top",
36427     /*
36428      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
36429      */
36430     currentTabWidth : 0,
36431     /*
36432      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
36433      */
36434     minTabWidth : 40,
36435     /*
36436      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
36437      */
36438     maxTabWidth : 250,
36439     /*
36440      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
36441      */
36442     preferredTabWidth : 175,
36443     /*
36444      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
36445      */
36446     resizeTabs : false,
36447     /*
36448      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
36449      */
36450     monitorResize : true,
36451     /*
36452      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
36453      */
36454     toolbar : false,
36455
36456     /**
36457      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
36458      * @param {String} id The id of the div to use <b>or create</b>
36459      * @param {String} text The text for the tab
36460      * @param {String} content (optional) Content to put in the TabPanelItem body
36461      * @param {Boolean} closable (optional) True to create a close icon on the tab
36462      * @return {Roo.TabPanelItem} The created TabPanelItem
36463      */
36464     addTab : function(id, text, content, closable, tpl)
36465     {
36466         var item = new Roo.bootstrap.panel.TabItem({
36467             panel: this,
36468             id : id,
36469             text : text,
36470             closable : closable,
36471             tpl : tpl
36472         });
36473         this.addTabItem(item);
36474         if(content){
36475             item.setContent(content);
36476         }
36477         return item;
36478     },
36479
36480     /**
36481      * Returns the {@link Roo.TabPanelItem} with the specified id/index
36482      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
36483      * @return {Roo.TabPanelItem}
36484      */
36485     getTab : function(id){
36486         return this.items[id];
36487     },
36488
36489     /**
36490      * Hides the {@link Roo.TabPanelItem} with the specified id/index
36491      * @param {String/Number} id The id or index of the TabPanelItem to hide.
36492      */
36493     hideTab : function(id){
36494         var t = this.items[id];
36495         if(!t.isHidden()){
36496            t.setHidden(true);
36497            this.hiddenCount++;
36498            this.autoSizeTabs();
36499         }
36500     },
36501
36502     /**
36503      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
36504      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
36505      */
36506     unhideTab : function(id){
36507         var t = this.items[id];
36508         if(t.isHidden()){
36509            t.setHidden(false);
36510            this.hiddenCount--;
36511            this.autoSizeTabs();
36512         }
36513     },
36514
36515     /**
36516      * Adds an existing {@link Roo.TabPanelItem}.
36517      * @param {Roo.TabPanelItem} item The TabPanelItem to add
36518      */
36519     addTabItem : function(item){
36520         this.items[item.id] = item;
36521         this.items.push(item);
36522       //  if(this.resizeTabs){
36523     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
36524   //         this.autoSizeTabs();
36525 //        }else{
36526 //            item.autoSize();
36527        // }
36528     },
36529
36530     /**
36531      * Removes a {@link Roo.TabPanelItem}.
36532      * @param {String/Number} id The id or index of the TabPanelItem to remove.
36533      */
36534     removeTab : function(id){
36535         var items = this.items;
36536         var tab = items[id];
36537         if(!tab) { return; }
36538         var index = items.indexOf(tab);
36539         if(this.active == tab && items.length > 1){
36540             var newTab = this.getNextAvailable(index);
36541             if(newTab) {
36542                 newTab.activate();
36543             }
36544         }
36545         this.stripEl.dom.removeChild(tab.pnode.dom);
36546         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
36547             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
36548         }
36549         items.splice(index, 1);
36550         delete this.items[tab.id];
36551         tab.fireEvent("close", tab);
36552         tab.purgeListeners();
36553         this.autoSizeTabs();
36554     },
36555
36556     getNextAvailable : function(start){
36557         var items = this.items;
36558         var index = start;
36559         // look for a next tab that will slide over to
36560         // replace the one being removed
36561         while(index < items.length){
36562             var item = items[++index];
36563             if(item && !item.isHidden()){
36564                 return item;
36565             }
36566         }
36567         // if one isn't found select the previous tab (on the left)
36568         index = start;
36569         while(index >= 0){
36570             var item = items[--index];
36571             if(item && !item.isHidden()){
36572                 return item;
36573             }
36574         }
36575         return null;
36576     },
36577
36578     /**
36579      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
36580      * @param {String/Number} id The id or index of the TabPanelItem to disable.
36581      */
36582     disableTab : function(id){
36583         var tab = this.items[id];
36584         if(tab && this.active != tab){
36585             tab.disable();
36586         }
36587     },
36588
36589     /**
36590      * Enables a {@link Roo.TabPanelItem} that is disabled.
36591      * @param {String/Number} id The id or index of the TabPanelItem to enable.
36592      */
36593     enableTab : function(id){
36594         var tab = this.items[id];
36595         tab.enable();
36596     },
36597
36598     /**
36599      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
36600      * @param {String/Number} id The id or index of the TabPanelItem to activate.
36601      * @return {Roo.TabPanelItem} The TabPanelItem.
36602      */
36603     activate : function(id){
36604         var tab = this.items[id];
36605         if(!tab){
36606             return null;
36607         }
36608         if(tab == this.active || tab.disabled){
36609             return tab;
36610         }
36611         var e = {};
36612         this.fireEvent("beforetabchange", this, e, tab);
36613         if(e.cancel !== true && !tab.disabled){
36614             if(this.active){
36615                 this.active.hide();
36616             }
36617             this.active = this.items[id];
36618             this.active.show();
36619             this.fireEvent("tabchange", this, this.active);
36620         }
36621         return tab;
36622     },
36623
36624     /**
36625      * Gets the active {@link Roo.TabPanelItem}.
36626      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
36627      */
36628     getActiveTab : function(){
36629         return this.active;
36630     },
36631
36632     /**
36633      * Updates the tab body element to fit the height of the container element
36634      * for overflow scrolling
36635      * @param {Number} targetHeight (optional) Override the starting height from the elements height
36636      */
36637     syncHeight : function(targetHeight){
36638         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36639         var bm = this.bodyEl.getMargins();
36640         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
36641         this.bodyEl.setHeight(newHeight);
36642         return newHeight;
36643     },
36644
36645     onResize : function(){
36646         if(this.monitorResize){
36647             this.autoSizeTabs();
36648         }
36649     },
36650
36651     /**
36652      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
36653      */
36654     beginUpdate : function(){
36655         this.updating = true;
36656     },
36657
36658     /**
36659      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36660      */
36661     endUpdate : function(){
36662         this.updating = false;
36663         this.autoSizeTabs();
36664     },
36665
36666     /**
36667      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36668      */
36669     autoSizeTabs : function(){
36670         var count = this.items.length;
36671         var vcount = count - this.hiddenCount;
36672         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36673             return;
36674         }
36675         var w = Math.max(this.el.getWidth() - this.cpad, 10);
36676         var availWidth = Math.floor(w / vcount);
36677         var b = this.stripBody;
36678         if(b.getWidth() > w){
36679             var tabs = this.items;
36680             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36681             if(availWidth < this.minTabWidth){
36682                 /*if(!this.sleft){    // incomplete scrolling code
36683                     this.createScrollButtons();
36684                 }
36685                 this.showScroll();
36686                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36687             }
36688         }else{
36689             if(this.currentTabWidth < this.preferredTabWidth){
36690                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36691             }
36692         }
36693     },
36694
36695     /**
36696      * Returns the number of tabs in this TabPanel.
36697      * @return {Number}
36698      */
36699      getCount : function(){
36700          return this.items.length;
36701      },
36702
36703     /**
36704      * Resizes all the tabs to the passed width
36705      * @param {Number} The new width
36706      */
36707     setTabWidth : function(width){
36708         this.currentTabWidth = width;
36709         for(var i = 0, len = this.items.length; i < len; i++) {
36710                 if(!this.items[i].isHidden()) {
36711                 this.items[i].setWidth(width);
36712             }
36713         }
36714     },
36715
36716     /**
36717      * Destroys this TabPanel
36718      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36719      */
36720     destroy : function(removeEl){
36721         Roo.EventManager.removeResizeListener(this.onResize, this);
36722         for(var i = 0, len = this.items.length; i < len; i++){
36723             this.items[i].purgeListeners();
36724         }
36725         if(removeEl === true){
36726             this.el.update("");
36727             this.el.remove();
36728         }
36729     },
36730     
36731     createStrip : function(container)
36732     {
36733         var strip = document.createElement("nav");
36734         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36735         container.appendChild(strip);
36736         return strip;
36737     },
36738     
36739     createStripList : function(strip)
36740     {
36741         // div wrapper for retard IE
36742         // returns the "tr" element.
36743         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36744         //'<div class="x-tabs-strip-wrap">'+
36745           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36746           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36747         return strip.firstChild; //.firstChild.firstChild.firstChild;
36748     },
36749     createBody : function(container)
36750     {
36751         var body = document.createElement("div");
36752         Roo.id(body, "tab-body");
36753         //Roo.fly(body).addClass("x-tabs-body");
36754         Roo.fly(body).addClass("tab-content");
36755         container.appendChild(body);
36756         return body;
36757     },
36758     createItemBody :function(bodyEl, id){
36759         var body = Roo.getDom(id);
36760         if(!body){
36761             body = document.createElement("div");
36762             body.id = id;
36763         }
36764         //Roo.fly(body).addClass("x-tabs-item-body");
36765         Roo.fly(body).addClass("tab-pane");
36766          bodyEl.insertBefore(body, bodyEl.firstChild);
36767         return body;
36768     },
36769     /** @private */
36770     createStripElements :  function(stripEl, text, closable, tpl)
36771     {
36772         var td = document.createElement("li"); // was td..
36773         
36774         
36775         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36776         
36777         
36778         stripEl.appendChild(td);
36779         /*if(closable){
36780             td.className = "x-tabs-closable";
36781             if(!this.closeTpl){
36782                 this.closeTpl = new Roo.Template(
36783                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36784                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36785                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
36786                 );
36787             }
36788             var el = this.closeTpl.overwrite(td, {"text": text});
36789             var close = el.getElementsByTagName("div")[0];
36790             var inner = el.getElementsByTagName("em")[0];
36791             return {"el": el, "close": close, "inner": inner};
36792         } else {
36793         */
36794         // not sure what this is..
36795 //            if(!this.tabTpl){
36796                 //this.tabTpl = new Roo.Template(
36797                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36798                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36799                 //);
36800 //                this.tabTpl = new Roo.Template(
36801 //                   '<a href="#">' +
36802 //                   '<span unselectable="on"' +
36803 //                            (this.disableTooltips ? '' : ' title="{text}"') +
36804 //                            ' >{text}</span></a>'
36805 //                );
36806 //                
36807 //            }
36808
36809
36810             var template = tpl || this.tabTpl || false;
36811             
36812             if(!template){
36813                 
36814                 template = new Roo.Template(
36815                    '<a href="#">' +
36816                    '<span unselectable="on"' +
36817                             (this.disableTooltips ? '' : ' title="{text}"') +
36818                             ' >{text}</span></a>'
36819                 );
36820             }
36821             
36822             switch (typeof(template)) {
36823                 case 'object' :
36824                     break;
36825                 case 'string' :
36826                     template = new Roo.Template(template);
36827                     break;
36828                 default :
36829                     break;
36830             }
36831             
36832             var el = template.overwrite(td, {"text": text});
36833             
36834             var inner = el.getElementsByTagName("span")[0];
36835             
36836             return {"el": el, "inner": inner};
36837             
36838     }
36839         
36840     
36841 });
36842
36843 /**
36844  * @class Roo.TabPanelItem
36845  * @extends Roo.util.Observable
36846  * Represents an individual item (tab plus body) in a TabPanel.
36847  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36848  * @param {String} id The id of this TabPanelItem
36849  * @param {String} text The text for the tab of this TabPanelItem
36850  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36851  */
36852 Roo.bootstrap.panel.TabItem = function(config){
36853     /**
36854      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36855      * @type Roo.TabPanel
36856      */
36857     this.tabPanel = config.panel;
36858     /**
36859      * The id for this TabPanelItem
36860      * @type String
36861      */
36862     this.id = config.id;
36863     /** @private */
36864     this.disabled = false;
36865     /** @private */
36866     this.text = config.text;
36867     /** @private */
36868     this.loaded = false;
36869     this.closable = config.closable;
36870
36871     /**
36872      * The body element for this TabPanelItem.
36873      * @type Roo.Element
36874      */
36875     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36876     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36877     this.bodyEl.setStyle("display", "block");
36878     this.bodyEl.setStyle("zoom", "1");
36879     //this.hideAction();
36880
36881     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36882     /** @private */
36883     this.el = Roo.get(els.el);
36884     this.inner = Roo.get(els.inner, true);
36885     this.textEl = Roo.get(this.el.dom.firstChild, true);
36886     this.pnode = Roo.get(els.el.parentNode, true);
36887     this.el.on("mousedown", this.onTabMouseDown, this);
36888     this.el.on("click", this.onTabClick, this);
36889     /** @private */
36890     if(config.closable){
36891         var c = Roo.get(els.close, true);
36892         c.dom.title = this.closeText;
36893         c.addClassOnOver("close-over");
36894         c.on("click", this.closeClick, this);
36895      }
36896
36897     this.addEvents({
36898          /**
36899          * @event activate
36900          * Fires when this tab becomes the active tab.
36901          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36902          * @param {Roo.TabPanelItem} this
36903          */
36904         "activate": true,
36905         /**
36906          * @event beforeclose
36907          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36908          * @param {Roo.TabPanelItem} this
36909          * @param {Object} e Set cancel to true on this object to cancel the close.
36910          */
36911         "beforeclose": true,
36912         /**
36913          * @event close
36914          * Fires when this tab is closed.
36915          * @param {Roo.TabPanelItem} this
36916          */
36917          "close": true,
36918         /**
36919          * @event deactivate
36920          * Fires when this tab is no longer the active tab.
36921          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36922          * @param {Roo.TabPanelItem} this
36923          */
36924          "deactivate" : true
36925     });
36926     this.hidden = false;
36927
36928     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36929 };
36930
36931 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36932            {
36933     purgeListeners : function(){
36934        Roo.util.Observable.prototype.purgeListeners.call(this);
36935        this.el.removeAllListeners();
36936     },
36937     /**
36938      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36939      */
36940     show : function(){
36941         this.pnode.addClass("active");
36942         this.showAction();
36943         if(Roo.isOpera){
36944             this.tabPanel.stripWrap.repaint();
36945         }
36946         this.fireEvent("activate", this.tabPanel, this);
36947     },
36948
36949     /**
36950      * Returns true if this tab is the active tab.
36951      * @return {Boolean}
36952      */
36953     isActive : function(){
36954         return this.tabPanel.getActiveTab() == this;
36955     },
36956
36957     /**
36958      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36959      */
36960     hide : function(){
36961         this.pnode.removeClass("active");
36962         this.hideAction();
36963         this.fireEvent("deactivate", this.tabPanel, this);
36964     },
36965
36966     hideAction : function(){
36967         this.bodyEl.hide();
36968         this.bodyEl.setStyle("position", "absolute");
36969         this.bodyEl.setLeft("-20000px");
36970         this.bodyEl.setTop("-20000px");
36971     },
36972
36973     showAction : function(){
36974         this.bodyEl.setStyle("position", "relative");
36975         this.bodyEl.setTop("");
36976         this.bodyEl.setLeft("");
36977         this.bodyEl.show();
36978     },
36979
36980     /**
36981      * Set the tooltip for the tab.
36982      * @param {String} tooltip The tab's tooltip
36983      */
36984     setTooltip : function(text){
36985         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36986             this.textEl.dom.qtip = text;
36987             this.textEl.dom.removeAttribute('title');
36988         }else{
36989             this.textEl.dom.title = text;
36990         }
36991     },
36992
36993     onTabClick : function(e){
36994         e.preventDefault();
36995         this.tabPanel.activate(this.id);
36996     },
36997
36998     onTabMouseDown : function(e){
36999         e.preventDefault();
37000         this.tabPanel.activate(this.id);
37001     },
37002 /*
37003     getWidth : function(){
37004         return this.inner.getWidth();
37005     },
37006
37007     setWidth : function(width){
37008         var iwidth = width - this.pnode.getPadding("lr");
37009         this.inner.setWidth(iwidth);
37010         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37011         this.pnode.setWidth(width);
37012     },
37013 */
37014     /**
37015      * Show or hide the tab
37016      * @param {Boolean} hidden True to hide or false to show.
37017      */
37018     setHidden : function(hidden){
37019         this.hidden = hidden;
37020         this.pnode.setStyle("display", hidden ? "none" : "");
37021     },
37022
37023     /**
37024      * Returns true if this tab is "hidden"
37025      * @return {Boolean}
37026      */
37027     isHidden : function(){
37028         return this.hidden;
37029     },
37030
37031     /**
37032      * Returns the text for this tab
37033      * @return {String}
37034      */
37035     getText : function(){
37036         return this.text;
37037     },
37038     /*
37039     autoSize : function(){
37040         //this.el.beginMeasure();
37041         this.textEl.setWidth(1);
37042         /*
37043          *  #2804 [new] Tabs in Roojs
37044          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37045          */
37046         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37047         //this.el.endMeasure();
37048     //},
37049
37050     /**
37051      * Sets the text for the tab (Note: this also sets the tooltip text)
37052      * @param {String} text The tab's text and tooltip
37053      */
37054     setText : function(text){
37055         this.text = text;
37056         this.textEl.update(text);
37057         this.setTooltip(text);
37058         //if(!this.tabPanel.resizeTabs){
37059         //    this.autoSize();
37060         //}
37061     },
37062     /**
37063      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37064      */
37065     activate : function(){
37066         this.tabPanel.activate(this.id);
37067     },
37068
37069     /**
37070      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37071      */
37072     disable : function(){
37073         if(this.tabPanel.active != this){
37074             this.disabled = true;
37075             this.pnode.addClass("disabled");
37076         }
37077     },
37078
37079     /**
37080      * Enables this TabPanelItem if it was previously disabled.
37081      */
37082     enable : function(){
37083         this.disabled = false;
37084         this.pnode.removeClass("disabled");
37085     },
37086
37087     /**
37088      * Sets the content for this TabPanelItem.
37089      * @param {String} content The content
37090      * @param {Boolean} loadScripts true to look for and load scripts
37091      */
37092     setContent : function(content, loadScripts){
37093         this.bodyEl.update(content, loadScripts);
37094     },
37095
37096     /**
37097      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37098      * @return {Roo.UpdateManager} The UpdateManager
37099      */
37100     getUpdateManager : function(){
37101         return this.bodyEl.getUpdateManager();
37102     },
37103
37104     /**
37105      * Set a URL to be used to load the content for this TabPanelItem.
37106      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37107      * @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)
37108      * @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)
37109      * @return {Roo.UpdateManager} The UpdateManager
37110      */
37111     setUrl : function(url, params, loadOnce){
37112         if(this.refreshDelegate){
37113             this.un('activate', this.refreshDelegate);
37114         }
37115         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37116         this.on("activate", this.refreshDelegate);
37117         return this.bodyEl.getUpdateManager();
37118     },
37119
37120     /** @private */
37121     _handleRefresh : function(url, params, loadOnce){
37122         if(!loadOnce || !this.loaded){
37123             var updater = this.bodyEl.getUpdateManager();
37124             updater.update(url, params, this._setLoaded.createDelegate(this));
37125         }
37126     },
37127
37128     /**
37129      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37130      *   Will fail silently if the setUrl method has not been called.
37131      *   This does not activate the panel, just updates its content.
37132      */
37133     refresh : function(){
37134         if(this.refreshDelegate){
37135            this.loaded = false;
37136            this.refreshDelegate();
37137         }
37138     },
37139
37140     /** @private */
37141     _setLoaded : function(){
37142         this.loaded = true;
37143     },
37144
37145     /** @private */
37146     closeClick : function(e){
37147         var o = {};
37148         e.stopEvent();
37149         this.fireEvent("beforeclose", this, o);
37150         if(o.cancel !== true){
37151             this.tabPanel.removeTab(this.id);
37152         }
37153     },
37154     /**
37155      * The text displayed in the tooltip for the close icon.
37156      * @type String
37157      */
37158     closeText : "Close this tab"
37159 });