css-bootstrap/bootstrap.css
[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             this.el.mask(this.msg, this.msgCls);
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         if(this.fireEvent('prepare', this, formData) != false){
28495             this.xhr.send(formData);
28496         };
28497     }
28498 });
28499
28500 /*
28501 * Licence: LGPL
28502 */
28503
28504 /**
28505  * @class Roo.bootstrap.DocumentViewer
28506  * @extends Roo.bootstrap.Component
28507  * Bootstrap DocumentViewer class
28508  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28509  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28510  * 
28511  * @constructor
28512  * Create a new DocumentViewer
28513  * @param {Object} config The config object
28514  */
28515
28516 Roo.bootstrap.DocumentViewer = function(config){
28517     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28518     
28519     this.addEvents({
28520         /**
28521          * @event initial
28522          * Fire after initEvent
28523          * @param {Roo.bootstrap.DocumentViewer} this
28524          */
28525         "initial" : true,
28526         /**
28527          * @event click
28528          * Fire after click
28529          * @param {Roo.bootstrap.DocumentViewer} this
28530          */
28531         "click" : true,
28532         /**
28533          * @event download
28534          * Fire after download button
28535          * @param {Roo.bootstrap.DocumentViewer} this
28536          */
28537         "download" : true,
28538         /**
28539          * @event trash
28540          * Fire after trash button
28541          * @param {Roo.bootstrap.DocumentViewer} this
28542          */
28543         "trash" : true
28544         
28545     });
28546 };
28547
28548 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28549     
28550     showDownload : true,
28551     
28552     showTrash : true,
28553     
28554     getAutoCreate : function()
28555     {
28556         var cfg = {
28557             tag : 'div',
28558             cls : 'roo-document-viewer',
28559             cn : [
28560                 {
28561                     tag : 'div',
28562                     cls : 'roo-document-viewer-body',
28563                     cn : [
28564                         {
28565                             tag : 'div',
28566                             cls : 'roo-document-viewer-thumb',
28567                             cn : [
28568                                 {
28569                                     tag : 'img',
28570                                     cls : 'roo-document-viewer-image'
28571                                 }
28572                             ]
28573                         }
28574                     ]
28575                 },
28576                 {
28577                     tag : 'div',
28578                     cls : 'roo-document-viewer-footer',
28579                     cn : {
28580                         tag : 'div',
28581                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28582                         cn : [
28583                             {
28584                                 tag : 'div',
28585                                 cls : 'btn-group roo-document-viewer-download',
28586                                 cn : [
28587                                     {
28588                                         tag : 'button',
28589                                         cls : 'btn btn-default',
28590                                         html : '<i class="fa fa-download"></i>'
28591                                     }
28592                                 ]
28593                             },
28594                             {
28595                                 tag : 'div',
28596                                 cls : 'btn-group roo-document-viewer-trash',
28597                                 cn : [
28598                                     {
28599                                         tag : 'button',
28600                                         cls : 'btn btn-default',
28601                                         html : '<i class="fa fa-trash"></i>'
28602                                     }
28603                                 ]
28604                             }
28605                         ]
28606                     }
28607                 }
28608             ]
28609         };
28610         
28611         return cfg;
28612     },
28613     
28614     initEvents : function()
28615     {
28616         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28617         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28618         
28619         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28620         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28621         
28622         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28623         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28624         
28625         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28626         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28627         
28628         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28629         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28630         
28631         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28632         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28633         
28634         this.bodyEl.on('click', this.onClick, this);
28635         this.downloadBtn.on('click', this.onDownload, this);
28636         this.trashBtn.on('click', this.onTrash, this);
28637         
28638         this.downloadBtn.hide();
28639         this.trashBtn.hide();
28640         
28641         if(this.showDownload){
28642             this.downloadBtn.show();
28643         }
28644         
28645         if(this.showTrash){
28646             this.trashBtn.show();
28647         }
28648         
28649         if(!this.showDownload && !this.showTrash) {
28650             this.footerEl.hide();
28651         }
28652         
28653     },
28654     
28655     initial : function()
28656     {
28657         this.fireEvent('initial', this);
28658         
28659     },
28660     
28661     onClick : function(e)
28662     {
28663         e.preventDefault();
28664         
28665         this.fireEvent('click', this);
28666     },
28667     
28668     onDownload : function(e)
28669     {
28670         e.preventDefault();
28671         
28672         this.fireEvent('download', this);
28673     },
28674     
28675     onTrash : function(e)
28676     {
28677         e.preventDefault();
28678         
28679         this.fireEvent('trash', this);
28680     }
28681     
28682 });
28683 /*
28684  * - LGPL
28685  *
28686  * nav progress bar
28687  * 
28688  */
28689
28690 /**
28691  * @class Roo.bootstrap.NavProgressBar
28692  * @extends Roo.bootstrap.Component
28693  * Bootstrap NavProgressBar class
28694  * 
28695  * @constructor
28696  * Create a new nav progress bar
28697  * @param {Object} config The config object
28698  */
28699
28700 Roo.bootstrap.NavProgressBar = function(config){
28701     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28702
28703     this.bullets = this.bullets || [];
28704    
28705 //    Roo.bootstrap.NavProgressBar.register(this);
28706      this.addEvents({
28707         /**
28708              * @event changed
28709              * Fires when the active item changes
28710              * @param {Roo.bootstrap.NavProgressBar} this
28711              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28712              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28713          */
28714         'changed': true
28715      });
28716     
28717 };
28718
28719 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28720     
28721     bullets : [],
28722     barItems : [],
28723     
28724     getAutoCreate : function()
28725     {
28726         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28727         
28728         cfg = {
28729             tag : 'div',
28730             cls : 'roo-navigation-bar-group',
28731             cn : [
28732                 {
28733                     tag : 'div',
28734                     cls : 'roo-navigation-top-bar'
28735                 },
28736                 {
28737                     tag : 'div',
28738                     cls : 'roo-navigation-bullets-bar',
28739                     cn : [
28740                         {
28741                             tag : 'ul',
28742                             cls : 'roo-navigation-bar'
28743                         }
28744                     ]
28745                 },
28746                 
28747                 {
28748                     tag : 'div',
28749                     cls : 'roo-navigation-bottom-bar'
28750                 }
28751             ]
28752             
28753         };
28754         
28755         return cfg;
28756         
28757     },
28758     
28759     initEvents: function() 
28760     {
28761         
28762     },
28763     
28764     onRender : function(ct, position) 
28765     {
28766         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28767         
28768         if(this.bullets.length){
28769             Roo.each(this.bullets, function(b){
28770                this.addItem(b);
28771             }, this);
28772         }
28773         
28774         this.format();
28775         
28776     },
28777     
28778     addItem : function(cfg)
28779     {
28780         var item = new Roo.bootstrap.NavProgressItem(cfg);
28781         
28782         item.parentId = this.id;
28783         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28784         
28785         if(cfg.html){
28786             var top = new Roo.bootstrap.Element({
28787                 tag : 'div',
28788                 cls : 'roo-navigation-bar-text'
28789             });
28790             
28791             var bottom = new Roo.bootstrap.Element({
28792                 tag : 'div',
28793                 cls : 'roo-navigation-bar-text'
28794             });
28795             
28796             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28797             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28798             
28799             var topText = new Roo.bootstrap.Element({
28800                 tag : 'span',
28801                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28802             });
28803             
28804             var bottomText = new Roo.bootstrap.Element({
28805                 tag : 'span',
28806                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28807             });
28808             
28809             topText.onRender(top.el, null);
28810             bottomText.onRender(bottom.el, null);
28811             
28812             item.topEl = top;
28813             item.bottomEl = bottom;
28814         }
28815         
28816         this.barItems.push(item);
28817         
28818         return item;
28819     },
28820     
28821     getActive : function()
28822     {
28823         var active = false;
28824         
28825         Roo.each(this.barItems, function(v){
28826             
28827             if (!v.isActive()) {
28828                 return;
28829             }
28830             
28831             active = v;
28832             return false;
28833             
28834         });
28835         
28836         return active;
28837     },
28838     
28839     setActiveItem : function(item)
28840     {
28841         var prev = false;
28842         
28843         Roo.each(this.barItems, function(v){
28844             if (v.rid == item.rid) {
28845                 return ;
28846             }
28847             
28848             if (v.isActive()) {
28849                 v.setActive(false);
28850                 prev = v;
28851             }
28852         });
28853
28854         item.setActive(true);
28855         
28856         this.fireEvent('changed', this, item, prev);
28857     },
28858     
28859     getBarItem: function(rid)
28860     {
28861         var ret = false;
28862         
28863         Roo.each(this.barItems, function(e) {
28864             if (e.rid != rid) {
28865                 return;
28866             }
28867             
28868             ret =  e;
28869             return false;
28870         });
28871         
28872         return ret;
28873     },
28874     
28875     indexOfItem : function(item)
28876     {
28877         var index = false;
28878         
28879         Roo.each(this.barItems, function(v, i){
28880             
28881             if (v.rid != item.rid) {
28882                 return;
28883             }
28884             
28885             index = i;
28886             return false
28887         });
28888         
28889         return index;
28890     },
28891     
28892     setActiveNext : function()
28893     {
28894         var i = this.indexOfItem(this.getActive());
28895         
28896         if (i > this.barItems.length) {
28897             return;
28898         }
28899         
28900         this.setActiveItem(this.barItems[i+1]);
28901     },
28902     
28903     setActivePrev : function()
28904     {
28905         var i = this.indexOfItem(this.getActive());
28906         
28907         if (i  < 1) {
28908             return;
28909         }
28910         
28911         this.setActiveItem(this.barItems[i-1]);
28912     },
28913     
28914     format : function()
28915     {
28916         if(!this.barItems.length){
28917             return;
28918         }
28919      
28920         var width = 100 / this.barItems.length;
28921         
28922         Roo.each(this.barItems, function(i){
28923             i.el.setStyle('width', width + '%');
28924             i.topEl.el.setStyle('width', width + '%');
28925             i.bottomEl.el.setStyle('width', width + '%');
28926         }, this);
28927         
28928     }
28929     
28930 });
28931 /*
28932  * - LGPL
28933  *
28934  * Nav Progress Item
28935  * 
28936  */
28937
28938 /**
28939  * @class Roo.bootstrap.NavProgressItem
28940  * @extends Roo.bootstrap.Component
28941  * Bootstrap NavProgressItem class
28942  * @cfg {String} rid the reference id
28943  * @cfg {Boolean} active (true|false) Is item active default false
28944  * @cfg {Boolean} disabled (true|false) Is item active default false
28945  * @cfg {String} html
28946  * @cfg {String} position (top|bottom) text position default bottom
28947  * @cfg {String} icon show icon instead of number
28948  * 
28949  * @constructor
28950  * Create a new NavProgressItem
28951  * @param {Object} config The config object
28952  */
28953 Roo.bootstrap.NavProgressItem = function(config){
28954     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28955     this.addEvents({
28956         // raw events
28957         /**
28958          * @event click
28959          * The raw click event for the entire grid.
28960          * @param {Roo.bootstrap.NavProgressItem} this
28961          * @param {Roo.EventObject} e
28962          */
28963         "click" : true
28964     });
28965    
28966 };
28967
28968 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28969     
28970     rid : '',
28971     active : false,
28972     disabled : false,
28973     html : '',
28974     position : 'bottom',
28975     icon : false,
28976     
28977     getAutoCreate : function()
28978     {
28979         var iconCls = 'roo-navigation-bar-item-icon';
28980         
28981         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28982         
28983         var cfg = {
28984             tag: 'li',
28985             cls: 'roo-navigation-bar-item',
28986             cn : [
28987                 {
28988                     tag : 'i',
28989                     cls : iconCls
28990                 }
28991             ]
28992         };
28993         
28994         if(this.active){
28995             cfg.cls += ' active';
28996         }
28997         if(this.disabled){
28998             cfg.cls += ' disabled';
28999         }
29000         
29001         return cfg;
29002     },
29003     
29004     disable : function()
29005     {
29006         this.setDisabled(true);
29007     },
29008     
29009     enable : function()
29010     {
29011         this.setDisabled(false);
29012     },
29013     
29014     initEvents: function() 
29015     {
29016         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29017         
29018         this.iconEl.on('click', this.onClick, this);
29019     },
29020     
29021     onClick : function(e)
29022     {
29023         e.preventDefault();
29024         
29025         if(this.disabled){
29026             return;
29027         }
29028         
29029         if(this.fireEvent('click', this, e) === false){
29030             return;
29031         };
29032         
29033         this.parent().setActiveItem(this);
29034     },
29035     
29036     isActive: function () 
29037     {
29038         return this.active;
29039     },
29040     
29041     setActive : function(state)
29042     {
29043         if(this.active == state){
29044             return;
29045         }
29046         
29047         this.active = state;
29048         
29049         if (state) {
29050             this.el.addClass('active');
29051             return;
29052         }
29053         
29054         this.el.removeClass('active');
29055         
29056         return;
29057     },
29058     
29059     setDisabled : function(state)
29060     {
29061         if(this.disabled == state){
29062             return;
29063         }
29064         
29065         this.disabled = state;
29066         
29067         if (state) {
29068             this.el.addClass('disabled');
29069             return;
29070         }
29071         
29072         this.el.removeClass('disabled');
29073     },
29074     
29075     tooltipEl : function()
29076     {
29077         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29078     }
29079 });
29080  
29081
29082  /*
29083  * - LGPL
29084  *
29085  * FieldLabel
29086  * 
29087  */
29088
29089 /**
29090  * @class Roo.bootstrap.FieldLabel
29091  * @extends Roo.bootstrap.Component
29092  * Bootstrap FieldLabel class
29093  * @cfg {String} html contents of the element
29094  * @cfg {String} tag tag of the element default label
29095  * @cfg {String} cls class of the element
29096  * @cfg {String} target label target 
29097  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29098  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29099  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29100  * @cfg {String} iconTooltip default "This field is required"
29101  * 
29102  * @constructor
29103  * Create a new FieldLabel
29104  * @param {Object} config The config object
29105  */
29106
29107 Roo.bootstrap.FieldLabel = function(config){
29108     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29109     
29110     this.addEvents({
29111             /**
29112              * @event invalid
29113              * Fires after the field has been marked as invalid.
29114              * @param {Roo.form.FieldLabel} this
29115              * @param {String} msg The validation message
29116              */
29117             invalid : true,
29118             /**
29119              * @event valid
29120              * Fires after the field has been validated with no errors.
29121              * @param {Roo.form.FieldLabel} this
29122              */
29123             valid : true
29124         });
29125 };
29126
29127 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29128     
29129     tag: 'label',
29130     cls: '',
29131     html: '',
29132     target: '',
29133     allowBlank : true,
29134     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29135     validClass : 'text-success fa fa-lg fa-check',
29136     iconTooltip : 'This field is required',
29137     
29138     getAutoCreate : function(){
29139         
29140         var cfg = {
29141             tag : this.tag,
29142             cls : 'roo-bootstrap-field-label ' + this.cls,
29143             for : this.target,
29144             cn : [
29145                 {
29146                     tag : 'i',
29147                     cls : '',
29148                     tooltip : this.iconTooltip
29149                 },
29150                 {
29151                     tag : 'span',
29152                     html : this.html
29153                 }
29154             ] 
29155         };
29156         
29157         return cfg;
29158     },
29159     
29160     initEvents: function() 
29161     {
29162         Roo.bootstrap.Element.superclass.initEvents.call(this);
29163         
29164         this.iconEl = this.el.select('i', true).first();
29165         
29166         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29167         
29168         Roo.bootstrap.FieldLabel.register(this);
29169     },
29170     
29171     /**
29172      * Mark this field as valid
29173      */
29174     markValid : function()
29175     {
29176         this.iconEl.show();
29177         
29178         this.iconEl.removeClass(this.invalidClass);
29179         
29180         this.iconEl.addClass(this.validClass);
29181         
29182         this.fireEvent('valid', this);
29183     },
29184     
29185     /**
29186      * Mark this field as invalid
29187      * @param {String} msg The validation message
29188      */
29189     markInvalid : function(msg)
29190     {
29191         this.iconEl.show();
29192         
29193         this.iconEl.removeClass(this.validClass);
29194         
29195         this.iconEl.addClass(this.invalidClass);
29196         
29197         this.fireEvent('invalid', this, msg);
29198     }
29199     
29200    
29201 });
29202
29203 Roo.apply(Roo.bootstrap.FieldLabel, {
29204     
29205     groups: {},
29206     
29207      /**
29208     * register a FieldLabel Group
29209     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29210     */
29211     register : function(label)
29212     {
29213         if(this.groups.hasOwnProperty(label.target)){
29214             return;
29215         }
29216      
29217         this.groups[label.target] = label;
29218         
29219     },
29220     /**
29221     * fetch a FieldLabel Group based on the target
29222     * @param {string} target
29223     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29224     */
29225     get: function(target) {
29226         if (typeof(this.groups[target]) == 'undefined') {
29227             return false;
29228         }
29229         
29230         return this.groups[target] ;
29231     }
29232 });
29233
29234  
29235
29236  /*
29237  * - LGPL
29238  *
29239  * page DateSplitField.
29240  * 
29241  */
29242
29243
29244 /**
29245  * @class Roo.bootstrap.DateSplitField
29246  * @extends Roo.bootstrap.Component
29247  * Bootstrap DateSplitField class
29248  * @cfg {string} fieldLabel - the label associated
29249  * @cfg {Number} labelWidth set the width of label (0-12)
29250  * @cfg {String} labelAlign (top|left)
29251  * @cfg {Boolean} dayAllowBlank (true|false) default false
29252  * @cfg {Boolean} monthAllowBlank (true|false) default false
29253  * @cfg {Boolean} yearAllowBlank (true|false) default false
29254  * @cfg {string} dayPlaceholder 
29255  * @cfg {string} monthPlaceholder
29256  * @cfg {string} yearPlaceholder
29257  * @cfg {string} dayFormat default 'd'
29258  * @cfg {string} monthFormat default 'm'
29259  * @cfg {string} yearFormat default 'Y'
29260  * @cfg {Number} labellg set the width of label (1-12)
29261  * @cfg {Number} labelmd set the width of label (1-12)
29262  * @cfg {Number} labelsm set the width of label (1-12)
29263  * @cfg {Number} labelxs set the width of label (1-12)
29264
29265  *     
29266  * @constructor
29267  * Create a new DateSplitField
29268  * @param {Object} config The config object
29269  */
29270
29271 Roo.bootstrap.DateSplitField = function(config){
29272     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29273     
29274     this.addEvents({
29275         // raw events
29276          /**
29277          * @event years
29278          * getting the data of years
29279          * @param {Roo.bootstrap.DateSplitField} this
29280          * @param {Object} years
29281          */
29282         "years" : true,
29283         /**
29284          * @event days
29285          * getting the data of days
29286          * @param {Roo.bootstrap.DateSplitField} this
29287          * @param {Object} days
29288          */
29289         "days" : true,
29290         /**
29291          * @event invalid
29292          * Fires after the field has been marked as invalid.
29293          * @param {Roo.form.Field} this
29294          * @param {String} msg The validation message
29295          */
29296         invalid : true,
29297        /**
29298          * @event valid
29299          * Fires after the field has been validated with no errors.
29300          * @param {Roo.form.Field} this
29301          */
29302         valid : true
29303     });
29304 };
29305
29306 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29307     
29308     fieldLabel : '',
29309     labelAlign : 'top',
29310     labelWidth : 3,
29311     dayAllowBlank : false,
29312     monthAllowBlank : false,
29313     yearAllowBlank : false,
29314     dayPlaceholder : '',
29315     monthPlaceholder : '',
29316     yearPlaceholder : '',
29317     dayFormat : 'd',
29318     monthFormat : 'm',
29319     yearFormat : 'Y',
29320     isFormField : true,
29321     labellg : 0,
29322     labelmd : 0,
29323     labelsm : 0,
29324     labelxs : 0,
29325     
29326     getAutoCreate : function()
29327     {
29328         var cfg = {
29329             tag : 'div',
29330             cls : 'row roo-date-split-field-group',
29331             cn : [
29332                 {
29333                     tag : 'input',
29334                     type : 'hidden',
29335                     cls : 'form-hidden-field roo-date-split-field-group-value',
29336                     name : this.name
29337                 }
29338             ]
29339         };
29340         
29341         var labelCls = 'col-md-12';
29342         var contentCls = 'col-md-4';
29343         
29344         if(this.fieldLabel){
29345             
29346             var label = {
29347                 tag : 'div',
29348                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29349                 cn : [
29350                     {
29351                         tag : 'label',
29352                         html : this.fieldLabel
29353                     }
29354                 ]
29355             };
29356             
29357             if(this.labelAlign == 'left'){
29358             
29359                 if(this.labelWidth > 12){
29360                     label.style = "width: " + this.labelWidth + 'px';
29361                 }
29362
29363                 if(this.labelWidth < 13 && this.labelmd == 0){
29364                     this.labelmd = this.labelWidth;
29365                 }
29366
29367                 if(this.labellg > 0){
29368                     labelCls = ' col-lg-' + this.labellg;
29369                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29370                 }
29371
29372                 if(this.labelmd > 0){
29373                     labelCls = ' col-md-' + this.labelmd;
29374                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29375                 }
29376
29377                 if(this.labelsm > 0){
29378                     labelCls = ' col-sm-' + this.labelsm;
29379                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29380                 }
29381
29382                 if(this.labelxs > 0){
29383                     labelCls = ' col-xs-' + this.labelxs;
29384                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29385                 }
29386             }
29387             
29388             label.cls += ' ' + labelCls;
29389             
29390             cfg.cn.push(label);
29391         }
29392         
29393         Roo.each(['day', 'month', 'year'], function(t){
29394             cfg.cn.push({
29395                 tag : 'div',
29396                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29397             });
29398         }, this);
29399         
29400         return cfg;
29401     },
29402     
29403     inputEl: function ()
29404     {
29405         return this.el.select('.roo-date-split-field-group-value', true).first();
29406     },
29407     
29408     onRender : function(ct, position) 
29409     {
29410         var _this = this;
29411         
29412         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29413         
29414         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29415         
29416         this.dayField = new Roo.bootstrap.ComboBox({
29417             allowBlank : this.dayAllowBlank,
29418             alwaysQuery : true,
29419             displayField : 'value',
29420             editable : false,
29421             fieldLabel : '',
29422             forceSelection : true,
29423             mode : 'local',
29424             placeholder : this.dayPlaceholder,
29425             selectOnFocus : true,
29426             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29427             triggerAction : 'all',
29428             typeAhead : true,
29429             valueField : 'value',
29430             store : new Roo.data.SimpleStore({
29431                 data : (function() {    
29432                     var days = [];
29433                     _this.fireEvent('days', _this, days);
29434                     return days;
29435                 })(),
29436                 fields : [ 'value' ]
29437             }),
29438             listeners : {
29439                 select : function (_self, record, index)
29440                 {
29441                     _this.setValue(_this.getValue());
29442                 }
29443             }
29444         });
29445
29446         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29447         
29448         this.monthField = new Roo.bootstrap.MonthField({
29449             after : '<i class=\"fa fa-calendar\"></i>',
29450             allowBlank : this.monthAllowBlank,
29451             placeholder : this.monthPlaceholder,
29452             readOnly : true,
29453             listeners : {
29454                 render : function (_self)
29455                 {
29456                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29457                         e.preventDefault();
29458                         _self.focus();
29459                     });
29460                 },
29461                 select : function (_self, oldvalue, newvalue)
29462                 {
29463                     _this.setValue(_this.getValue());
29464                 }
29465             }
29466         });
29467         
29468         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29469         
29470         this.yearField = new Roo.bootstrap.ComboBox({
29471             allowBlank : this.yearAllowBlank,
29472             alwaysQuery : true,
29473             displayField : 'value',
29474             editable : false,
29475             fieldLabel : '',
29476             forceSelection : true,
29477             mode : 'local',
29478             placeholder : this.yearPlaceholder,
29479             selectOnFocus : true,
29480             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29481             triggerAction : 'all',
29482             typeAhead : true,
29483             valueField : 'value',
29484             store : new Roo.data.SimpleStore({
29485                 data : (function() {
29486                     var years = [];
29487                     _this.fireEvent('years', _this, years);
29488                     return years;
29489                 })(),
29490                 fields : [ 'value' ]
29491             }),
29492             listeners : {
29493                 select : function (_self, record, index)
29494                 {
29495                     _this.setValue(_this.getValue());
29496                 }
29497             }
29498         });
29499
29500         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29501     },
29502     
29503     setValue : function(v, format)
29504     {
29505         this.inputEl.dom.value = v;
29506         
29507         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29508         
29509         var d = Date.parseDate(v, f);
29510         
29511         if(!d){
29512             this.validate();
29513             return;
29514         }
29515         
29516         this.setDay(d.format(this.dayFormat));
29517         this.setMonth(d.format(this.monthFormat));
29518         this.setYear(d.format(this.yearFormat));
29519         
29520         this.validate();
29521         
29522         return;
29523     },
29524     
29525     setDay : function(v)
29526     {
29527         this.dayField.setValue(v);
29528         this.inputEl.dom.value = this.getValue();
29529         this.validate();
29530         return;
29531     },
29532     
29533     setMonth : function(v)
29534     {
29535         this.monthField.setValue(v, true);
29536         this.inputEl.dom.value = this.getValue();
29537         this.validate();
29538         return;
29539     },
29540     
29541     setYear : function(v)
29542     {
29543         this.yearField.setValue(v);
29544         this.inputEl.dom.value = this.getValue();
29545         this.validate();
29546         return;
29547     },
29548     
29549     getDay : function()
29550     {
29551         return this.dayField.getValue();
29552     },
29553     
29554     getMonth : function()
29555     {
29556         return this.monthField.getValue();
29557     },
29558     
29559     getYear : function()
29560     {
29561         return this.yearField.getValue();
29562     },
29563     
29564     getValue : function()
29565     {
29566         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29567         
29568         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29569         
29570         return date;
29571     },
29572     
29573     reset : function()
29574     {
29575         this.setDay('');
29576         this.setMonth('');
29577         this.setYear('');
29578         this.inputEl.dom.value = '';
29579         this.validate();
29580         return;
29581     },
29582     
29583     validate : function()
29584     {
29585         var d = this.dayField.validate();
29586         var m = this.monthField.validate();
29587         var y = this.yearField.validate();
29588         
29589         var valid = true;
29590         
29591         if(
29592                 (!this.dayAllowBlank && !d) ||
29593                 (!this.monthAllowBlank && !m) ||
29594                 (!this.yearAllowBlank && !y)
29595         ){
29596             valid = false;
29597         }
29598         
29599         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29600             return valid;
29601         }
29602         
29603         if(valid){
29604             this.markValid();
29605             return valid;
29606         }
29607         
29608         this.markInvalid();
29609         
29610         return valid;
29611     },
29612     
29613     markValid : function()
29614     {
29615         
29616         var label = this.el.select('label', true).first();
29617         var icon = this.el.select('i.fa-star', true).first();
29618
29619         if(label && icon){
29620             icon.remove();
29621         }
29622         
29623         this.fireEvent('valid', this);
29624     },
29625     
29626      /**
29627      * Mark this field as invalid
29628      * @param {String} msg The validation message
29629      */
29630     markInvalid : function(msg)
29631     {
29632         
29633         var label = this.el.select('label', true).first();
29634         var icon = this.el.select('i.fa-star', true).first();
29635
29636         if(label && !icon){
29637             this.el.select('.roo-date-split-field-label', true).createChild({
29638                 tag : 'i',
29639                 cls : 'text-danger fa fa-lg fa-star',
29640                 tooltip : 'This field is required',
29641                 style : 'margin-right:5px;'
29642             }, label, true);
29643         }
29644         
29645         this.fireEvent('invalid', this, msg);
29646     },
29647     
29648     clearInvalid : function()
29649     {
29650         var label = this.el.select('label', true).first();
29651         var icon = this.el.select('i.fa-star', true).first();
29652
29653         if(label && icon){
29654             icon.remove();
29655         }
29656         
29657         this.fireEvent('valid', this);
29658     },
29659     
29660     getName: function()
29661     {
29662         return this.name;
29663     }
29664     
29665 });
29666
29667  /**
29668  *
29669  * This is based on 
29670  * http://masonry.desandro.com
29671  *
29672  * The idea is to render all the bricks based on vertical width...
29673  *
29674  * The original code extends 'outlayer' - we might need to use that....
29675  * 
29676  */
29677
29678
29679 /**
29680  * @class Roo.bootstrap.LayoutMasonry
29681  * @extends Roo.bootstrap.Component
29682  * Bootstrap Layout Masonry class
29683  * 
29684  * @constructor
29685  * Create a new Element
29686  * @param {Object} config The config object
29687  */
29688
29689 Roo.bootstrap.LayoutMasonry = function(config){
29690     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29691     
29692     this.bricks = [];
29693     
29694 };
29695
29696 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29697     
29698     /**
29699      * @cfg {Boolean} isLayoutInstant = no animation?
29700      */   
29701     isLayoutInstant : false, // needed?
29702    
29703     /**
29704      * @cfg {Number} boxWidth  width of the columns
29705      */   
29706     boxWidth : 450,
29707     
29708       /**
29709      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29710      */   
29711     boxHeight : 0,
29712     
29713     /**
29714      * @cfg {Number} padWidth padding below box..
29715      */   
29716     padWidth : 10, 
29717     
29718     /**
29719      * @cfg {Number} gutter gutter width..
29720      */   
29721     gutter : 10,
29722     
29723      /**
29724      * @cfg {Number} maxCols maximum number of columns
29725      */   
29726     
29727     maxCols: 0,
29728     
29729     /**
29730      * @cfg {Boolean} isAutoInitial defalut true
29731      */   
29732     isAutoInitial : true, 
29733     
29734     containerWidth: 0,
29735     
29736     /**
29737      * @cfg {Boolean} isHorizontal defalut false
29738      */   
29739     isHorizontal : false, 
29740
29741     currentSize : null,
29742     
29743     tag: 'div',
29744     
29745     cls: '',
29746     
29747     bricks: null, //CompositeElement
29748     
29749     cols : 1,
29750     
29751     _isLayoutInited : false,
29752     
29753 //    isAlternative : false, // only use for vertical layout...
29754     
29755     /**
29756      * @cfg {Number} alternativePadWidth padding below box..
29757      */   
29758     alternativePadWidth : 50, 
29759     
29760     getAutoCreate : function(){
29761         
29762         var cfg = {
29763             tag: this.tag,
29764             cls: 'blog-masonary-wrapper ' + this.cls,
29765             cn : {
29766                 cls : 'mas-boxes masonary'
29767             }
29768         };
29769         
29770         return cfg;
29771     },
29772     
29773     getChildContainer: function( )
29774     {
29775         if (this.boxesEl) {
29776             return this.boxesEl;
29777         }
29778         
29779         this.boxesEl = this.el.select('.mas-boxes').first();
29780         
29781         return this.boxesEl;
29782     },
29783     
29784     
29785     initEvents : function()
29786     {
29787         var _this = this;
29788         
29789         if(this.isAutoInitial){
29790             Roo.log('hook children rendered');
29791             this.on('childrenrendered', function() {
29792                 Roo.log('children rendered');
29793                 _this.initial();
29794             } ,this);
29795         }
29796     },
29797     
29798     initial : function()
29799     {
29800         this.currentSize = this.el.getBox(true);
29801         
29802         Roo.EventManager.onWindowResize(this.resize, this); 
29803
29804         if(!this.isAutoInitial){
29805             this.layout();
29806             return;
29807         }
29808         
29809         this.layout();
29810         
29811         return;
29812         //this.layout.defer(500,this);
29813         
29814     },
29815     
29816     resize : function()
29817     {
29818         Roo.log('resize');
29819         
29820         var cs = this.el.getBox(true);
29821         
29822         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29823             Roo.log("no change in with or X");
29824             return;
29825         }
29826         
29827         this.currentSize = cs;
29828         
29829         this.layout();
29830         
29831     },
29832     
29833     layout : function()
29834     {   
29835         this._resetLayout();
29836         
29837         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29838         
29839         this.layoutItems( isInstant );
29840       
29841         this._isLayoutInited = true;
29842         
29843     },
29844     
29845     _resetLayout : function()
29846     {
29847         if(this.isHorizontal){
29848             this.horizontalMeasureColumns();
29849             return;
29850         }
29851         
29852         this.verticalMeasureColumns();
29853         
29854     },
29855     
29856     verticalMeasureColumns : function()
29857     {
29858         this.getContainerWidth();
29859         
29860 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29861 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29862 //            return;
29863 //        }
29864         
29865         var boxWidth = this.boxWidth + this.padWidth;
29866         
29867         if(this.containerWidth < this.boxWidth){
29868             boxWidth = this.containerWidth
29869         }
29870         
29871         var containerWidth = this.containerWidth;
29872         
29873         var cols = Math.floor(containerWidth / boxWidth);
29874         
29875         this.cols = Math.max( cols, 1 );
29876         
29877         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29878         
29879         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29880         
29881         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29882         
29883         this.colWidth = boxWidth + avail - this.padWidth;
29884         
29885         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29886         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29887     },
29888     
29889     horizontalMeasureColumns : function()
29890     {
29891         this.getContainerWidth();
29892         
29893         var boxWidth = this.boxWidth;
29894         
29895         if(this.containerWidth < boxWidth){
29896             boxWidth = this.containerWidth;
29897         }
29898         
29899         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29900         
29901         this.el.setHeight(boxWidth);
29902         
29903     },
29904     
29905     getContainerWidth : function()
29906     {
29907         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29908     },
29909     
29910     layoutItems : function( isInstant )
29911     {
29912         var items = Roo.apply([], this.bricks);
29913         
29914         if(this.isHorizontal){
29915             this._horizontalLayoutItems( items , isInstant );
29916             return;
29917         }
29918         
29919 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29920 //            this._verticalAlternativeLayoutItems( items , isInstant );
29921 //            return;
29922 //        }
29923         
29924         this._verticalLayoutItems( items , isInstant );
29925         
29926     },
29927     
29928     _verticalLayoutItems : function ( items , isInstant)
29929     {
29930         if ( !items || !items.length ) {
29931             return;
29932         }
29933         
29934         var standard = [
29935             ['xs', 'xs', 'xs', 'tall'],
29936             ['xs', 'xs', 'tall'],
29937             ['xs', 'xs', 'sm'],
29938             ['xs', 'xs', 'xs'],
29939             ['xs', 'tall'],
29940             ['xs', 'sm'],
29941             ['xs', 'xs'],
29942             ['xs'],
29943             
29944             ['sm', 'xs', 'xs'],
29945             ['sm', 'xs'],
29946             ['sm'],
29947             
29948             ['tall', 'xs', 'xs', 'xs'],
29949             ['tall', 'xs', 'xs'],
29950             ['tall', 'xs'],
29951             ['tall']
29952             
29953         ];
29954         
29955         var queue = [];
29956         
29957         var boxes = [];
29958         
29959         var box = [];
29960         
29961         Roo.each(items, function(item, k){
29962             
29963             switch (item.size) {
29964                 // these layouts take up a full box,
29965                 case 'md' :
29966                 case 'md-left' :
29967                 case 'md-right' :
29968                 case 'wide' :
29969                     
29970                     if(box.length){
29971                         boxes.push(box);
29972                         box = [];
29973                     }
29974                     
29975                     boxes.push([item]);
29976                     
29977                     break;
29978                     
29979                 case 'xs' :
29980                 case 'sm' :
29981                 case 'tall' :
29982                     
29983                     box.push(item);
29984                     
29985                     break;
29986                 default :
29987                     break;
29988                     
29989             }
29990             
29991         }, this);
29992         
29993         if(box.length){
29994             boxes.push(box);
29995             box = [];
29996         }
29997         
29998         var filterPattern = function(box, length)
29999         {
30000             if(!box.length){
30001                 return;
30002             }
30003             
30004             var match = false;
30005             
30006             var pattern = box.slice(0, length);
30007             
30008             var format = [];
30009             
30010             Roo.each(pattern, function(i){
30011                 format.push(i.size);
30012             }, this);
30013             
30014             Roo.each(standard, function(s){
30015                 
30016                 if(String(s) != String(format)){
30017                     return;
30018                 }
30019                 
30020                 match = true;
30021                 return false;
30022                 
30023             }, this);
30024             
30025             if(!match && length == 1){
30026                 return;
30027             }
30028             
30029             if(!match){
30030                 filterPattern(box, length - 1);
30031                 return;
30032             }
30033                 
30034             queue.push(pattern);
30035
30036             box = box.slice(length, box.length);
30037
30038             filterPattern(box, 4);
30039
30040             return;
30041             
30042         }
30043         
30044         Roo.each(boxes, function(box, k){
30045             
30046             if(!box.length){
30047                 return;
30048             }
30049             
30050             if(box.length == 1){
30051                 queue.push(box);
30052                 return;
30053             }
30054             
30055             filterPattern(box, 4);
30056             
30057         }, this);
30058         
30059         this._processVerticalLayoutQueue( queue, isInstant );
30060         
30061     },
30062     
30063 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30064 //    {
30065 //        if ( !items || !items.length ) {
30066 //            return;
30067 //        }
30068 //
30069 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30070 //        
30071 //    },
30072     
30073     _horizontalLayoutItems : function ( items , isInstant)
30074     {
30075         if ( !items || !items.length || items.length < 3) {
30076             return;
30077         }
30078         
30079         items.reverse();
30080         
30081         var eItems = items.slice(0, 3);
30082         
30083         items = items.slice(3, items.length);
30084         
30085         var standard = [
30086             ['xs', 'xs', 'xs', 'wide'],
30087             ['xs', 'xs', 'wide'],
30088             ['xs', 'xs', 'sm'],
30089             ['xs', 'xs', 'xs'],
30090             ['xs', 'wide'],
30091             ['xs', 'sm'],
30092             ['xs', 'xs'],
30093             ['xs'],
30094             
30095             ['sm', 'xs', 'xs'],
30096             ['sm', 'xs'],
30097             ['sm'],
30098             
30099             ['wide', 'xs', 'xs', 'xs'],
30100             ['wide', 'xs', 'xs'],
30101             ['wide', 'xs'],
30102             ['wide'],
30103             
30104             ['wide-thin']
30105         ];
30106         
30107         var queue = [];
30108         
30109         var boxes = [];
30110         
30111         var box = [];
30112         
30113         Roo.each(items, function(item, k){
30114             
30115             switch (item.size) {
30116                 case 'md' :
30117                 case 'md-left' :
30118                 case 'md-right' :
30119                 case 'tall' :
30120                     
30121                     if(box.length){
30122                         boxes.push(box);
30123                         box = [];
30124                     }
30125                     
30126                     boxes.push([item]);
30127                     
30128                     break;
30129                     
30130                 case 'xs' :
30131                 case 'sm' :
30132                 case 'wide' :
30133                 case 'wide-thin' :
30134                     
30135                     box.push(item);
30136                     
30137                     break;
30138                 default :
30139                     break;
30140                     
30141             }
30142             
30143         }, this);
30144         
30145         if(box.length){
30146             boxes.push(box);
30147             box = [];
30148         }
30149         
30150         var filterPattern = function(box, length)
30151         {
30152             if(!box.length){
30153                 return;
30154             }
30155             
30156             var match = false;
30157             
30158             var pattern = box.slice(0, length);
30159             
30160             var format = [];
30161             
30162             Roo.each(pattern, function(i){
30163                 format.push(i.size);
30164             }, this);
30165             
30166             Roo.each(standard, function(s){
30167                 
30168                 if(String(s) != String(format)){
30169                     return;
30170                 }
30171                 
30172                 match = true;
30173                 return false;
30174                 
30175             }, this);
30176             
30177             if(!match && length == 1){
30178                 return;
30179             }
30180             
30181             if(!match){
30182                 filterPattern(box, length - 1);
30183                 return;
30184             }
30185                 
30186             queue.push(pattern);
30187
30188             box = box.slice(length, box.length);
30189
30190             filterPattern(box, 4);
30191
30192             return;
30193             
30194         }
30195         
30196         Roo.each(boxes, function(box, k){
30197             
30198             if(!box.length){
30199                 return;
30200             }
30201             
30202             if(box.length == 1){
30203                 queue.push(box);
30204                 return;
30205             }
30206             
30207             filterPattern(box, 4);
30208             
30209         }, this);
30210         
30211         
30212         var prune = [];
30213         
30214         var pos = this.el.getBox(true);
30215         
30216         var minX = pos.x;
30217         
30218         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30219         
30220         var hit_end = false;
30221         
30222         Roo.each(queue, function(box){
30223             
30224             if(hit_end){
30225                 
30226                 Roo.each(box, function(b){
30227                 
30228                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30229                     b.el.hide();
30230
30231                 }, this);
30232
30233                 return;
30234             }
30235             
30236             var mx = 0;
30237             
30238             Roo.each(box, function(b){
30239                 
30240                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30241                 b.el.show();
30242
30243                 mx = Math.max(mx, b.x);
30244                 
30245             }, this);
30246             
30247             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30248             
30249             if(maxX < minX){
30250                 
30251                 Roo.each(box, function(b){
30252                 
30253                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30254                     b.el.hide();
30255                     
30256                 }, this);
30257                 
30258                 hit_end = true;
30259                 
30260                 return;
30261             }
30262             
30263             prune.push(box);
30264             
30265         }, this);
30266         
30267         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30268     },
30269     
30270     /** Sets position of item in DOM
30271     * @param {Element} item
30272     * @param {Number} x - horizontal position
30273     * @param {Number} y - vertical position
30274     * @param {Boolean} isInstant - disables transitions
30275     */
30276     _processVerticalLayoutQueue : function( queue, isInstant )
30277     {
30278         var pos = this.el.getBox(true);
30279         var x = pos.x;
30280         var y = pos.y;
30281         var maxY = [];
30282         
30283         for (var i = 0; i < this.cols; i++){
30284             maxY[i] = pos.y;
30285         }
30286         
30287         Roo.each(queue, function(box, k){
30288             
30289             var col = k % this.cols;
30290             
30291             Roo.each(box, function(b,kk){
30292                 
30293                 b.el.position('absolute');
30294                 
30295                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30296                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30297                 
30298                 if(b.size == 'md-left' || b.size == 'md-right'){
30299                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30300                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30301                 }
30302                 
30303                 b.el.setWidth(width);
30304                 b.el.setHeight(height);
30305                 // iframe?
30306                 b.el.select('iframe',true).setSize(width,height);
30307                 
30308             }, this);
30309             
30310             for (var i = 0; i < this.cols; i++){
30311                 
30312                 if(maxY[i] < maxY[col]){
30313                     col = i;
30314                     continue;
30315                 }
30316                 
30317                 col = Math.min(col, i);
30318                 
30319             }
30320             
30321             x = pos.x + col * (this.colWidth + this.padWidth);
30322             
30323             y = maxY[col];
30324             
30325             var positions = [];
30326             
30327             switch (box.length){
30328                 case 1 :
30329                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30330                     break;
30331                 case 2 :
30332                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30333                     break;
30334                 case 3 :
30335                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30336                     break;
30337                 case 4 :
30338                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30339                     break;
30340                 default :
30341                     break;
30342             }
30343             
30344             Roo.each(box, function(b,kk){
30345                 
30346                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30347                 
30348                 var sz = b.el.getSize();
30349                 
30350                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30351                 
30352             }, this);
30353             
30354         }, this);
30355         
30356         var mY = 0;
30357         
30358         for (var i = 0; i < this.cols; i++){
30359             mY = Math.max(mY, maxY[i]);
30360         }
30361         
30362         this.el.setHeight(mY - pos.y);
30363         
30364     },
30365     
30366 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30367 //    {
30368 //        var pos = this.el.getBox(true);
30369 //        var x = pos.x;
30370 //        var y = pos.y;
30371 //        var maxX = pos.right;
30372 //        
30373 //        var maxHeight = 0;
30374 //        
30375 //        Roo.each(items, function(item, k){
30376 //            
30377 //            var c = k % 2;
30378 //            
30379 //            item.el.position('absolute');
30380 //                
30381 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30382 //
30383 //            item.el.setWidth(width);
30384 //
30385 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30386 //
30387 //            item.el.setHeight(height);
30388 //            
30389 //            if(c == 0){
30390 //                item.el.setXY([x, y], isInstant ? false : true);
30391 //            } else {
30392 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30393 //            }
30394 //            
30395 //            y = y + height + this.alternativePadWidth;
30396 //            
30397 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30398 //            
30399 //        }, this);
30400 //        
30401 //        this.el.setHeight(maxHeight);
30402 //        
30403 //    },
30404     
30405     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30406     {
30407         var pos = this.el.getBox(true);
30408         
30409         var minX = pos.x;
30410         var minY = pos.y;
30411         
30412         var maxX = pos.right;
30413         
30414         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30415         
30416         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30417         
30418         Roo.each(queue, function(box, k){
30419             
30420             Roo.each(box, function(b, kk){
30421                 
30422                 b.el.position('absolute');
30423                 
30424                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30425                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30426                 
30427                 if(b.size == 'md-left' || b.size == 'md-right'){
30428                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30429                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30430                 }
30431                 
30432                 b.el.setWidth(width);
30433                 b.el.setHeight(height);
30434                 
30435             }, this);
30436             
30437             if(!box.length){
30438                 return;
30439             }
30440             
30441             var positions = [];
30442             
30443             switch (box.length){
30444                 case 1 :
30445                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30446                     break;
30447                 case 2 :
30448                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30449                     break;
30450                 case 3 :
30451                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30452                     break;
30453                 case 4 :
30454                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30455                     break;
30456                 default :
30457                     break;
30458             }
30459             
30460             Roo.each(box, function(b,kk){
30461                 
30462                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30463                 
30464                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30465                 
30466             }, this);
30467             
30468         }, this);
30469         
30470     },
30471     
30472     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30473     {
30474         Roo.each(eItems, function(b,k){
30475             
30476             b.size = (k == 0) ? 'sm' : 'xs';
30477             b.x = (k == 0) ? 2 : 1;
30478             b.y = (k == 0) ? 2 : 1;
30479             
30480             b.el.position('absolute');
30481             
30482             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30483                 
30484             b.el.setWidth(width);
30485             
30486             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30487             
30488             b.el.setHeight(height);
30489             
30490         }, this);
30491
30492         var positions = [];
30493         
30494         positions.push({
30495             x : maxX - this.unitWidth * 2 - this.gutter,
30496             y : minY
30497         });
30498         
30499         positions.push({
30500             x : maxX - this.unitWidth,
30501             y : minY + (this.unitWidth + this.gutter) * 2
30502         });
30503         
30504         positions.push({
30505             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30506             y : minY
30507         });
30508         
30509         Roo.each(eItems, function(b,k){
30510             
30511             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30512
30513         }, this);
30514         
30515     },
30516     
30517     getVerticalOneBoxColPositions : function(x, y, box)
30518     {
30519         var pos = [];
30520         
30521         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30522         
30523         if(box[0].size == 'md-left'){
30524             rand = 0;
30525         }
30526         
30527         if(box[0].size == 'md-right'){
30528             rand = 1;
30529         }
30530         
30531         pos.push({
30532             x : x + (this.unitWidth + this.gutter) * rand,
30533             y : y
30534         });
30535         
30536         return pos;
30537     },
30538     
30539     getVerticalTwoBoxColPositions : function(x, y, box)
30540     {
30541         var pos = [];
30542         
30543         if(box[0].size == 'xs'){
30544             
30545             pos.push({
30546                 x : x,
30547                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30548             });
30549
30550             pos.push({
30551                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30552                 y : y
30553             });
30554             
30555             return pos;
30556             
30557         }
30558         
30559         pos.push({
30560             x : x,
30561             y : y
30562         });
30563
30564         pos.push({
30565             x : x + (this.unitWidth + this.gutter) * 2,
30566             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30567         });
30568         
30569         return pos;
30570         
30571     },
30572     
30573     getVerticalThreeBoxColPositions : function(x, y, box)
30574     {
30575         var pos = [];
30576         
30577         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30578             
30579             pos.push({
30580                 x : x,
30581                 y : y
30582             });
30583
30584             pos.push({
30585                 x : x + (this.unitWidth + this.gutter) * 1,
30586                 y : y
30587             });
30588             
30589             pos.push({
30590                 x : x + (this.unitWidth + this.gutter) * 2,
30591                 y : y
30592             });
30593             
30594             return pos;
30595             
30596         }
30597         
30598         if(box[0].size == 'xs' && box[1].size == 'xs'){
30599             
30600             pos.push({
30601                 x : x,
30602                 y : y
30603             });
30604
30605             pos.push({
30606                 x : x,
30607                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30608             });
30609             
30610             pos.push({
30611                 x : x + (this.unitWidth + this.gutter) * 1,
30612                 y : y
30613             });
30614             
30615             return pos;
30616             
30617         }
30618         
30619         pos.push({
30620             x : x,
30621             y : y
30622         });
30623
30624         pos.push({
30625             x : x + (this.unitWidth + this.gutter) * 2,
30626             y : y
30627         });
30628
30629         pos.push({
30630             x : x + (this.unitWidth + this.gutter) * 2,
30631             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30632         });
30633             
30634         return pos;
30635         
30636     },
30637     
30638     getVerticalFourBoxColPositions : function(x, y, box)
30639     {
30640         var pos = [];
30641         
30642         if(box[0].size == 'xs'){
30643             
30644             pos.push({
30645                 x : x,
30646                 y : y
30647             });
30648
30649             pos.push({
30650                 x : x,
30651                 y : y + (this.unitHeight + this.gutter) * 1
30652             });
30653             
30654             pos.push({
30655                 x : x,
30656                 y : y + (this.unitHeight + this.gutter) * 2
30657             });
30658             
30659             pos.push({
30660                 x : x + (this.unitWidth + this.gutter) * 1,
30661                 y : y
30662             });
30663             
30664             return pos;
30665             
30666         }
30667         
30668         pos.push({
30669             x : x,
30670             y : y
30671         });
30672
30673         pos.push({
30674             x : x + (this.unitWidth + this.gutter) * 2,
30675             y : y
30676         });
30677
30678         pos.push({
30679             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30680             y : y + (this.unitHeight + this.gutter) * 1
30681         });
30682
30683         pos.push({
30684             x : x + (this.unitWidth + this.gutter) * 2,
30685             y : y + (this.unitWidth + this.gutter) * 2
30686         });
30687
30688         return pos;
30689         
30690     },
30691     
30692     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30693     {
30694         var pos = [];
30695         
30696         if(box[0].size == 'md-left'){
30697             pos.push({
30698                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30699                 y : minY
30700             });
30701             
30702             return pos;
30703         }
30704         
30705         if(box[0].size == 'md-right'){
30706             pos.push({
30707                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30708                 y : minY + (this.unitWidth + this.gutter) * 1
30709             });
30710             
30711             return pos;
30712         }
30713         
30714         var rand = Math.floor(Math.random() * (4 - box[0].y));
30715         
30716         pos.push({
30717             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30718             y : minY + (this.unitWidth + this.gutter) * rand
30719         });
30720         
30721         return pos;
30722         
30723     },
30724     
30725     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30726     {
30727         var pos = [];
30728         
30729         if(box[0].size == 'xs'){
30730             
30731             pos.push({
30732                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30733                 y : minY
30734             });
30735
30736             pos.push({
30737                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30738                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30739             });
30740             
30741             return pos;
30742             
30743         }
30744         
30745         pos.push({
30746             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30747             y : minY
30748         });
30749
30750         pos.push({
30751             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30752             y : minY + (this.unitWidth + this.gutter) * 2
30753         });
30754         
30755         return pos;
30756         
30757     },
30758     
30759     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30760     {
30761         var pos = [];
30762         
30763         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30764             
30765             pos.push({
30766                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30767                 y : minY
30768             });
30769
30770             pos.push({
30771                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30772                 y : minY + (this.unitWidth + this.gutter) * 1
30773             });
30774             
30775             pos.push({
30776                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30777                 y : minY + (this.unitWidth + this.gutter) * 2
30778             });
30779             
30780             return pos;
30781             
30782         }
30783         
30784         if(box[0].size == 'xs' && box[1].size == 'xs'){
30785             
30786             pos.push({
30787                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30788                 y : minY
30789             });
30790
30791             pos.push({
30792                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30793                 y : minY
30794             });
30795             
30796             pos.push({
30797                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30798                 y : minY + (this.unitWidth + this.gutter) * 1
30799             });
30800             
30801             return pos;
30802             
30803         }
30804         
30805         pos.push({
30806             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30807             y : minY
30808         });
30809
30810         pos.push({
30811             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30812             y : minY + (this.unitWidth + this.gutter) * 2
30813         });
30814
30815         pos.push({
30816             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30817             y : minY + (this.unitWidth + this.gutter) * 2
30818         });
30819             
30820         return pos;
30821         
30822     },
30823     
30824     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30825     {
30826         var pos = [];
30827         
30828         if(box[0].size == 'xs'){
30829             
30830             pos.push({
30831                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30832                 y : minY
30833             });
30834
30835             pos.push({
30836                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30837                 y : minY
30838             });
30839             
30840             pos.push({
30841                 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),
30842                 y : minY
30843             });
30844             
30845             pos.push({
30846                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30847                 y : minY + (this.unitWidth + this.gutter) * 1
30848             });
30849             
30850             return pos;
30851             
30852         }
30853         
30854         pos.push({
30855             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30856             y : minY
30857         });
30858         
30859         pos.push({
30860             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30861             y : minY + (this.unitWidth + this.gutter) * 2
30862         });
30863         
30864         pos.push({
30865             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30866             y : minY + (this.unitWidth + this.gutter) * 2
30867         });
30868         
30869         pos.push({
30870             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),
30871             y : minY + (this.unitWidth + this.gutter) * 2
30872         });
30873
30874         return pos;
30875         
30876     }
30877     
30878 });
30879
30880  
30881
30882  /**
30883  *
30884  * This is based on 
30885  * http://masonry.desandro.com
30886  *
30887  * The idea is to render all the bricks based on vertical width...
30888  *
30889  * The original code extends 'outlayer' - we might need to use that....
30890  * 
30891  */
30892
30893
30894 /**
30895  * @class Roo.bootstrap.LayoutMasonryAuto
30896  * @extends Roo.bootstrap.Component
30897  * Bootstrap Layout Masonry class
30898  * 
30899  * @constructor
30900  * Create a new Element
30901  * @param {Object} config The config object
30902  */
30903
30904 Roo.bootstrap.LayoutMasonryAuto = function(config){
30905     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30906 };
30907
30908 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30909     
30910       /**
30911      * @cfg {Boolean} isFitWidth  - resize the width..
30912      */   
30913     isFitWidth : false,  // options..
30914     /**
30915      * @cfg {Boolean} isOriginLeft = left align?
30916      */   
30917     isOriginLeft : true,
30918     /**
30919      * @cfg {Boolean} isOriginTop = top align?
30920      */   
30921     isOriginTop : false,
30922     /**
30923      * @cfg {Boolean} isLayoutInstant = no animation?
30924      */   
30925     isLayoutInstant : false, // needed?
30926     /**
30927      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30928      */   
30929     isResizingContainer : true,
30930     /**
30931      * @cfg {Number} columnWidth  width of the columns 
30932      */   
30933     
30934     columnWidth : 0,
30935     
30936     /**
30937      * @cfg {Number} maxCols maximum number of columns
30938      */   
30939     
30940     maxCols: 0,
30941     /**
30942      * @cfg {Number} padHeight padding below box..
30943      */   
30944     
30945     padHeight : 10, 
30946     
30947     /**
30948      * @cfg {Boolean} isAutoInitial defalut true
30949      */   
30950     
30951     isAutoInitial : true, 
30952     
30953     // private?
30954     gutter : 0,
30955     
30956     containerWidth: 0,
30957     initialColumnWidth : 0,
30958     currentSize : null,
30959     
30960     colYs : null, // array.
30961     maxY : 0,
30962     padWidth: 10,
30963     
30964     
30965     tag: 'div',
30966     cls: '',
30967     bricks: null, //CompositeElement
30968     cols : 0, // array?
30969     // element : null, // wrapped now this.el
30970     _isLayoutInited : null, 
30971     
30972     
30973     getAutoCreate : function(){
30974         
30975         var cfg = {
30976             tag: this.tag,
30977             cls: 'blog-masonary-wrapper ' + this.cls,
30978             cn : {
30979                 cls : 'mas-boxes masonary'
30980             }
30981         };
30982         
30983         return cfg;
30984     },
30985     
30986     getChildContainer: function( )
30987     {
30988         if (this.boxesEl) {
30989             return this.boxesEl;
30990         }
30991         
30992         this.boxesEl = this.el.select('.mas-boxes').first();
30993         
30994         return this.boxesEl;
30995     },
30996     
30997     
30998     initEvents : function()
30999     {
31000         var _this = this;
31001         
31002         if(this.isAutoInitial){
31003             Roo.log('hook children rendered');
31004             this.on('childrenrendered', function() {
31005                 Roo.log('children rendered');
31006                 _this.initial();
31007             } ,this);
31008         }
31009         
31010     },
31011     
31012     initial : function()
31013     {
31014         this.reloadItems();
31015
31016         this.currentSize = this.el.getBox(true);
31017
31018         /// was window resize... - let's see if this works..
31019         Roo.EventManager.onWindowResize(this.resize, this); 
31020
31021         if(!this.isAutoInitial){
31022             this.layout();
31023             return;
31024         }
31025         
31026         this.layout.defer(500,this);
31027     },
31028     
31029     reloadItems: function()
31030     {
31031         this.bricks = this.el.select('.masonry-brick', true);
31032         
31033         this.bricks.each(function(b) {
31034             //Roo.log(b.getSize());
31035             if (!b.attr('originalwidth')) {
31036                 b.attr('originalwidth',  b.getSize().width);
31037             }
31038             
31039         });
31040         
31041         Roo.log(this.bricks.elements.length);
31042     },
31043     
31044     resize : function()
31045     {
31046         Roo.log('resize');
31047         var cs = this.el.getBox(true);
31048         
31049         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31050             Roo.log("no change in with or X");
31051             return;
31052         }
31053         this.currentSize = cs;
31054         this.layout();
31055     },
31056     
31057     layout : function()
31058     {
31059          Roo.log('layout');
31060         this._resetLayout();
31061         //this._manageStamps();
31062       
31063         // don't animate first layout
31064         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31065         this.layoutItems( isInstant );
31066       
31067         // flag for initalized
31068         this._isLayoutInited = true;
31069     },
31070     
31071     layoutItems : function( isInstant )
31072     {
31073         //var items = this._getItemsForLayout( this.items );
31074         // original code supports filtering layout items.. we just ignore it..
31075         
31076         this._layoutItems( this.bricks , isInstant );
31077       
31078         this._postLayout();
31079     },
31080     _layoutItems : function ( items , isInstant)
31081     {
31082        //this.fireEvent( 'layout', this, items );
31083     
31084
31085         if ( !items || !items.elements.length ) {
31086           // no items, emit event with empty array
31087             return;
31088         }
31089
31090         var queue = [];
31091         items.each(function(item) {
31092             Roo.log("layout item");
31093             Roo.log(item);
31094             // get x/y object from method
31095             var position = this._getItemLayoutPosition( item );
31096             // enqueue
31097             position.item = item;
31098             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31099             queue.push( position );
31100         }, this);
31101       
31102         this._processLayoutQueue( queue );
31103     },
31104     /** Sets position of item in DOM
31105     * @param {Element} item
31106     * @param {Number} x - horizontal position
31107     * @param {Number} y - vertical position
31108     * @param {Boolean} isInstant - disables transitions
31109     */
31110     _processLayoutQueue : function( queue )
31111     {
31112         for ( var i=0, len = queue.length; i < len; i++ ) {
31113             var obj = queue[i];
31114             obj.item.position('absolute');
31115             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31116         }
31117     },
31118       
31119     
31120     /**
31121     * Any logic you want to do after each layout,
31122     * i.e. size the container
31123     */
31124     _postLayout : function()
31125     {
31126         this.resizeContainer();
31127     },
31128     
31129     resizeContainer : function()
31130     {
31131         if ( !this.isResizingContainer ) {
31132             return;
31133         }
31134         var size = this._getContainerSize();
31135         if ( size ) {
31136             this.el.setSize(size.width,size.height);
31137             this.boxesEl.setSize(size.width,size.height);
31138         }
31139     },
31140     
31141     
31142     
31143     _resetLayout : function()
31144     {
31145         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31146         this.colWidth = this.el.getWidth();
31147         //this.gutter = this.el.getWidth(); 
31148         
31149         this.measureColumns();
31150
31151         // reset column Y
31152         var i = this.cols;
31153         this.colYs = [];
31154         while (i--) {
31155             this.colYs.push( 0 );
31156         }
31157     
31158         this.maxY = 0;
31159     },
31160
31161     measureColumns : function()
31162     {
31163         this.getContainerWidth();
31164       // if columnWidth is 0, default to outerWidth of first item
31165         if ( !this.columnWidth ) {
31166             var firstItem = this.bricks.first();
31167             Roo.log(firstItem);
31168             this.columnWidth  = this.containerWidth;
31169             if (firstItem && firstItem.attr('originalwidth') ) {
31170                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31171             }
31172             // columnWidth fall back to item of first element
31173             Roo.log("set column width?");
31174                         this.initialColumnWidth = this.columnWidth  ;
31175
31176             // if first elem has no width, default to size of container
31177             
31178         }
31179         
31180         
31181         if (this.initialColumnWidth) {
31182             this.columnWidth = this.initialColumnWidth;
31183         }
31184         
31185         
31186             
31187         // column width is fixed at the top - however if container width get's smaller we should
31188         // reduce it...
31189         
31190         // this bit calcs how man columns..
31191             
31192         var columnWidth = this.columnWidth += this.gutter;
31193       
31194         // calculate columns
31195         var containerWidth = this.containerWidth + this.gutter;
31196         
31197         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31198         // fix rounding errors, typically with gutters
31199         var excess = columnWidth - containerWidth % columnWidth;
31200         
31201         
31202         // if overshoot is less than a pixel, round up, otherwise floor it
31203         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31204         cols = Math[ mathMethod ]( cols );
31205         this.cols = Math.max( cols, 1 );
31206         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31207         
31208          // padding positioning..
31209         var totalColWidth = this.cols * this.columnWidth;
31210         var padavail = this.containerWidth - totalColWidth;
31211         // so for 2 columns - we need 3 'pads'
31212         
31213         var padNeeded = (1+this.cols) * this.padWidth;
31214         
31215         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31216         
31217         this.columnWidth += padExtra
31218         //this.padWidth = Math.floor(padavail /  ( this.cols));
31219         
31220         // adjust colum width so that padding is fixed??
31221         
31222         // we have 3 columns ... total = width * 3
31223         // we have X left over... that should be used by 
31224         
31225         //if (this.expandC) {
31226             
31227         //}
31228         
31229         
31230         
31231     },
31232     
31233     getContainerWidth : function()
31234     {
31235        /* // container is parent if fit width
31236         var container = this.isFitWidth ? this.element.parentNode : this.element;
31237         // check that this.size and size are there
31238         // IE8 triggers resize on body size change, so they might not be
31239         
31240         var size = getSize( container );  //FIXME
31241         this.containerWidth = size && size.innerWidth; //FIXME
31242         */
31243          
31244         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31245         
31246     },
31247     
31248     _getItemLayoutPosition : function( item )  // what is item?
31249     {
31250         // we resize the item to our columnWidth..
31251       
31252         item.setWidth(this.columnWidth);
31253         item.autoBoxAdjust  = false;
31254         
31255         var sz = item.getSize();
31256  
31257         // how many columns does this brick span
31258         var remainder = this.containerWidth % this.columnWidth;
31259         
31260         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31261         // round if off by 1 pixel, otherwise use ceil
31262         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31263         colSpan = Math.min( colSpan, this.cols );
31264         
31265         // normally this should be '1' as we dont' currently allow multi width columns..
31266         
31267         var colGroup = this._getColGroup( colSpan );
31268         // get the minimum Y value from the columns
31269         var minimumY = Math.min.apply( Math, colGroup );
31270         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31271         
31272         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31273          
31274         // position the brick
31275         var position = {
31276             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31277             y: this.currentSize.y + minimumY + this.padHeight
31278         };
31279         
31280         Roo.log(position);
31281         // apply setHeight to necessary columns
31282         var setHeight = minimumY + sz.height + this.padHeight;
31283         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31284         
31285         var setSpan = this.cols + 1 - colGroup.length;
31286         for ( var i = 0; i < setSpan; i++ ) {
31287           this.colYs[ shortColIndex + i ] = setHeight ;
31288         }
31289       
31290         return position;
31291     },
31292     
31293     /**
31294      * @param {Number} colSpan - number of columns the element spans
31295      * @returns {Array} colGroup
31296      */
31297     _getColGroup : function( colSpan )
31298     {
31299         if ( colSpan < 2 ) {
31300           // if brick spans only one column, use all the column Ys
31301           return this.colYs;
31302         }
31303       
31304         var colGroup = [];
31305         // how many different places could this brick fit horizontally
31306         var groupCount = this.cols + 1 - colSpan;
31307         // for each group potential horizontal position
31308         for ( var i = 0; i < groupCount; i++ ) {
31309           // make an array of colY values for that one group
31310           var groupColYs = this.colYs.slice( i, i + colSpan );
31311           // and get the max value of the array
31312           colGroup[i] = Math.max.apply( Math, groupColYs );
31313         }
31314         return colGroup;
31315     },
31316     /*
31317     _manageStamp : function( stamp )
31318     {
31319         var stampSize =  stamp.getSize();
31320         var offset = stamp.getBox();
31321         // get the columns that this stamp affects
31322         var firstX = this.isOriginLeft ? offset.x : offset.right;
31323         var lastX = firstX + stampSize.width;
31324         var firstCol = Math.floor( firstX / this.columnWidth );
31325         firstCol = Math.max( 0, firstCol );
31326         
31327         var lastCol = Math.floor( lastX / this.columnWidth );
31328         // lastCol should not go over if multiple of columnWidth #425
31329         lastCol -= lastX % this.columnWidth ? 0 : 1;
31330         lastCol = Math.min( this.cols - 1, lastCol );
31331         
31332         // set colYs to bottom of the stamp
31333         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31334             stampSize.height;
31335             
31336         for ( var i = firstCol; i <= lastCol; i++ ) {
31337           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31338         }
31339     },
31340     */
31341     
31342     _getContainerSize : function()
31343     {
31344         this.maxY = Math.max.apply( Math, this.colYs );
31345         var size = {
31346             height: this.maxY
31347         };
31348       
31349         if ( this.isFitWidth ) {
31350             size.width = this._getContainerFitWidth();
31351         }
31352       
31353         return size;
31354     },
31355     
31356     _getContainerFitWidth : function()
31357     {
31358         var unusedCols = 0;
31359         // count unused columns
31360         var i = this.cols;
31361         while ( --i ) {
31362           if ( this.colYs[i] !== 0 ) {
31363             break;
31364           }
31365           unusedCols++;
31366         }
31367         // fit container to columns that have been used
31368         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31369     },
31370     
31371     needsResizeLayout : function()
31372     {
31373         var previousWidth = this.containerWidth;
31374         this.getContainerWidth();
31375         return previousWidth !== this.containerWidth;
31376     }
31377  
31378 });
31379
31380  
31381
31382  /*
31383  * - LGPL
31384  *
31385  * element
31386  * 
31387  */
31388
31389 /**
31390  * @class Roo.bootstrap.MasonryBrick
31391  * @extends Roo.bootstrap.Component
31392  * Bootstrap MasonryBrick class
31393  * 
31394  * @constructor
31395  * Create a new MasonryBrick
31396  * @param {Object} config The config object
31397  */
31398
31399 Roo.bootstrap.MasonryBrick = function(config){
31400     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31401     
31402     this.addEvents({
31403         // raw events
31404         /**
31405          * @event click
31406          * When a MasonryBrick is clcik
31407          * @param {Roo.bootstrap.MasonryBrick} this
31408          * @param {Roo.EventObject} e
31409          */
31410         "click" : true
31411     });
31412 };
31413
31414 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31415     
31416     /**
31417      * @cfg {String} title
31418      */   
31419     title : '',
31420     /**
31421      * @cfg {String} html
31422      */   
31423     html : '',
31424     /**
31425      * @cfg {String} bgimage
31426      */   
31427     bgimage : '',
31428     /**
31429      * @cfg {String} videourl
31430      */   
31431     videourl : '',
31432     /**
31433      * @cfg {String} cls
31434      */   
31435     cls : '',
31436     /**
31437      * @cfg {String} href
31438      */   
31439     href : '',
31440     /**
31441      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31442      */   
31443     size : 'xs',
31444     
31445     /**
31446      * @cfg {String} (center|bottom) placetitle
31447      */   
31448     placetitle : '',
31449     
31450     /**
31451      * @cfg {Boolean} isFitContainer defalut true
31452      */   
31453     isFitContainer : true, 
31454     
31455     /**
31456      * @cfg {Boolean} preventDefault defalut false
31457      */   
31458     preventDefault : false, 
31459     
31460     getAutoCreate : function()
31461     {
31462         if(!this.isFitContainer){
31463             return this.getSplitAutoCreate();
31464         }
31465         
31466         var cls = 'masonry-brick masonry-brick-full';
31467         
31468         if(this.href.length){
31469             cls += ' masonry-brick-link';
31470         }
31471         
31472         if(this.bgimage.length){
31473             cls += ' masonry-brick-image';
31474         }
31475         
31476         if(!this.html.length){
31477             cls += ' enable-mask';
31478         }
31479         
31480         if(this.size){
31481             cls += ' masonry-' + this.size + '-brick';
31482         }
31483         
31484         if(this.placetitle.length){
31485             
31486             switch (this.placetitle) {
31487                 case 'center' :
31488                     cls += ' masonry-center-title';
31489                     break;
31490                 case 'bottom' :
31491                     cls += ' masonry-bottom-title';
31492                     break;
31493                 default:
31494                     break;
31495             }
31496             
31497         } else {
31498             if(!this.html.length && !this.bgimage.length){
31499                 cls += ' masonry-center-title';
31500             }
31501
31502             if(!this.html.length && this.bgimage.length){
31503                 cls += ' masonry-bottom-title';
31504             }
31505         }
31506         
31507         if(this.cls){
31508             cls += ' ' + this.cls;
31509         }
31510         
31511         var cfg = {
31512             tag: (this.href.length) ? 'a' : 'div',
31513             cls: cls,
31514             cn: [
31515                 {
31516                     tag: 'div',
31517                     cls: 'masonry-brick-paragraph',
31518                     cn: []
31519                 }
31520             ]
31521         };
31522         
31523         if(this.href.length){
31524             cfg.href = this.href;
31525         }
31526         
31527         var cn = cfg.cn[0].cn;
31528         
31529         if(this.title.length){
31530             cn.push({
31531                 tag: 'h4',
31532                 cls: 'masonry-brick-title',
31533                 html: this.title
31534             });
31535         }
31536         
31537         if(this.html.length){
31538             cn.push({
31539                 tag: 'p',
31540                 cls: 'masonry-brick-text',
31541                 html: this.html
31542             });
31543         }  
31544         if (!this.title.length && !this.html.length) {
31545             cfg.cn[0].cls += ' hide';
31546         }
31547         
31548         if(this.bgimage.length){
31549             cfg.cn.push({
31550                 tag: 'img',
31551                 cls: 'masonry-brick-image-view',
31552                 src: this.bgimage
31553             });
31554         }
31555         
31556         if(this.videourl.length){
31557             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31558             // youtube support only?
31559             cfg.cn.push({
31560                 tag: 'iframe',
31561                 cls: 'masonry-brick-image-view',
31562                 src: vurl,
31563                 frameborder : 0,
31564                 allowfullscreen : true
31565             });
31566             
31567             
31568         }
31569         
31570         cfg.cn.push({
31571             tag: 'div',
31572             cls: 'masonry-brick-mask'
31573         });
31574         
31575         return cfg;
31576         
31577     },
31578     
31579     getSplitAutoCreate : function()
31580     {
31581         var cls = 'masonry-brick masonry-brick-split';
31582         
31583         if(this.href.length){
31584             cls += ' masonry-brick-link';
31585         }
31586         
31587         if(this.bgimage.length){
31588             cls += ' masonry-brick-image';
31589         }
31590         
31591         if(this.size){
31592             cls += ' masonry-' + this.size + '-brick';
31593         }
31594         
31595         switch (this.placetitle) {
31596             case 'center' :
31597                 cls += ' masonry-center-title';
31598                 break;
31599             case 'bottom' :
31600                 cls += ' masonry-bottom-title';
31601                 break;
31602             default:
31603                 if(!this.bgimage.length){
31604                     cls += ' masonry-center-title';
31605                 }
31606
31607                 if(this.bgimage.length){
31608                     cls += ' masonry-bottom-title';
31609                 }
31610                 break;
31611         }
31612         
31613         if(this.cls){
31614             cls += ' ' + this.cls;
31615         }
31616         
31617         var cfg = {
31618             tag: (this.href.length) ? 'a' : 'div',
31619             cls: cls,
31620             cn: [
31621                 {
31622                     tag: 'div',
31623                     cls: 'masonry-brick-split-head',
31624                     cn: [
31625                         {
31626                             tag: 'div',
31627                             cls: 'masonry-brick-paragraph',
31628                             cn: []
31629                         }
31630                     ]
31631                 },
31632                 {
31633                     tag: 'div',
31634                     cls: 'masonry-brick-split-body',
31635                     cn: []
31636                 }
31637             ]
31638         };
31639         
31640         if(this.href.length){
31641             cfg.href = this.href;
31642         }
31643         
31644         if(this.title.length){
31645             cfg.cn[0].cn[0].cn.push({
31646                 tag: 'h4',
31647                 cls: 'masonry-brick-title',
31648                 html: this.title
31649             });
31650         }
31651         
31652         if(this.html.length){
31653             cfg.cn[1].cn.push({
31654                 tag: 'p',
31655                 cls: 'masonry-brick-text',
31656                 html: this.html
31657             });
31658         }
31659
31660         if(this.bgimage.length){
31661             cfg.cn[0].cn.push({
31662                 tag: 'img',
31663                 cls: 'masonry-brick-image-view',
31664                 src: this.bgimage
31665             });
31666         }
31667         
31668         if(this.videourl.length){
31669             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31670             // youtube support only?
31671             cfg.cn[0].cn.cn.push({
31672                 tag: 'iframe',
31673                 cls: 'masonry-brick-image-view',
31674                 src: vurl,
31675                 frameborder : 0,
31676                 allowfullscreen : true
31677             });
31678         }
31679         
31680         return cfg;
31681     },
31682     
31683     initEvents: function() 
31684     {
31685         switch (this.size) {
31686             case 'xs' :
31687                 this.x = 1;
31688                 this.y = 1;
31689                 break;
31690             case 'sm' :
31691                 this.x = 2;
31692                 this.y = 2;
31693                 break;
31694             case 'md' :
31695             case 'md-left' :
31696             case 'md-right' :
31697                 this.x = 3;
31698                 this.y = 3;
31699                 break;
31700             case 'tall' :
31701                 this.x = 2;
31702                 this.y = 3;
31703                 break;
31704             case 'wide' :
31705                 this.x = 3;
31706                 this.y = 2;
31707                 break;
31708             case 'wide-thin' :
31709                 this.x = 3;
31710                 this.y = 1;
31711                 break;
31712                         
31713             default :
31714                 break;
31715         }
31716         
31717         if(Roo.isTouch){
31718             this.el.on('touchstart', this.onTouchStart, this);
31719             this.el.on('touchmove', this.onTouchMove, this);
31720             this.el.on('touchend', this.onTouchEnd, this);
31721             this.el.on('contextmenu', this.onContextMenu, this);
31722         } else {
31723             this.el.on('mouseenter'  ,this.enter, this);
31724             this.el.on('mouseleave', this.leave, this);
31725             this.el.on('click', this.onClick, this);
31726         }
31727         
31728         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31729             this.parent().bricks.push(this);   
31730         }
31731         
31732     },
31733     
31734     onClick: function(e, el)
31735     {
31736         var time = this.endTimer - this.startTimer;
31737         
31738         if(Roo.isTouch){
31739             if(time > 1000){
31740                 e.preventDefault();
31741                 return;
31742             }
31743         }
31744         
31745         if(!this.preventDefault){
31746             return;
31747         }
31748         
31749         e.preventDefault();
31750         this.fireEvent('click', this);
31751     },
31752     
31753     enter: function(e, el)
31754     {
31755         e.preventDefault();
31756         
31757         if(!this.isFitContainer){
31758             return;
31759         }
31760         
31761         if(this.bgimage.length && this.html.length){
31762             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31763         }
31764     },
31765     
31766     leave: function(e, el)
31767     {
31768         e.preventDefault();
31769         
31770         if(!this.isFitContainer){
31771             return;
31772         }
31773         
31774         if(this.bgimage.length && this.html.length){
31775             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31776         }
31777     },
31778     
31779     onTouchStart: function(e, el)
31780     {
31781 //        e.preventDefault();
31782         
31783         this.touchmoved = false;
31784         
31785         if(!this.isFitContainer){
31786             return;
31787         }
31788         
31789         if(!this.bgimage.length || !this.html.length){
31790             return;
31791         }
31792         
31793         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31794         
31795         this.timer = new Date().getTime();
31796         
31797     },
31798     
31799     onTouchMove: function(e, el)
31800     {
31801         this.touchmoved = true;
31802     },
31803     
31804     onContextMenu : function(e,el)
31805     {
31806         e.preventDefault();
31807         e.stopPropagation();
31808         return false;
31809     },
31810     
31811     onTouchEnd: function(e, el)
31812     {
31813 //        e.preventDefault();
31814         
31815         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31816         
31817             this.leave(e,el);
31818             
31819             return;
31820         }
31821         
31822         if(!this.bgimage.length || !this.html.length){
31823             
31824             if(this.href.length){
31825                 window.location.href = this.href;
31826             }
31827             
31828             return;
31829         }
31830         
31831         if(!this.isFitContainer){
31832             return;
31833         }
31834         
31835         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31836         
31837         window.location.href = this.href;
31838     }
31839     
31840 });
31841
31842  
31843
31844  /*
31845  * - LGPL
31846  *
31847  * element
31848  * 
31849  */
31850
31851 /**
31852  * @class Roo.bootstrap.Brick
31853  * @extends Roo.bootstrap.Component
31854  * Bootstrap Brick class
31855  * 
31856  * @constructor
31857  * Create a new Brick
31858  * @param {Object} config The config object
31859  */
31860
31861 Roo.bootstrap.Brick = function(config){
31862     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31863     
31864     this.addEvents({
31865         // raw events
31866         /**
31867          * @event click
31868          * When a Brick is click
31869          * @param {Roo.bootstrap.Brick} this
31870          * @param {Roo.EventObject} e
31871          */
31872         "click" : true
31873     });
31874 };
31875
31876 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31877     
31878     /**
31879      * @cfg {String} title
31880      */   
31881     title : '',
31882     /**
31883      * @cfg {String} html
31884      */   
31885     html : '',
31886     /**
31887      * @cfg {String} bgimage
31888      */   
31889     bgimage : '',
31890     /**
31891      * @cfg {String} cls
31892      */   
31893     cls : '',
31894     /**
31895      * @cfg {String} href
31896      */   
31897     href : '',
31898     /**
31899      * @cfg {String} video
31900      */   
31901     video : '',
31902     /**
31903      * @cfg {Boolean} square
31904      */   
31905     square : true,
31906     
31907     getAutoCreate : function()
31908     {
31909         var cls = 'roo-brick';
31910         
31911         if(this.href.length){
31912             cls += ' roo-brick-link';
31913         }
31914         
31915         if(this.bgimage.length){
31916             cls += ' roo-brick-image';
31917         }
31918         
31919         if(!this.html.length && !this.bgimage.length){
31920             cls += ' roo-brick-center-title';
31921         }
31922         
31923         if(!this.html.length && this.bgimage.length){
31924             cls += ' roo-brick-bottom-title';
31925         }
31926         
31927         if(this.cls){
31928             cls += ' ' + this.cls;
31929         }
31930         
31931         var cfg = {
31932             tag: (this.href.length) ? 'a' : 'div',
31933             cls: cls,
31934             cn: [
31935                 {
31936                     tag: 'div',
31937                     cls: 'roo-brick-paragraph',
31938                     cn: []
31939                 }
31940             ]
31941         };
31942         
31943         if(this.href.length){
31944             cfg.href = this.href;
31945         }
31946         
31947         var cn = cfg.cn[0].cn;
31948         
31949         if(this.title.length){
31950             cn.push({
31951                 tag: 'h4',
31952                 cls: 'roo-brick-title',
31953                 html: this.title
31954             });
31955         }
31956         
31957         if(this.html.length){
31958             cn.push({
31959                 tag: 'p',
31960                 cls: 'roo-brick-text',
31961                 html: this.html
31962             });
31963         } else {
31964             cn.cls += ' hide';
31965         }
31966         
31967         if(this.bgimage.length){
31968             cfg.cn.push({
31969                 tag: 'img',
31970                 cls: 'roo-brick-image-view',
31971                 src: this.bgimage
31972             });
31973         }
31974         
31975         return cfg;
31976     },
31977     
31978     initEvents: function() 
31979     {
31980         if(this.title.length || this.html.length){
31981             this.el.on('mouseenter'  ,this.enter, this);
31982             this.el.on('mouseleave', this.leave, this);
31983         }
31984         
31985         
31986         Roo.EventManager.onWindowResize(this.resize, this); 
31987         
31988         this.resize();
31989     },
31990     
31991     resize : function()
31992     {
31993         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31994         
31995         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31996         
31997         if(this.bgimage.length){
31998             var image = this.el.select('.roo-brick-image-view', true).first();
31999             image.setWidth(paragraph.getWidth());
32000             image.setHeight(paragraph.getWidth());
32001             
32002             this.el.setHeight(paragraph.getWidth());
32003             
32004         }
32005         
32006     },
32007     
32008     enter: function(e, el)
32009     {
32010         e.preventDefault();
32011         
32012         if(this.bgimage.length){
32013             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32014             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32015         }
32016     },
32017     
32018     leave: function(e, el)
32019     {
32020         e.preventDefault();
32021         
32022         if(this.bgimage.length){
32023             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32024             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32025         }
32026     }
32027     
32028 });
32029
32030  
32031
32032  /*
32033  * - LGPL
32034  *
32035  * Input
32036  * 
32037  */
32038
32039 /**
32040  * @class Roo.bootstrap.NumberField
32041  * @extends Roo.bootstrap.Input
32042  * Bootstrap NumberField class
32043  * 
32044  * 
32045  * 
32046  * 
32047  * @constructor
32048  * Create a new NumberField
32049  * @param {Object} config The config object
32050  */
32051
32052 Roo.bootstrap.NumberField = function(config){
32053     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32054 };
32055
32056 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32057     
32058     /**
32059      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32060      */
32061     allowDecimals : true,
32062     /**
32063      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32064      */
32065     decimalSeparator : ".",
32066     /**
32067      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32068      */
32069     decimalPrecision : 2,
32070     /**
32071      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32072      */
32073     allowNegative : true,
32074     /**
32075      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32076      */
32077     minValue : Number.NEGATIVE_INFINITY,
32078     /**
32079      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32080      */
32081     maxValue : Number.MAX_VALUE,
32082     /**
32083      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32084      */
32085     minText : "The minimum value for this field is {0}",
32086     /**
32087      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32088      */
32089     maxText : "The maximum value for this field is {0}",
32090     /**
32091      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32092      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32093      */
32094     nanText : "{0} is not a valid number",
32095     /**
32096      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32097      */
32098     castInt : true,
32099
32100     // private
32101     initEvents : function()
32102     {   
32103         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32104         
32105         var allowed = "0123456789";
32106         
32107         if(this.allowDecimals){
32108             allowed += this.decimalSeparator;
32109         }
32110         
32111         if(this.allowNegative){
32112             allowed += "-";
32113         }
32114         
32115         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32116         
32117         var keyPress = function(e){
32118             
32119             var k = e.getKey();
32120             
32121             var c = e.getCharCode();
32122             
32123             if(
32124                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32125                     allowed.indexOf(String.fromCharCode(c)) === -1
32126             ){
32127                 e.stopEvent();
32128                 return;
32129             }
32130             
32131             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32132                 return;
32133             }
32134             
32135             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32136                 e.stopEvent();
32137             }
32138         };
32139         
32140         this.el.on("keypress", keyPress, this);
32141     },
32142     
32143     validateValue : function(value)
32144     {
32145         
32146         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32147             return false;
32148         }
32149         
32150         var num = this.parseValue(value);
32151         
32152         if(isNaN(num)){
32153             this.markInvalid(String.format(this.nanText, value));
32154             return false;
32155         }
32156         
32157         if(num < this.minValue){
32158             this.markInvalid(String.format(this.minText, this.minValue));
32159             return false;
32160         }
32161         
32162         if(num > this.maxValue){
32163             this.markInvalid(String.format(this.maxText, this.maxValue));
32164             return false;
32165         }
32166         
32167         return true;
32168     },
32169
32170     getValue : function()
32171     {
32172         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32173     },
32174
32175     parseValue : function(value)
32176     {
32177         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32178         return isNaN(value) ? '' : value;
32179     },
32180
32181     fixPrecision : function(value)
32182     {
32183         var nan = isNaN(value);
32184         
32185         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32186             return nan ? '' : value;
32187         }
32188         return parseFloat(value).toFixed(this.decimalPrecision);
32189     },
32190
32191     setValue : function(v)
32192     {
32193         v = this.fixPrecision(v);
32194         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32195     },
32196
32197     decimalPrecisionFcn : function(v)
32198     {
32199         return Math.floor(v);
32200     },
32201
32202     beforeBlur : function()
32203     {
32204         if(!this.castInt){
32205             return;
32206         }
32207         
32208         var v = this.parseValue(this.getRawValue());
32209         if(v){
32210             this.setValue(v);
32211         }
32212     }
32213     
32214 });
32215
32216  
32217
32218 /*
32219 * Licence: LGPL
32220 */
32221
32222 /**
32223  * @class Roo.bootstrap.DocumentSlider
32224  * @extends Roo.bootstrap.Component
32225  * Bootstrap DocumentSlider class
32226  * 
32227  * @constructor
32228  * Create a new DocumentViewer
32229  * @param {Object} config The config object
32230  */
32231
32232 Roo.bootstrap.DocumentSlider = function(config){
32233     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32234     
32235     this.files = [];
32236     
32237     this.addEvents({
32238         /**
32239          * @event initial
32240          * Fire after initEvent
32241          * @param {Roo.bootstrap.DocumentSlider} this
32242          */
32243         "initial" : true,
32244         /**
32245          * @event update
32246          * Fire after update
32247          * @param {Roo.bootstrap.DocumentSlider} this
32248          */
32249         "update" : true,
32250         /**
32251          * @event click
32252          * Fire after click
32253          * @param {Roo.bootstrap.DocumentSlider} this
32254          */
32255         "click" : true
32256     });
32257 };
32258
32259 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32260     
32261     files : false,
32262     
32263     indicator : 0,
32264     
32265     getAutoCreate : function()
32266     {
32267         var cfg = {
32268             tag : 'div',
32269             cls : 'roo-document-slider',
32270             cn : [
32271                 {
32272                     tag : 'div',
32273                     cls : 'roo-document-slider-header',
32274                     cn : [
32275                         {
32276                             tag : 'div',
32277                             cls : 'roo-document-slider-header-title'
32278                         }
32279                     ]
32280                 },
32281                 {
32282                     tag : 'div',
32283                     cls : 'roo-document-slider-body',
32284                     cn : [
32285                         {
32286                             tag : 'div',
32287                             cls : 'roo-document-slider-prev',
32288                             cn : [
32289                                 {
32290                                     tag : 'i',
32291                                     cls : 'fa fa-chevron-left'
32292                                 }
32293                             ]
32294                         },
32295                         {
32296                             tag : 'div',
32297                             cls : 'roo-document-slider-thumb',
32298                             cn : [
32299                                 {
32300                                     tag : 'img',
32301                                     cls : 'roo-document-slider-image'
32302                                 }
32303                             ]
32304                         },
32305                         {
32306                             tag : 'div',
32307                             cls : 'roo-document-slider-next',
32308                             cn : [
32309                                 {
32310                                     tag : 'i',
32311                                     cls : 'fa fa-chevron-right'
32312                                 }
32313                             ]
32314                         }
32315                     ]
32316                 }
32317             ]
32318         };
32319         
32320         return cfg;
32321     },
32322     
32323     initEvents : function()
32324     {
32325         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
32326         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
32327         
32328         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
32329         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
32330         
32331         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
32332         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32333         
32334         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
32335         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32336         
32337         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
32338         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32339         
32340         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
32341         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32342         
32343         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
32344         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32345         
32346         this.thumbEl.on('click', this.onClick, this);
32347         
32348         this.prevIndicator.on('click', this.prev, this);
32349         
32350         this.nextIndicator.on('click', this.next, this);
32351         
32352     },
32353     
32354     initial : function()
32355     {
32356         if(this.files.length){
32357             this.indicator = 1;
32358             this.update()
32359         }
32360         
32361         this.fireEvent('initial', this);
32362     },
32363     
32364     update : function()
32365     {
32366         this.imageEl.attr('src', this.files[this.indicator - 1]);
32367         
32368         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
32369         
32370         this.prevIndicator.show();
32371         
32372         if(this.indicator == 1){
32373             this.prevIndicator.hide();
32374         }
32375         
32376         this.nextIndicator.show();
32377         
32378         if(this.indicator == this.files.length){
32379             this.nextIndicator.hide();
32380         }
32381         
32382         this.thumbEl.scrollTo('top');
32383         
32384         this.fireEvent('update', this);
32385     },
32386     
32387     onClick : function(e)
32388     {
32389         e.preventDefault();
32390         
32391         this.fireEvent('click', this);
32392     },
32393     
32394     prev : function(e)
32395     {
32396         e.preventDefault();
32397         
32398         this.indicator = Math.max(1, this.indicator - 1);
32399         
32400         this.update();
32401     },
32402     
32403     next : function(e)
32404     {
32405         e.preventDefault();
32406         
32407         this.indicator = Math.min(this.files.length, this.indicator + 1);
32408         
32409         this.update();
32410     }
32411 });
32412 /*
32413  * - LGPL
32414  *
32415  * RadioSet
32416  *
32417  *
32418  */
32419
32420 /**
32421  * @class Roo.bootstrap.RadioSet
32422  * @extends Roo.bootstrap.Input
32423  * Bootstrap RadioSet class
32424  * @cfg {String} indicatorpos (left|right) default left
32425  * @cfg {Boolean} inline (true|false) inline the element (default true)
32426  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
32427  * @constructor
32428  * Create a new RadioSet
32429  * @param {Object} config The config object
32430  */
32431
32432 Roo.bootstrap.RadioSet = function(config){
32433     
32434     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
32435     
32436     this.radioes = [];
32437     
32438     Roo.bootstrap.RadioSet.register(this);
32439     
32440     this.addEvents({
32441         /**
32442         * @event check
32443         * Fires when the element is checked or unchecked.
32444         * @param {Roo.bootstrap.RadioSet} this This radio
32445         * @param {Roo.bootstrap.Radio} item The checked item
32446         */
32447        check : true
32448     });
32449     
32450 };
32451
32452 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
32453
32454     radioes : false,
32455     
32456     inline : true,
32457     
32458     weight : '',
32459     
32460     indicatorpos : 'left',
32461     
32462     getAutoCreate : function()
32463     {
32464         var label = {
32465             tag : 'label',
32466             cls : 'roo-radio-set-label',
32467             cn : [
32468                 {
32469                     tag : 'span',
32470                     html : this.fieldLabel
32471                 }
32472             ]
32473         };
32474         
32475         if(this.indicatorpos == 'left'){
32476             label.cn.unshift({
32477                 tag : 'i',
32478                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
32479                 tooltip : 'This field is required'
32480             });
32481         } else {
32482             label.cn.push({
32483                 tag : 'i',
32484                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
32485                 tooltip : 'This field is required'
32486             });
32487         }
32488         
32489         var items = {
32490             tag : 'div',
32491             cls : 'roo-radio-set-items'
32492         };
32493         
32494         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
32495         
32496         if (align === 'left' && this.fieldLabel.length) {
32497             
32498             items = {
32499                 cls : "roo-radio-set-right", 
32500                 cn: [
32501                     items
32502                 ]
32503             };
32504             
32505             if(this.labelWidth > 12){
32506                 label.style = "width: " + this.labelWidth + 'px';
32507             }
32508             
32509             if(this.labelWidth < 13 && this.labelmd == 0){
32510                 this.labelmd = this.labelWidth;
32511             }
32512             
32513             if(this.labellg > 0){
32514                 label.cls += ' col-lg-' + this.labellg;
32515                 items.cls += ' col-lg-' + (12 - this.labellg);
32516             }
32517             
32518             if(this.labelmd > 0){
32519                 label.cls += ' col-md-' + this.labelmd;
32520                 items.cls += ' col-md-' + (12 - this.labelmd);
32521             }
32522             
32523             if(this.labelsm > 0){
32524                 label.cls += ' col-sm-' + this.labelsm;
32525                 items.cls += ' col-sm-' + (12 - this.labelsm);
32526             }
32527             
32528             if(this.labelxs > 0){
32529                 label.cls += ' col-xs-' + this.labelxs;
32530                 items.cls += ' col-xs-' + (12 - this.labelxs);
32531             }
32532         }
32533         
32534         var cfg = {
32535             tag : 'div',
32536             cls : 'roo-radio-set',
32537             cn : [
32538                 {
32539                     tag : 'input',
32540                     cls : 'roo-radio-set-input',
32541                     type : 'hidden',
32542                     name : this.name,
32543                     value : this.value ? this.value :  ''
32544                 },
32545                 label,
32546                 items
32547             ]
32548         };
32549         
32550         if(this.weight.length){
32551             cfg.cls += ' roo-radio-' + this.weight;
32552         }
32553         
32554         if(this.inline) {
32555             cfg.cls += ' roo-radio-set-inline';
32556         }
32557         
32558         return cfg;
32559         
32560     },
32561
32562     initEvents : function()
32563     {
32564         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
32565         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
32566         
32567         if(!this.fieldLabel.length){
32568             this.labelEl.hide();
32569         }
32570         
32571         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
32572         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
32573         
32574         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
32575         this.indicatorEl().hide();
32576         
32577         this.originalValue = this.getValue();
32578         
32579     },
32580     
32581     inputEl: function ()
32582     {
32583         return this.el.select('.roo-radio-set-input', true).first();
32584     },
32585     
32586     getChildContainer : function()
32587     {
32588         return this.itemsEl;
32589     },
32590     
32591     register : function(item)
32592     {
32593         this.radioes.push(item);
32594         
32595     },
32596     
32597     validate : function()
32598     {   
32599         var valid = false;
32600         
32601         Roo.each(this.radioes, function(i){
32602             if(!i.checked){
32603                 return;
32604             }
32605             
32606             valid = true;
32607             return false;
32608         });
32609         
32610         if(this.allowBlank) {
32611             return true;
32612         }
32613         
32614         if(this.disabled || valid){
32615             this.markValid();
32616             return true;
32617         }
32618         
32619         this.markInvalid();
32620         return false;
32621         
32622     },
32623     
32624     markValid : function()
32625     {
32626         if(this.labelEl.isVisible(true)){
32627             this.indicatorEl().hide();
32628         }
32629         
32630         this.el.removeClass([this.invalidClass, this.validClass]);
32631         this.el.addClass(this.validClass);
32632         
32633         this.fireEvent('valid', this);
32634     },
32635     
32636     markInvalid : function(msg)
32637     {
32638         if(this.allowBlank || this.disabled){
32639             return;
32640         }
32641         
32642         if(this.labelEl.isVisible(true)){
32643             this.indicatorEl().show();
32644         }
32645         
32646         this.el.removeClass([this.invalidClass, this.validClass]);
32647         this.el.addClass(this.invalidClass);
32648         
32649         this.fireEvent('invalid', this, msg);
32650         
32651     },
32652     
32653     setValue : function(v, suppressEvent)
32654     {   
32655         Roo.each(this.radioes, function(i){
32656             
32657             i.checked = false;
32658             i.el.removeClass('checked');
32659             
32660             if(i.value === v || i.value.toString() === v.toString()){
32661                 i.checked = true;
32662                 i.el.addClass('checked');
32663                 
32664                 if(suppressEvent !== true){
32665                     this.fireEvent('check', this, i);
32666                 }
32667             }
32668             
32669         }, this);
32670         
32671         Roo.bootstrap.RadioSet.superclass.setValue.call(this, v);
32672         
32673     },
32674     
32675     clearInvalid : function(){
32676         
32677         if(!this.el || this.preventMark){
32678             return;
32679         }
32680         
32681         if(this.labelEl.isVisible(true)){
32682             this.indicatorEl().hide();
32683         }
32684         
32685         this.el.removeClass([this.invalidClass]);
32686         
32687         this.fireEvent('valid', this);
32688     }
32689     
32690 });
32691
32692 Roo.apply(Roo.bootstrap.RadioSet, {
32693     
32694     groups: {},
32695     
32696     register : function(set)
32697     {
32698         this.groups[set.name] = set;
32699     },
32700     
32701     get: function(name) 
32702     {
32703         if (typeof(this.groups[name]) == 'undefined') {
32704             return false;
32705         }
32706         
32707         return this.groups[name] ;
32708     }
32709     
32710 });
32711 /*
32712  * Based on:
32713  * Ext JS Library 1.1.1
32714  * Copyright(c) 2006-2007, Ext JS, LLC.
32715  *
32716  * Originally Released Under LGPL - original licence link has changed is not relivant.
32717  *
32718  * Fork - LGPL
32719  * <script type="text/javascript">
32720  */
32721
32722
32723 /**
32724  * @class Roo.bootstrap.SplitBar
32725  * @extends Roo.util.Observable
32726  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32727  * <br><br>
32728  * Usage:
32729  * <pre><code>
32730 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32731                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32732 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32733 split.minSize = 100;
32734 split.maxSize = 600;
32735 split.animate = true;
32736 split.on('moved', splitterMoved);
32737 </code></pre>
32738  * @constructor
32739  * Create a new SplitBar
32740  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
32741  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32742  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32743  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
32744                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32745                         position of the SplitBar).
32746  */
32747 Roo.bootstrap.SplitBar = function(cfg){
32748     
32749     /** @private */
32750     
32751     //{
32752     //  dragElement : elm
32753     //  resizingElement: el,
32754         // optional..
32755     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32756     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
32757         // existingProxy ???
32758     //}
32759     
32760     this.el = Roo.get(cfg.dragElement, true);
32761     this.el.dom.unselectable = "on";
32762     /** @private */
32763     this.resizingEl = Roo.get(cfg.resizingElement, true);
32764
32765     /**
32766      * @private
32767      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32768      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32769      * @type Number
32770      */
32771     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32772     
32773     /**
32774      * The minimum size of the resizing element. (Defaults to 0)
32775      * @type Number
32776      */
32777     this.minSize = 0;
32778     
32779     /**
32780      * The maximum size of the resizing element. (Defaults to 2000)
32781      * @type Number
32782      */
32783     this.maxSize = 2000;
32784     
32785     /**
32786      * Whether to animate the transition to the new size
32787      * @type Boolean
32788      */
32789     this.animate = false;
32790     
32791     /**
32792      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32793      * @type Boolean
32794      */
32795     this.useShim = false;
32796     
32797     /** @private */
32798     this.shim = null;
32799     
32800     if(!cfg.existingProxy){
32801         /** @private */
32802         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32803     }else{
32804         this.proxy = Roo.get(cfg.existingProxy).dom;
32805     }
32806     /** @private */
32807     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32808     
32809     /** @private */
32810     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32811     
32812     /** @private */
32813     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32814     
32815     /** @private */
32816     this.dragSpecs = {};
32817     
32818     /**
32819      * @private The adapter to use to positon and resize elements
32820      */
32821     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32822     this.adapter.init(this);
32823     
32824     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32825         /** @private */
32826         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32827         this.el.addClass("roo-splitbar-h");
32828     }else{
32829         /** @private */
32830         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32831         this.el.addClass("roo-splitbar-v");
32832     }
32833     
32834     this.addEvents({
32835         /**
32836          * @event resize
32837          * Fires when the splitter is moved (alias for {@link #event-moved})
32838          * @param {Roo.bootstrap.SplitBar} this
32839          * @param {Number} newSize the new width or height
32840          */
32841         "resize" : true,
32842         /**
32843          * @event moved
32844          * Fires when the splitter is moved
32845          * @param {Roo.bootstrap.SplitBar} this
32846          * @param {Number} newSize the new width or height
32847          */
32848         "moved" : true,
32849         /**
32850          * @event beforeresize
32851          * Fires before the splitter is dragged
32852          * @param {Roo.bootstrap.SplitBar} this
32853          */
32854         "beforeresize" : true,
32855
32856         "beforeapply" : true
32857     });
32858
32859     Roo.util.Observable.call(this);
32860 };
32861
32862 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32863     onStartProxyDrag : function(x, y){
32864         this.fireEvent("beforeresize", this);
32865         if(!this.overlay){
32866             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32867             o.unselectable();
32868             o.enableDisplayMode("block");
32869             // all splitbars share the same overlay
32870             Roo.bootstrap.SplitBar.prototype.overlay = o;
32871         }
32872         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32873         this.overlay.show();
32874         Roo.get(this.proxy).setDisplayed("block");
32875         var size = this.adapter.getElementSize(this);
32876         this.activeMinSize = this.getMinimumSize();;
32877         this.activeMaxSize = this.getMaximumSize();;
32878         var c1 = size - this.activeMinSize;
32879         var c2 = Math.max(this.activeMaxSize - size, 0);
32880         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32881             this.dd.resetConstraints();
32882             this.dd.setXConstraint(
32883                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32884                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32885             );
32886             this.dd.setYConstraint(0, 0);
32887         }else{
32888             this.dd.resetConstraints();
32889             this.dd.setXConstraint(0, 0);
32890             this.dd.setYConstraint(
32891                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32892                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32893             );
32894          }
32895         this.dragSpecs.startSize = size;
32896         this.dragSpecs.startPoint = [x, y];
32897         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32898     },
32899     
32900     /** 
32901      * @private Called after the drag operation by the DDProxy
32902      */
32903     onEndProxyDrag : function(e){
32904         Roo.get(this.proxy).setDisplayed(false);
32905         var endPoint = Roo.lib.Event.getXY(e);
32906         if(this.overlay){
32907             this.overlay.hide();
32908         }
32909         var newSize;
32910         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32911             newSize = this.dragSpecs.startSize + 
32912                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32913                     endPoint[0] - this.dragSpecs.startPoint[0] :
32914                     this.dragSpecs.startPoint[0] - endPoint[0]
32915                 );
32916         }else{
32917             newSize = this.dragSpecs.startSize + 
32918                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32919                     endPoint[1] - this.dragSpecs.startPoint[1] :
32920                     this.dragSpecs.startPoint[1] - endPoint[1]
32921                 );
32922         }
32923         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32924         if(newSize != this.dragSpecs.startSize){
32925             if(this.fireEvent('beforeapply', this, newSize) !== false){
32926                 this.adapter.setElementSize(this, newSize);
32927                 this.fireEvent("moved", this, newSize);
32928                 this.fireEvent("resize", this, newSize);
32929             }
32930         }
32931     },
32932     
32933     /**
32934      * Get the adapter this SplitBar uses
32935      * @return The adapter object
32936      */
32937     getAdapter : function(){
32938         return this.adapter;
32939     },
32940     
32941     /**
32942      * Set the adapter this SplitBar uses
32943      * @param {Object} adapter A SplitBar adapter object
32944      */
32945     setAdapter : function(adapter){
32946         this.adapter = adapter;
32947         this.adapter.init(this);
32948     },
32949     
32950     /**
32951      * Gets the minimum size for the resizing element
32952      * @return {Number} The minimum size
32953      */
32954     getMinimumSize : function(){
32955         return this.minSize;
32956     },
32957     
32958     /**
32959      * Sets the minimum size for the resizing element
32960      * @param {Number} minSize The minimum size
32961      */
32962     setMinimumSize : function(minSize){
32963         this.minSize = minSize;
32964     },
32965     
32966     /**
32967      * Gets the maximum size for the resizing element
32968      * @return {Number} The maximum size
32969      */
32970     getMaximumSize : function(){
32971         return this.maxSize;
32972     },
32973     
32974     /**
32975      * Sets the maximum size for the resizing element
32976      * @param {Number} maxSize The maximum size
32977      */
32978     setMaximumSize : function(maxSize){
32979         this.maxSize = maxSize;
32980     },
32981     
32982     /**
32983      * Sets the initialize size for the resizing element
32984      * @param {Number} size The initial size
32985      */
32986     setCurrentSize : function(size){
32987         var oldAnimate = this.animate;
32988         this.animate = false;
32989         this.adapter.setElementSize(this, size);
32990         this.animate = oldAnimate;
32991     },
32992     
32993     /**
32994      * Destroy this splitbar. 
32995      * @param {Boolean} removeEl True to remove the element
32996      */
32997     destroy : function(removeEl){
32998         if(this.shim){
32999             this.shim.remove();
33000         }
33001         this.dd.unreg();
33002         this.proxy.parentNode.removeChild(this.proxy);
33003         if(removeEl){
33004             this.el.remove();
33005         }
33006     }
33007 });
33008
33009 /**
33010  * @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.
33011  */
33012 Roo.bootstrap.SplitBar.createProxy = function(dir){
33013     var proxy = new Roo.Element(document.createElement("div"));
33014     proxy.unselectable();
33015     var cls = 'roo-splitbar-proxy';
33016     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33017     document.body.appendChild(proxy.dom);
33018     return proxy.dom;
33019 };
33020
33021 /** 
33022  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33023  * Default Adapter. It assumes the splitter and resizing element are not positioned
33024  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33025  */
33026 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33027 };
33028
33029 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33030     // do nothing for now
33031     init : function(s){
33032     
33033     },
33034     /**
33035      * Called before drag operations to get the current size of the resizing element. 
33036      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33037      */
33038      getElementSize : function(s){
33039         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33040             return s.resizingEl.getWidth();
33041         }else{
33042             return s.resizingEl.getHeight();
33043         }
33044     },
33045     
33046     /**
33047      * Called after drag operations to set the size of the resizing element.
33048      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33049      * @param {Number} newSize The new size to set
33050      * @param {Function} onComplete A function to be invoked when resizing is complete
33051      */
33052     setElementSize : function(s, newSize, onComplete){
33053         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33054             if(!s.animate){
33055                 s.resizingEl.setWidth(newSize);
33056                 if(onComplete){
33057                     onComplete(s, newSize);
33058                 }
33059             }else{
33060                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33061             }
33062         }else{
33063             
33064             if(!s.animate){
33065                 s.resizingEl.setHeight(newSize);
33066                 if(onComplete){
33067                     onComplete(s, newSize);
33068                 }
33069             }else{
33070                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33071             }
33072         }
33073     }
33074 };
33075
33076 /** 
33077  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33078  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33079  * Adapter that  moves the splitter element to align with the resized sizing element. 
33080  * Used with an absolute positioned SplitBar.
33081  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33082  * document.body, make sure you assign an id to the body element.
33083  */
33084 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33085     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33086     this.container = Roo.get(container);
33087 };
33088
33089 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33090     init : function(s){
33091         this.basic.init(s);
33092     },
33093     
33094     getElementSize : function(s){
33095         return this.basic.getElementSize(s);
33096     },
33097     
33098     setElementSize : function(s, newSize, onComplete){
33099         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33100     },
33101     
33102     moveSplitter : function(s){
33103         var yes = Roo.bootstrap.SplitBar;
33104         switch(s.placement){
33105             case yes.LEFT:
33106                 s.el.setX(s.resizingEl.getRight());
33107                 break;
33108             case yes.RIGHT:
33109                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33110                 break;
33111             case yes.TOP:
33112                 s.el.setY(s.resizingEl.getBottom());
33113                 break;
33114             case yes.BOTTOM:
33115                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33116                 break;
33117         }
33118     }
33119 };
33120
33121 /**
33122  * Orientation constant - Create a vertical SplitBar
33123  * @static
33124  * @type Number
33125  */
33126 Roo.bootstrap.SplitBar.VERTICAL = 1;
33127
33128 /**
33129  * Orientation constant - Create a horizontal SplitBar
33130  * @static
33131  * @type Number
33132  */
33133 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33134
33135 /**
33136  * Placement constant - The resizing element is to the left of the splitter element
33137  * @static
33138  * @type Number
33139  */
33140 Roo.bootstrap.SplitBar.LEFT = 1;
33141
33142 /**
33143  * Placement constant - The resizing element is to the right of the splitter element
33144  * @static
33145  * @type Number
33146  */
33147 Roo.bootstrap.SplitBar.RIGHT = 2;
33148
33149 /**
33150  * Placement constant - The resizing element is positioned above the splitter element
33151  * @static
33152  * @type Number
33153  */
33154 Roo.bootstrap.SplitBar.TOP = 3;
33155
33156 /**
33157  * Placement constant - The resizing element is positioned under splitter element
33158  * @static
33159  * @type Number
33160  */
33161 Roo.bootstrap.SplitBar.BOTTOM = 4;
33162 Roo.namespace("Roo.bootstrap.layout");/*
33163  * Based on:
33164  * Ext JS Library 1.1.1
33165  * Copyright(c) 2006-2007, Ext JS, LLC.
33166  *
33167  * Originally Released Under LGPL - original licence link has changed is not relivant.
33168  *
33169  * Fork - LGPL
33170  * <script type="text/javascript">
33171  */
33172
33173 /**
33174  * @class Roo.bootstrap.layout.Manager
33175  * @extends Roo.bootstrap.Component
33176  * Base class for layout managers.
33177  */
33178 Roo.bootstrap.layout.Manager = function(config)
33179 {
33180     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33181
33182
33183
33184
33185
33186     /** false to disable window resize monitoring @type Boolean */
33187     this.monitorWindowResize = true;
33188     this.regions = {};
33189     this.addEvents({
33190         /**
33191          * @event layout
33192          * Fires when a layout is performed.
33193          * @param {Roo.LayoutManager} this
33194          */
33195         "layout" : true,
33196         /**
33197          * @event regionresized
33198          * Fires when the user resizes a region.
33199          * @param {Roo.LayoutRegion} region The resized region
33200          * @param {Number} newSize The new size (width for east/west, height for north/south)
33201          */
33202         "regionresized" : true,
33203         /**
33204          * @event regioncollapsed
33205          * Fires when a region is collapsed.
33206          * @param {Roo.LayoutRegion} region The collapsed region
33207          */
33208         "regioncollapsed" : true,
33209         /**
33210          * @event regionexpanded
33211          * Fires when a region is expanded.
33212          * @param {Roo.LayoutRegion} region The expanded region
33213          */
33214         "regionexpanded" : true
33215     });
33216     this.updating = false;
33217
33218     if (config.el) {
33219         this.el = Roo.get(config.el);
33220         this.initEvents();
33221     }
33222
33223 };
33224
33225 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33226
33227
33228     regions : null,
33229
33230     monitorWindowResize : true,
33231
33232
33233     updating : false,
33234
33235
33236     onRender : function(ct, position)
33237     {
33238         if(!this.el){
33239             this.el = Roo.get(ct);
33240             this.initEvents();
33241         }
33242         //this.fireEvent('render',this);
33243     },
33244
33245
33246     initEvents: function()
33247     {
33248
33249
33250         // ie scrollbar fix
33251         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33252             document.body.scroll = "no";
33253         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33254             this.el.position('relative');
33255         }
33256         this.id = this.el.id;
33257         this.el.addClass("roo-layout-container");
33258         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33259         if(this.el.dom != document.body ) {
33260             this.el.on('resize', this.layout,this);
33261             this.el.on('show', this.layout,this);
33262         }
33263
33264     },
33265
33266     /**
33267      * Returns true if this layout is currently being updated
33268      * @return {Boolean}
33269      */
33270     isUpdating : function(){
33271         return this.updating;
33272     },
33273
33274     /**
33275      * Suspend the LayoutManager from doing auto-layouts while
33276      * making multiple add or remove calls
33277      */
33278     beginUpdate : function(){
33279         this.updating = true;
33280     },
33281
33282     /**
33283      * Restore auto-layouts and optionally disable the manager from performing a layout
33284      * @param {Boolean} noLayout true to disable a layout update
33285      */
33286     endUpdate : function(noLayout){
33287         this.updating = false;
33288         if(!noLayout){
33289             this.layout();
33290         }
33291     },
33292
33293     layout: function(){
33294         // abstract...
33295     },
33296
33297     onRegionResized : function(region, newSize){
33298         this.fireEvent("regionresized", region, newSize);
33299         this.layout();
33300     },
33301
33302     onRegionCollapsed : function(region){
33303         this.fireEvent("regioncollapsed", region);
33304     },
33305
33306     onRegionExpanded : function(region){
33307         this.fireEvent("regionexpanded", region);
33308     },
33309
33310     /**
33311      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33312      * performs box-model adjustments.
33313      * @return {Object} The size as an object {width: (the width), height: (the height)}
33314      */
33315     getViewSize : function()
33316     {
33317         var size;
33318         if(this.el.dom != document.body){
33319             size = this.el.getSize();
33320         }else{
33321             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33322         }
33323         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33324         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33325         return size;
33326     },
33327
33328     /**
33329      * Returns the Element this layout is bound to.
33330      * @return {Roo.Element}
33331      */
33332     getEl : function(){
33333         return this.el;
33334     },
33335
33336     /**
33337      * Returns the specified region.
33338      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33339      * @return {Roo.LayoutRegion}
33340      */
33341     getRegion : function(target){
33342         return this.regions[target.toLowerCase()];
33343     },
33344
33345     onWindowResize : function(){
33346         if(this.monitorWindowResize){
33347             this.layout();
33348         }
33349     }
33350 });
33351 /*
33352  * Based on:
33353  * Ext JS Library 1.1.1
33354  * Copyright(c) 2006-2007, Ext JS, LLC.
33355  *
33356  * Originally Released Under LGPL - original licence link has changed is not relivant.
33357  *
33358  * Fork - LGPL
33359  * <script type="text/javascript">
33360  */
33361 /**
33362  * @class Roo.bootstrap.layout.Border
33363  * @extends Roo.bootstrap.layout.Manager
33364  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33365  * please see: examples/bootstrap/nested.html<br><br>
33366  
33367 <b>The container the layout is rendered into can be either the body element or any other element.
33368 If it is not the body element, the container needs to either be an absolute positioned element,
33369 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33370 the container size if it is not the body element.</b>
33371
33372 * @constructor
33373 * Create a new Border
33374 * @param {Object} config Configuration options
33375  */
33376 Roo.bootstrap.layout.Border = function(config){
33377     config = config || {};
33378     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
33379     
33380     
33381     
33382     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33383         if(config[region]){
33384             config[region].region = region;
33385             this.addRegion(config[region]);
33386         }
33387     },this);
33388     
33389 };
33390
33391 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
33392
33393 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
33394     /**
33395      * Creates and adds a new region if it doesn't already exist.
33396      * @param {String} target The target region key (north, south, east, west or center).
33397      * @param {Object} config The regions config object
33398      * @return {BorderLayoutRegion} The new region
33399      */
33400     addRegion : function(config)
33401     {
33402         if(!this.regions[config.region]){
33403             var r = this.factory(config);
33404             this.bindRegion(r);
33405         }
33406         return this.regions[config.region];
33407     },
33408
33409     // private (kinda)
33410     bindRegion : function(r){
33411         this.regions[r.config.region] = r;
33412         
33413         r.on("visibilitychange",    this.layout, this);
33414         r.on("paneladded",          this.layout, this);
33415         r.on("panelremoved",        this.layout, this);
33416         r.on("invalidated",         this.layout, this);
33417         r.on("resized",             this.onRegionResized, this);
33418         r.on("collapsed",           this.onRegionCollapsed, this);
33419         r.on("expanded",            this.onRegionExpanded, this);
33420     },
33421
33422     /**
33423      * Performs a layout update.
33424      */
33425     layout : function()
33426     {
33427         if(this.updating) {
33428             return;
33429         }
33430         
33431         // render all the rebions if they have not been done alreayd?
33432         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33433             if(this.regions[region] && !this.regions[region].bodyEl){
33434                 this.regions[region].onRender(this.el)
33435             }
33436         },this);
33437         
33438         var size = this.getViewSize();
33439         var w = size.width;
33440         var h = size.height;
33441         var centerW = w;
33442         var centerH = h;
33443         var centerY = 0;
33444         var centerX = 0;
33445         //var x = 0, y = 0;
33446
33447         var rs = this.regions;
33448         var north = rs["north"];
33449         var south = rs["south"]; 
33450         var west = rs["west"];
33451         var east = rs["east"];
33452         var center = rs["center"];
33453         //if(this.hideOnLayout){ // not supported anymore
33454             //c.el.setStyle("display", "none");
33455         //}
33456         if(north && north.isVisible()){
33457             var b = north.getBox();
33458             var m = north.getMargins();
33459             b.width = w - (m.left+m.right);
33460             b.x = m.left;
33461             b.y = m.top;
33462             centerY = b.height + b.y + m.bottom;
33463             centerH -= centerY;
33464             north.updateBox(this.safeBox(b));
33465         }
33466         if(south && south.isVisible()){
33467             var b = south.getBox();
33468             var m = south.getMargins();
33469             b.width = w - (m.left+m.right);
33470             b.x = m.left;
33471             var totalHeight = (b.height + m.top + m.bottom);
33472             b.y = h - totalHeight + m.top;
33473             centerH -= totalHeight;
33474             south.updateBox(this.safeBox(b));
33475         }
33476         if(west && west.isVisible()){
33477             var b = west.getBox();
33478             var m = west.getMargins();
33479             b.height = centerH - (m.top+m.bottom);
33480             b.x = m.left;
33481             b.y = centerY + m.top;
33482             var totalWidth = (b.width + m.left + m.right);
33483             centerX += totalWidth;
33484             centerW -= totalWidth;
33485             west.updateBox(this.safeBox(b));
33486         }
33487         if(east && east.isVisible()){
33488             var b = east.getBox();
33489             var m = east.getMargins();
33490             b.height = centerH - (m.top+m.bottom);
33491             var totalWidth = (b.width + m.left + m.right);
33492             b.x = w - totalWidth + m.left;
33493             b.y = centerY + m.top;
33494             centerW -= totalWidth;
33495             east.updateBox(this.safeBox(b));
33496         }
33497         if(center){
33498             var m = center.getMargins();
33499             var centerBox = {
33500                 x: centerX + m.left,
33501                 y: centerY + m.top,
33502                 width: centerW - (m.left+m.right),
33503                 height: centerH - (m.top+m.bottom)
33504             };
33505             //if(this.hideOnLayout){
33506                 //center.el.setStyle("display", "block");
33507             //}
33508             center.updateBox(this.safeBox(centerBox));
33509         }
33510         this.el.repaint();
33511         this.fireEvent("layout", this);
33512     },
33513
33514     // private
33515     safeBox : function(box){
33516         box.width = Math.max(0, box.width);
33517         box.height = Math.max(0, box.height);
33518         return box;
33519     },
33520
33521     /**
33522      * Adds a ContentPanel (or subclass) to this layout.
33523      * @param {String} target The target region key (north, south, east, west or center).
33524      * @param {Roo.ContentPanel} panel The panel to add
33525      * @return {Roo.ContentPanel} The added panel
33526      */
33527     add : function(target, panel){
33528          
33529         target = target.toLowerCase();
33530         return this.regions[target].add(panel);
33531     },
33532
33533     /**
33534      * Remove a ContentPanel (or subclass) to this layout.
33535      * @param {String} target The target region key (north, south, east, west or center).
33536      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33537      * @return {Roo.ContentPanel} The removed panel
33538      */
33539     remove : function(target, panel){
33540         target = target.toLowerCase();
33541         return this.regions[target].remove(panel);
33542     },
33543
33544     /**
33545      * Searches all regions for a panel with the specified id
33546      * @param {String} panelId
33547      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33548      */
33549     findPanel : function(panelId){
33550         var rs = this.regions;
33551         for(var target in rs){
33552             if(typeof rs[target] != "function"){
33553                 var p = rs[target].getPanel(panelId);
33554                 if(p){
33555                     return p;
33556                 }
33557             }
33558         }
33559         return null;
33560     },
33561
33562     /**
33563      * Searches all regions for a panel with the specified id and activates (shows) it.
33564      * @param {String/ContentPanel} panelId The panels id or the panel itself
33565      * @return {Roo.ContentPanel} The shown panel or null
33566      */
33567     showPanel : function(panelId) {
33568       var rs = this.regions;
33569       for(var target in rs){
33570          var r = rs[target];
33571          if(typeof r != "function"){
33572             if(r.hasPanel(panelId)){
33573                return r.showPanel(panelId);
33574             }
33575          }
33576       }
33577       return null;
33578    },
33579
33580    /**
33581      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33582      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33583      */
33584    /*
33585     restoreState : function(provider){
33586         if(!provider){
33587             provider = Roo.state.Manager;
33588         }
33589         var sm = new Roo.LayoutStateManager();
33590         sm.init(this, provider);
33591     },
33592 */
33593  
33594  
33595     /**
33596      * Adds a xtype elements to the layout.
33597      * <pre><code>
33598
33599 layout.addxtype({
33600        xtype : 'ContentPanel',
33601        region: 'west',
33602        items: [ .... ]
33603    }
33604 );
33605
33606 layout.addxtype({
33607         xtype : 'NestedLayoutPanel',
33608         region: 'west',
33609         layout: {
33610            center: { },
33611            west: { }   
33612         },
33613         items : [ ... list of content panels or nested layout panels.. ]
33614    }
33615 );
33616 </code></pre>
33617      * @param {Object} cfg Xtype definition of item to add.
33618      */
33619     addxtype : function(cfg)
33620     {
33621         // basically accepts a pannel...
33622         // can accept a layout region..!?!?
33623         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33624         
33625         
33626         // theory?  children can only be panels??
33627         
33628         //if (!cfg.xtype.match(/Panel$/)) {
33629         //    return false;
33630         //}
33631         var ret = false;
33632         
33633         if (typeof(cfg.region) == 'undefined') {
33634             Roo.log("Failed to add Panel, region was not set");
33635             Roo.log(cfg);
33636             return false;
33637         }
33638         var region = cfg.region;
33639         delete cfg.region;
33640         
33641           
33642         var xitems = [];
33643         if (cfg.items) {
33644             xitems = cfg.items;
33645             delete cfg.items;
33646         }
33647         var nb = false;
33648         
33649         switch(cfg.xtype) 
33650         {
33651             case 'Content':  // ContentPanel (el, cfg)
33652             case 'Scroll':  // ContentPanel (el, cfg)
33653             case 'View': 
33654                 cfg.autoCreate = true;
33655                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33656                 //} else {
33657                 //    var el = this.el.createChild();
33658                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33659                 //}
33660                 
33661                 this.add(region, ret);
33662                 break;
33663             
33664             /*
33665             case 'TreePanel': // our new panel!
33666                 cfg.el = this.el.createChild();
33667                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33668                 this.add(region, ret);
33669                 break;
33670             */
33671             
33672             case 'Nest': 
33673                 // create a new Layout (which is  a Border Layout...
33674                 
33675                 var clayout = cfg.layout;
33676                 clayout.el  = this.el.createChild();
33677                 clayout.items   = clayout.items  || [];
33678                 
33679                 delete cfg.layout;
33680                 
33681                 // replace this exitems with the clayout ones..
33682                 xitems = clayout.items;
33683                  
33684                 // force background off if it's in center...
33685                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33686                     cfg.background = false;
33687                 }
33688                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
33689                 
33690                 
33691                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33692                 //console.log('adding nested layout panel '  + cfg.toSource());
33693                 this.add(region, ret);
33694                 nb = {}; /// find first...
33695                 break;
33696             
33697             case 'Grid':
33698                 
33699                 // needs grid and region
33700                 
33701                 //var el = this.getRegion(region).el.createChild();
33702                 /*
33703                  *var el = this.el.createChild();
33704                 // create the grid first...
33705                 cfg.grid.container = el;
33706                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33707                 */
33708                 
33709                 if (region == 'center' && this.active ) {
33710                     cfg.background = false;
33711                 }
33712                 
33713                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33714                 
33715                 this.add(region, ret);
33716                 /*
33717                 if (cfg.background) {
33718                     // render grid on panel activation (if panel background)
33719                     ret.on('activate', function(gp) {
33720                         if (!gp.grid.rendered) {
33721                     //        gp.grid.render(el);
33722                         }
33723                     });
33724                 } else {
33725                   //  cfg.grid.render(el);
33726                 }
33727                 */
33728                 break;
33729            
33730            
33731             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33732                 // it was the old xcomponent building that caused this before.
33733                 // espeically if border is the top element in the tree.
33734                 ret = this;
33735                 break; 
33736                 
33737                     
33738                 
33739                 
33740                 
33741             default:
33742                 /*
33743                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33744                     
33745                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33746                     this.add(region, ret);
33747                 } else {
33748                 */
33749                     Roo.log(cfg);
33750                     throw "Can not add '" + cfg.xtype + "' to Border";
33751                     return null;
33752              
33753                                 
33754              
33755         }
33756         this.beginUpdate();
33757         // add children..
33758         var region = '';
33759         var abn = {};
33760         Roo.each(xitems, function(i)  {
33761             region = nb && i.region ? i.region : false;
33762             
33763             var add = ret.addxtype(i);
33764            
33765             if (region) {
33766                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33767                 if (!i.background) {
33768                     abn[region] = nb[region] ;
33769                 }
33770             }
33771             
33772         });
33773         this.endUpdate();
33774
33775         // make the last non-background panel active..
33776         //if (nb) { Roo.log(abn); }
33777         if (nb) {
33778             
33779             for(var r in abn) {
33780                 region = this.getRegion(r);
33781                 if (region) {
33782                     // tried using nb[r], but it does not work..
33783                      
33784                     region.showPanel(abn[r]);
33785                    
33786                 }
33787             }
33788         }
33789         return ret;
33790         
33791     },
33792     
33793     
33794 // private
33795     factory : function(cfg)
33796     {
33797         
33798         var validRegions = Roo.bootstrap.layout.Border.regions;
33799
33800         var target = cfg.region;
33801         cfg.mgr = this;
33802         
33803         var r = Roo.bootstrap.layout;
33804         Roo.log(target);
33805         switch(target){
33806             case "north":
33807                 return new r.North(cfg);
33808             case "south":
33809                 return new r.South(cfg);
33810             case "east":
33811                 return new r.East(cfg);
33812             case "west":
33813                 return new r.West(cfg);
33814             case "center":
33815                 return new r.Center(cfg);
33816         }
33817         throw 'Layout region "'+target+'" not supported.';
33818     }
33819     
33820     
33821 });
33822  /*
33823  * Based on:
33824  * Ext JS Library 1.1.1
33825  * Copyright(c) 2006-2007, Ext JS, LLC.
33826  *
33827  * Originally Released Under LGPL - original licence link has changed is not relivant.
33828  *
33829  * Fork - LGPL
33830  * <script type="text/javascript">
33831  */
33832  
33833 /**
33834  * @class Roo.bootstrap.layout.Basic
33835  * @extends Roo.util.Observable
33836  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33837  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33838  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33839  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33840  * @cfg {string}   region  the region that it inhabits..
33841  * @cfg {bool}   skipConfig skip config?
33842  * 
33843
33844  */
33845 Roo.bootstrap.layout.Basic = function(config){
33846     
33847     this.mgr = config.mgr;
33848     
33849     this.position = config.region;
33850     
33851     var skipConfig = config.skipConfig;
33852     
33853     this.events = {
33854         /**
33855          * @scope Roo.BasicLayoutRegion
33856          */
33857         
33858         /**
33859          * @event beforeremove
33860          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33861          * @param {Roo.LayoutRegion} this
33862          * @param {Roo.ContentPanel} panel The panel
33863          * @param {Object} e The cancel event object
33864          */
33865         "beforeremove" : true,
33866         /**
33867          * @event invalidated
33868          * Fires when the layout for this region is changed.
33869          * @param {Roo.LayoutRegion} this
33870          */
33871         "invalidated" : true,
33872         /**
33873          * @event visibilitychange
33874          * Fires when this region is shown or hidden 
33875          * @param {Roo.LayoutRegion} this
33876          * @param {Boolean} visibility true or false
33877          */
33878         "visibilitychange" : true,
33879         /**
33880          * @event paneladded
33881          * Fires when a panel is added. 
33882          * @param {Roo.LayoutRegion} this
33883          * @param {Roo.ContentPanel} panel The panel
33884          */
33885         "paneladded" : true,
33886         /**
33887          * @event panelremoved
33888          * Fires when a panel is removed. 
33889          * @param {Roo.LayoutRegion} this
33890          * @param {Roo.ContentPanel} panel The panel
33891          */
33892         "panelremoved" : true,
33893         /**
33894          * @event beforecollapse
33895          * Fires when this region before collapse.
33896          * @param {Roo.LayoutRegion} this
33897          */
33898         "beforecollapse" : true,
33899         /**
33900          * @event collapsed
33901          * Fires when this region is collapsed.
33902          * @param {Roo.LayoutRegion} this
33903          */
33904         "collapsed" : true,
33905         /**
33906          * @event expanded
33907          * Fires when this region is expanded.
33908          * @param {Roo.LayoutRegion} this
33909          */
33910         "expanded" : true,
33911         /**
33912          * @event slideshow
33913          * Fires when this region is slid into view.
33914          * @param {Roo.LayoutRegion} this
33915          */
33916         "slideshow" : true,
33917         /**
33918          * @event slidehide
33919          * Fires when this region slides out of view. 
33920          * @param {Roo.LayoutRegion} this
33921          */
33922         "slidehide" : true,
33923         /**
33924          * @event panelactivated
33925          * Fires when a panel is activated. 
33926          * @param {Roo.LayoutRegion} this
33927          * @param {Roo.ContentPanel} panel The activated panel
33928          */
33929         "panelactivated" : true,
33930         /**
33931          * @event resized
33932          * Fires when the user resizes this region. 
33933          * @param {Roo.LayoutRegion} this
33934          * @param {Number} newSize The new size (width for east/west, height for north/south)
33935          */
33936         "resized" : true
33937     };
33938     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33939     this.panels = new Roo.util.MixedCollection();
33940     this.panels.getKey = this.getPanelId.createDelegate(this);
33941     this.box = null;
33942     this.activePanel = null;
33943     // ensure listeners are added...
33944     
33945     if (config.listeners || config.events) {
33946         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33947             listeners : config.listeners || {},
33948             events : config.events || {}
33949         });
33950     }
33951     
33952     if(skipConfig !== true){
33953         this.applyConfig(config);
33954     }
33955 };
33956
33957 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33958 {
33959     getPanelId : function(p){
33960         return p.getId();
33961     },
33962     
33963     applyConfig : function(config){
33964         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33965         this.config = config;
33966         
33967     },
33968     
33969     /**
33970      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33971      * the width, for horizontal (north, south) the height.
33972      * @param {Number} newSize The new width or height
33973      */
33974     resizeTo : function(newSize){
33975         var el = this.el ? this.el :
33976                  (this.activePanel ? this.activePanel.getEl() : null);
33977         if(el){
33978             switch(this.position){
33979                 case "east":
33980                 case "west":
33981                     el.setWidth(newSize);
33982                     this.fireEvent("resized", this, newSize);
33983                 break;
33984                 case "north":
33985                 case "south":
33986                     el.setHeight(newSize);
33987                     this.fireEvent("resized", this, newSize);
33988                 break;                
33989             }
33990         }
33991     },
33992     
33993     getBox : function(){
33994         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33995     },
33996     
33997     getMargins : function(){
33998         return this.margins;
33999     },
34000     
34001     updateBox : function(box){
34002         this.box = box;
34003         var el = this.activePanel.getEl();
34004         el.dom.style.left = box.x + "px";
34005         el.dom.style.top = box.y + "px";
34006         this.activePanel.setSize(box.width, box.height);
34007     },
34008     
34009     /**
34010      * Returns the container element for this region.
34011      * @return {Roo.Element}
34012      */
34013     getEl : function(){
34014         return this.activePanel;
34015     },
34016     
34017     /**
34018      * Returns true if this region is currently visible.
34019      * @return {Boolean}
34020      */
34021     isVisible : function(){
34022         return this.activePanel ? true : false;
34023     },
34024     
34025     setActivePanel : function(panel){
34026         panel = this.getPanel(panel);
34027         if(this.activePanel && this.activePanel != panel){
34028             this.activePanel.setActiveState(false);
34029             this.activePanel.getEl().setLeftTop(-10000,-10000);
34030         }
34031         this.activePanel = panel;
34032         panel.setActiveState(true);
34033         if(this.box){
34034             panel.setSize(this.box.width, this.box.height);
34035         }
34036         this.fireEvent("panelactivated", this, panel);
34037         this.fireEvent("invalidated");
34038     },
34039     
34040     /**
34041      * Show the specified panel.
34042      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34043      * @return {Roo.ContentPanel} The shown panel or null
34044      */
34045     showPanel : function(panel){
34046         panel = this.getPanel(panel);
34047         if(panel){
34048             this.setActivePanel(panel);
34049         }
34050         return panel;
34051     },
34052     
34053     /**
34054      * Get the active panel for this region.
34055      * @return {Roo.ContentPanel} The active panel or null
34056      */
34057     getActivePanel : function(){
34058         return this.activePanel;
34059     },
34060     
34061     /**
34062      * Add the passed ContentPanel(s)
34063      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34064      * @return {Roo.ContentPanel} The panel added (if only one was added)
34065      */
34066     add : function(panel){
34067         if(arguments.length > 1){
34068             for(var i = 0, len = arguments.length; i < len; i++) {
34069                 this.add(arguments[i]);
34070             }
34071             return null;
34072         }
34073         if(this.hasPanel(panel)){
34074             this.showPanel(panel);
34075             return panel;
34076         }
34077         var el = panel.getEl();
34078         if(el.dom.parentNode != this.mgr.el.dom){
34079             this.mgr.el.dom.appendChild(el.dom);
34080         }
34081         if(panel.setRegion){
34082             panel.setRegion(this);
34083         }
34084         this.panels.add(panel);
34085         el.setStyle("position", "absolute");
34086         if(!panel.background){
34087             this.setActivePanel(panel);
34088             if(this.config.initialSize && this.panels.getCount()==1){
34089                 this.resizeTo(this.config.initialSize);
34090             }
34091         }
34092         this.fireEvent("paneladded", this, panel);
34093         return panel;
34094     },
34095     
34096     /**
34097      * Returns true if the panel is in this region.
34098      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34099      * @return {Boolean}
34100      */
34101     hasPanel : function(panel){
34102         if(typeof panel == "object"){ // must be panel obj
34103             panel = panel.getId();
34104         }
34105         return this.getPanel(panel) ? true : false;
34106     },
34107     
34108     /**
34109      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34110      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34111      * @param {Boolean} preservePanel Overrides the config preservePanel option
34112      * @return {Roo.ContentPanel} The panel that was removed
34113      */
34114     remove : function(panel, preservePanel){
34115         panel = this.getPanel(panel);
34116         if(!panel){
34117             return null;
34118         }
34119         var e = {};
34120         this.fireEvent("beforeremove", this, panel, e);
34121         if(e.cancel === true){
34122             return null;
34123         }
34124         var panelId = panel.getId();
34125         this.panels.removeKey(panelId);
34126         return panel;
34127     },
34128     
34129     /**
34130      * Returns the panel specified or null if it's not in this region.
34131      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34132      * @return {Roo.ContentPanel}
34133      */
34134     getPanel : function(id){
34135         if(typeof id == "object"){ // must be panel obj
34136             return id;
34137         }
34138         return this.panels.get(id);
34139     },
34140     
34141     /**
34142      * Returns this regions position (north/south/east/west/center).
34143      * @return {String} 
34144      */
34145     getPosition: function(){
34146         return this.position;    
34147     }
34148 });/*
34149  * Based on:
34150  * Ext JS Library 1.1.1
34151  * Copyright(c) 2006-2007, Ext JS, LLC.
34152  *
34153  * Originally Released Under LGPL - original licence link has changed is not relivant.
34154  *
34155  * Fork - LGPL
34156  * <script type="text/javascript">
34157  */
34158  
34159 /**
34160  * @class Roo.bootstrap.layout.Region
34161  * @extends Roo.bootstrap.layout.Basic
34162  * This class represents a region in a layout manager.
34163  
34164  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34165  * @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})
34166  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34167  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34168  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34169  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34170  * @cfg {String}    title           The title for the region (overrides panel titles)
34171  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34172  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34173  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34174  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34175  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34176  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34177  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34178  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34179  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34180  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34181
34182  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34183  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34184  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34185  * @cfg {Number}    width           For East/West panels
34186  * @cfg {Number}    height          For North/South panels
34187  * @cfg {Boolean}   split           To show the splitter
34188  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34189  * 
34190  * @cfg {string}   cls             Extra CSS classes to add to region
34191  * 
34192  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34193  * @cfg {string}   region  the region that it inhabits..
34194  *
34195
34196  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34197  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34198
34199  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34200  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34201  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34202  */
34203 Roo.bootstrap.layout.Region = function(config)
34204 {
34205     this.applyConfig(config);
34206
34207     var mgr = config.mgr;
34208     var pos = config.region;
34209     config.skipConfig = true;
34210     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34211     
34212     if (mgr.el) {
34213         this.onRender(mgr.el);   
34214     }
34215      
34216     this.visible = true;
34217     this.collapsed = false;
34218     this.unrendered_panels = [];
34219 };
34220
34221 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34222
34223     position: '', // set by wrapper (eg. north/south etc..)
34224     unrendered_panels : null,  // unrendered panels.
34225     createBody : function(){
34226         /** This region's body element 
34227         * @type Roo.Element */
34228         this.bodyEl = this.el.createChild({
34229                 tag: "div",
34230                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34231         });
34232     },
34233
34234     onRender: function(ctr, pos)
34235     {
34236         var dh = Roo.DomHelper;
34237         /** This region's container element 
34238         * @type Roo.Element */
34239         this.el = dh.append(ctr.dom, {
34240                 tag: "div",
34241                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34242             }, true);
34243         /** This region's title element 
34244         * @type Roo.Element */
34245     
34246         this.titleEl = dh.append(this.el.dom,
34247             {
34248                     tag: "div",
34249                     unselectable: "on",
34250                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34251                     children:[
34252                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34253                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34254                     ]}, true);
34255         
34256         this.titleEl.enableDisplayMode();
34257         /** This region's title text element 
34258         * @type HTMLElement */
34259         this.titleTextEl = this.titleEl.dom.firstChild;
34260         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34261         /*
34262         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34263         this.closeBtn.enableDisplayMode();
34264         this.closeBtn.on("click", this.closeClicked, this);
34265         this.closeBtn.hide();
34266     */
34267         this.createBody(this.config);
34268         if(this.config.hideWhenEmpty){
34269             this.hide();
34270             this.on("paneladded", this.validateVisibility, this);
34271             this.on("panelremoved", this.validateVisibility, this);
34272         }
34273         if(this.autoScroll){
34274             this.bodyEl.setStyle("overflow", "auto");
34275         }else{
34276             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34277         }
34278         //if(c.titlebar !== false){
34279             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34280                 this.titleEl.hide();
34281             }else{
34282                 this.titleEl.show();
34283                 if(this.config.title){
34284                     this.titleTextEl.innerHTML = this.config.title;
34285                 }
34286             }
34287         //}
34288         if(this.config.collapsed){
34289             this.collapse(true);
34290         }
34291         if(this.config.hidden){
34292             this.hide();
34293         }
34294         
34295         if (this.unrendered_panels && this.unrendered_panels.length) {
34296             for (var i =0;i< this.unrendered_panels.length; i++) {
34297                 this.add(this.unrendered_panels[i]);
34298             }
34299             this.unrendered_panels = null;
34300             
34301         }
34302         
34303     },
34304     
34305     applyConfig : function(c)
34306     {
34307         /*
34308          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34309             var dh = Roo.DomHelper;
34310             if(c.titlebar !== false){
34311                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34312                 this.collapseBtn.on("click", this.collapse, this);
34313                 this.collapseBtn.enableDisplayMode();
34314                 /*
34315                 if(c.showPin === true || this.showPin){
34316                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34317                     this.stickBtn.enableDisplayMode();
34318                     this.stickBtn.on("click", this.expand, this);
34319                     this.stickBtn.hide();
34320                 }
34321                 
34322             }
34323             */
34324             /** This region's collapsed element
34325             * @type Roo.Element */
34326             /*
34327              *
34328             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34329                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34330             ]}, true);
34331             
34332             if(c.floatable !== false){
34333                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34334                this.collapsedEl.on("click", this.collapseClick, this);
34335             }
34336
34337             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34338                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34339                    id: "message", unselectable: "on", style:{"float":"left"}});
34340                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34341              }
34342             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34343             this.expandBtn.on("click", this.expand, this);
34344             
34345         }
34346         
34347         if(this.collapseBtn){
34348             this.collapseBtn.setVisible(c.collapsible == true);
34349         }
34350         
34351         this.cmargins = c.cmargins || this.cmargins ||
34352                          (this.position == "west" || this.position == "east" ?
34353                              {top: 0, left: 2, right:2, bottom: 0} :
34354                              {top: 2, left: 0, right:0, bottom: 2});
34355         */
34356         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34357         
34358         
34359         this.bottomTabs = c.tabPosition != "top";
34360         
34361         this.autoScroll = c.autoScroll || false;
34362         
34363         
34364        
34365         
34366         this.duration = c.duration || .30;
34367         this.slideDuration = c.slideDuration || .45;
34368         this.config = c;
34369        
34370     },
34371     /**
34372      * Returns true if this region is currently visible.
34373      * @return {Boolean}
34374      */
34375     isVisible : function(){
34376         return this.visible;
34377     },
34378
34379     /**
34380      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34381      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34382      */
34383     //setCollapsedTitle : function(title){
34384     //    title = title || "&#160;";
34385      //   if(this.collapsedTitleTextEl){
34386       //      this.collapsedTitleTextEl.innerHTML = title;
34387        // }
34388     //},
34389
34390     getBox : function(){
34391         var b;
34392       //  if(!this.collapsed){
34393             b = this.el.getBox(false, true);
34394        // }else{
34395           //  b = this.collapsedEl.getBox(false, true);
34396         //}
34397         return b;
34398     },
34399
34400     getMargins : function(){
34401         return this.margins;
34402         //return this.collapsed ? this.cmargins : this.margins;
34403     },
34404 /*
34405     highlight : function(){
34406         this.el.addClass("x-layout-panel-dragover");
34407     },
34408
34409     unhighlight : function(){
34410         this.el.removeClass("x-layout-panel-dragover");
34411     },
34412 */
34413     updateBox : function(box)
34414     {
34415         if (!this.bodyEl) {
34416             return; // not rendered yet..
34417         }
34418         
34419         this.box = box;
34420         if(!this.collapsed){
34421             this.el.dom.style.left = box.x + "px";
34422             this.el.dom.style.top = box.y + "px";
34423             this.updateBody(box.width, box.height);
34424         }else{
34425             this.collapsedEl.dom.style.left = box.x + "px";
34426             this.collapsedEl.dom.style.top = box.y + "px";
34427             this.collapsedEl.setSize(box.width, box.height);
34428         }
34429         if(this.tabs){
34430             this.tabs.autoSizeTabs();
34431         }
34432     },
34433
34434     updateBody : function(w, h)
34435     {
34436         if(w !== null){
34437             this.el.setWidth(w);
34438             w -= this.el.getBorderWidth("rl");
34439             if(this.config.adjustments){
34440                 w += this.config.adjustments[0];
34441             }
34442         }
34443         if(h !== null && h > 0){
34444             this.el.setHeight(h);
34445             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34446             h -= this.el.getBorderWidth("tb");
34447             if(this.config.adjustments){
34448                 h += this.config.adjustments[1];
34449             }
34450             this.bodyEl.setHeight(h);
34451             if(this.tabs){
34452                 h = this.tabs.syncHeight(h);
34453             }
34454         }
34455         if(this.panelSize){
34456             w = w !== null ? w : this.panelSize.width;
34457             h = h !== null ? h : this.panelSize.height;
34458         }
34459         if(this.activePanel){
34460             var el = this.activePanel.getEl();
34461             w = w !== null ? w : el.getWidth();
34462             h = h !== null ? h : el.getHeight();
34463             this.panelSize = {width: w, height: h};
34464             this.activePanel.setSize(w, h);
34465         }
34466         if(Roo.isIE && this.tabs){
34467             this.tabs.el.repaint();
34468         }
34469     },
34470
34471     /**
34472      * Returns the container element for this region.
34473      * @return {Roo.Element}
34474      */
34475     getEl : function(){
34476         return this.el;
34477     },
34478
34479     /**
34480      * Hides this region.
34481      */
34482     hide : function(){
34483         //if(!this.collapsed){
34484             this.el.dom.style.left = "-2000px";
34485             this.el.hide();
34486         //}else{
34487          //   this.collapsedEl.dom.style.left = "-2000px";
34488          //   this.collapsedEl.hide();
34489        // }
34490         this.visible = false;
34491         this.fireEvent("visibilitychange", this, false);
34492     },
34493
34494     /**
34495      * Shows this region if it was previously hidden.
34496      */
34497     show : function(){
34498         //if(!this.collapsed){
34499             this.el.show();
34500         //}else{
34501         //    this.collapsedEl.show();
34502        // }
34503         this.visible = true;
34504         this.fireEvent("visibilitychange", this, true);
34505     },
34506 /*
34507     closeClicked : function(){
34508         if(this.activePanel){
34509             this.remove(this.activePanel);
34510         }
34511     },
34512
34513     collapseClick : function(e){
34514         if(this.isSlid){
34515            e.stopPropagation();
34516            this.slideIn();
34517         }else{
34518            e.stopPropagation();
34519            this.slideOut();
34520         }
34521     },
34522 */
34523     /**
34524      * Collapses this region.
34525      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34526      */
34527     /*
34528     collapse : function(skipAnim, skipCheck = false){
34529         if(this.collapsed) {
34530             return;
34531         }
34532         
34533         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34534             
34535             this.collapsed = true;
34536             if(this.split){
34537                 this.split.el.hide();
34538             }
34539             if(this.config.animate && skipAnim !== true){
34540                 this.fireEvent("invalidated", this);
34541                 this.animateCollapse();
34542             }else{
34543                 this.el.setLocation(-20000,-20000);
34544                 this.el.hide();
34545                 this.collapsedEl.show();
34546                 this.fireEvent("collapsed", this);
34547                 this.fireEvent("invalidated", this);
34548             }
34549         }
34550         
34551     },
34552 */
34553     animateCollapse : function(){
34554         // overridden
34555     },
34556
34557     /**
34558      * Expands this region if it was previously collapsed.
34559      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34560      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34561      */
34562     /*
34563     expand : function(e, skipAnim){
34564         if(e) {
34565             e.stopPropagation();
34566         }
34567         if(!this.collapsed || this.el.hasActiveFx()) {
34568             return;
34569         }
34570         if(this.isSlid){
34571             this.afterSlideIn();
34572             skipAnim = true;
34573         }
34574         this.collapsed = false;
34575         if(this.config.animate && skipAnim !== true){
34576             this.animateExpand();
34577         }else{
34578             this.el.show();
34579             if(this.split){
34580                 this.split.el.show();
34581             }
34582             this.collapsedEl.setLocation(-2000,-2000);
34583             this.collapsedEl.hide();
34584             this.fireEvent("invalidated", this);
34585             this.fireEvent("expanded", this);
34586         }
34587     },
34588 */
34589     animateExpand : function(){
34590         // overridden
34591     },
34592
34593     initTabs : function()
34594     {
34595         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
34596         
34597         var ts = new Roo.bootstrap.panel.Tabs({
34598                 el: this.bodyEl.dom,
34599                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
34600                 disableTooltips: this.config.disableTabTips,
34601                 toolbar : this.config.toolbar
34602             });
34603         
34604         if(this.config.hideTabs){
34605             ts.stripWrap.setDisplayed(false);
34606         }
34607         this.tabs = ts;
34608         ts.resizeTabs = this.config.resizeTabs === true;
34609         ts.minTabWidth = this.config.minTabWidth || 40;
34610         ts.maxTabWidth = this.config.maxTabWidth || 250;
34611         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34612         ts.monitorResize = false;
34613         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
34614         ts.bodyEl.addClass('roo-layout-tabs-body');
34615         this.panels.each(this.initPanelAsTab, this);
34616     },
34617
34618     initPanelAsTab : function(panel){
34619         var ti = this.tabs.addTab(
34620             panel.getEl().id,
34621             panel.getTitle(),
34622             null,
34623             this.config.closeOnTab && panel.isClosable(),
34624             panel.tpl
34625         );
34626         if(panel.tabTip !== undefined){
34627             ti.setTooltip(panel.tabTip);
34628         }
34629         ti.on("activate", function(){
34630               this.setActivePanel(panel);
34631         }, this);
34632         
34633         if(this.config.closeOnTab){
34634             ti.on("beforeclose", function(t, e){
34635                 e.cancel = true;
34636                 this.remove(panel);
34637             }, this);
34638         }
34639         
34640         panel.tabItem = ti;
34641         
34642         return ti;
34643     },
34644
34645     updatePanelTitle : function(panel, title)
34646     {
34647         if(this.activePanel == panel){
34648             this.updateTitle(title);
34649         }
34650         if(this.tabs){
34651             var ti = this.tabs.getTab(panel.getEl().id);
34652             ti.setText(title);
34653             if(panel.tabTip !== undefined){
34654                 ti.setTooltip(panel.tabTip);
34655             }
34656         }
34657     },
34658
34659     updateTitle : function(title){
34660         if(this.titleTextEl && !this.config.title){
34661             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34662         }
34663     },
34664
34665     setActivePanel : function(panel)
34666     {
34667         panel = this.getPanel(panel);
34668         if(this.activePanel && this.activePanel != panel){
34669             this.activePanel.setActiveState(false);
34670         }
34671         this.activePanel = panel;
34672         panel.setActiveState(true);
34673         if(this.panelSize){
34674             panel.setSize(this.panelSize.width, this.panelSize.height);
34675         }
34676         if(this.closeBtn){
34677             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34678         }
34679         this.updateTitle(panel.getTitle());
34680         if(this.tabs){
34681             this.fireEvent("invalidated", this);
34682         }
34683         this.fireEvent("panelactivated", this, panel);
34684     },
34685
34686     /**
34687      * Shows the specified panel.
34688      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34689      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34690      */
34691     showPanel : function(panel)
34692     {
34693         panel = this.getPanel(panel);
34694         if(panel){
34695             if(this.tabs){
34696                 var tab = this.tabs.getTab(panel.getEl().id);
34697                 if(tab.isHidden()){
34698                     this.tabs.unhideTab(tab.id);
34699                 }
34700                 tab.activate();
34701             }else{
34702                 this.setActivePanel(panel);
34703             }
34704         }
34705         return panel;
34706     },
34707
34708     /**
34709      * Get the active panel for this region.
34710      * @return {Roo.ContentPanel} The active panel or null
34711      */
34712     getActivePanel : function(){
34713         return this.activePanel;
34714     },
34715
34716     validateVisibility : function(){
34717         if(this.panels.getCount() < 1){
34718             this.updateTitle("&#160;");
34719             this.closeBtn.hide();
34720             this.hide();
34721         }else{
34722             if(!this.isVisible()){
34723                 this.show();
34724             }
34725         }
34726     },
34727
34728     /**
34729      * Adds the passed ContentPanel(s) to this region.
34730      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34731      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34732      */
34733     add : function(panel)
34734     {
34735         if(arguments.length > 1){
34736             for(var i = 0, len = arguments.length; i < len; i++) {
34737                 this.add(arguments[i]);
34738             }
34739             return null;
34740         }
34741         
34742         // if we have not been rendered yet, then we can not really do much of this..
34743         if (!this.bodyEl) {
34744             this.unrendered_panels.push(panel);
34745             return panel;
34746         }
34747         
34748         
34749         
34750         
34751         if(this.hasPanel(panel)){
34752             this.showPanel(panel);
34753             return panel;
34754         }
34755         panel.setRegion(this);
34756         this.panels.add(panel);
34757        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34758             // sinle panel - no tab...?? would it not be better to render it with the tabs,
34759             // and hide them... ???
34760             this.bodyEl.dom.appendChild(panel.getEl().dom);
34761             if(panel.background !== true){
34762                 this.setActivePanel(panel);
34763             }
34764             this.fireEvent("paneladded", this, panel);
34765             return panel;
34766         }
34767         */
34768         if(!this.tabs){
34769             this.initTabs();
34770         }else{
34771             this.initPanelAsTab(panel);
34772         }
34773         
34774         
34775         if(panel.background !== true){
34776             this.tabs.activate(panel.getEl().id);
34777         }
34778         this.fireEvent("paneladded", this, panel);
34779         return panel;
34780     },
34781
34782     /**
34783      * Hides the tab for the specified panel.
34784      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34785      */
34786     hidePanel : function(panel){
34787         if(this.tabs && (panel = this.getPanel(panel))){
34788             this.tabs.hideTab(panel.getEl().id);
34789         }
34790     },
34791
34792     /**
34793      * Unhides the tab for a previously hidden panel.
34794      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34795      */
34796     unhidePanel : function(panel){
34797         if(this.tabs && (panel = this.getPanel(panel))){
34798             this.tabs.unhideTab(panel.getEl().id);
34799         }
34800     },
34801
34802     clearPanels : function(){
34803         while(this.panels.getCount() > 0){
34804              this.remove(this.panels.first());
34805         }
34806     },
34807
34808     /**
34809      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34810      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34811      * @param {Boolean} preservePanel Overrides the config preservePanel option
34812      * @return {Roo.ContentPanel} The panel that was removed
34813      */
34814     remove : function(panel, preservePanel)
34815     {
34816         panel = this.getPanel(panel);
34817         if(!panel){
34818             return null;
34819         }
34820         var e = {};
34821         this.fireEvent("beforeremove", this, panel, e);
34822         if(e.cancel === true){
34823             return null;
34824         }
34825         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34826         var panelId = panel.getId();
34827         this.panels.removeKey(panelId);
34828         if(preservePanel){
34829             document.body.appendChild(panel.getEl().dom);
34830         }
34831         if(this.tabs){
34832             this.tabs.removeTab(panel.getEl().id);
34833         }else if (!preservePanel){
34834             this.bodyEl.dom.removeChild(panel.getEl().dom);
34835         }
34836         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34837             var p = this.panels.first();
34838             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34839             tempEl.appendChild(p.getEl().dom);
34840             this.bodyEl.update("");
34841             this.bodyEl.dom.appendChild(p.getEl().dom);
34842             tempEl = null;
34843             this.updateTitle(p.getTitle());
34844             this.tabs = null;
34845             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34846             this.setActivePanel(p);
34847         }
34848         panel.setRegion(null);
34849         if(this.activePanel == panel){
34850             this.activePanel = null;
34851         }
34852         if(this.config.autoDestroy !== false && preservePanel !== true){
34853             try{panel.destroy();}catch(e){}
34854         }
34855         this.fireEvent("panelremoved", this, panel);
34856         return panel;
34857     },
34858
34859     /**
34860      * Returns the TabPanel component used by this region
34861      * @return {Roo.TabPanel}
34862      */
34863     getTabs : function(){
34864         return this.tabs;
34865     },
34866
34867     createTool : function(parentEl, className){
34868         var btn = Roo.DomHelper.append(parentEl, {
34869             tag: "div",
34870             cls: "x-layout-tools-button",
34871             children: [ {
34872                 tag: "div",
34873                 cls: "roo-layout-tools-button-inner " + className,
34874                 html: "&#160;"
34875             }]
34876         }, true);
34877         btn.addClassOnOver("roo-layout-tools-button-over");
34878         return btn;
34879     }
34880 });/*
34881  * Based on:
34882  * Ext JS Library 1.1.1
34883  * Copyright(c) 2006-2007, Ext JS, LLC.
34884  *
34885  * Originally Released Under LGPL - original licence link has changed is not relivant.
34886  *
34887  * Fork - LGPL
34888  * <script type="text/javascript">
34889  */
34890  
34891
34892
34893 /**
34894  * @class Roo.SplitLayoutRegion
34895  * @extends Roo.LayoutRegion
34896  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34897  */
34898 Roo.bootstrap.layout.Split = function(config){
34899     this.cursor = config.cursor;
34900     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34901 };
34902
34903 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34904 {
34905     splitTip : "Drag to resize.",
34906     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34907     useSplitTips : false,
34908
34909     applyConfig : function(config){
34910         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34911     },
34912     
34913     onRender : function(ctr,pos) {
34914         
34915         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34916         if(!this.config.split){
34917             return;
34918         }
34919         if(!this.split){
34920             
34921             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34922                             tag: "div",
34923                             id: this.el.id + "-split",
34924                             cls: "roo-layout-split roo-layout-split-"+this.position,
34925                             html: "&#160;"
34926             });
34927             /** The SplitBar for this region 
34928             * @type Roo.SplitBar */
34929             // does not exist yet...
34930             Roo.log([this.position, this.orientation]);
34931             
34932             this.split = new Roo.bootstrap.SplitBar({
34933                 dragElement : splitEl,
34934                 resizingElement: this.el,
34935                 orientation : this.orientation
34936             });
34937             
34938             this.split.on("moved", this.onSplitMove, this);
34939             this.split.useShim = this.config.useShim === true;
34940             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34941             if(this.useSplitTips){
34942                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34943             }
34944             //if(config.collapsible){
34945             //    this.split.el.on("dblclick", this.collapse,  this);
34946             //}
34947         }
34948         if(typeof this.config.minSize != "undefined"){
34949             this.split.minSize = this.config.minSize;
34950         }
34951         if(typeof this.config.maxSize != "undefined"){
34952             this.split.maxSize = this.config.maxSize;
34953         }
34954         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34955             this.hideSplitter();
34956         }
34957         
34958     },
34959
34960     getHMaxSize : function(){
34961          var cmax = this.config.maxSize || 10000;
34962          var center = this.mgr.getRegion("center");
34963          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34964     },
34965
34966     getVMaxSize : function(){
34967          var cmax = this.config.maxSize || 10000;
34968          var center = this.mgr.getRegion("center");
34969          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34970     },
34971
34972     onSplitMove : function(split, newSize){
34973         this.fireEvent("resized", this, newSize);
34974     },
34975     
34976     /** 
34977      * Returns the {@link Roo.SplitBar} for this region.
34978      * @return {Roo.SplitBar}
34979      */
34980     getSplitBar : function(){
34981         return this.split;
34982     },
34983     
34984     hide : function(){
34985         this.hideSplitter();
34986         Roo.bootstrap.layout.Split.superclass.hide.call(this);
34987     },
34988
34989     hideSplitter : function(){
34990         if(this.split){
34991             this.split.el.setLocation(-2000,-2000);
34992             this.split.el.hide();
34993         }
34994     },
34995
34996     show : function(){
34997         if(this.split){
34998             this.split.el.show();
34999         }
35000         Roo.bootstrap.layout.Split.superclass.show.call(this);
35001     },
35002     
35003     beforeSlide: function(){
35004         if(Roo.isGecko){// firefox overflow auto bug workaround
35005             this.bodyEl.clip();
35006             if(this.tabs) {
35007                 this.tabs.bodyEl.clip();
35008             }
35009             if(this.activePanel){
35010                 this.activePanel.getEl().clip();
35011                 
35012                 if(this.activePanel.beforeSlide){
35013                     this.activePanel.beforeSlide();
35014                 }
35015             }
35016         }
35017     },
35018     
35019     afterSlide : function(){
35020         if(Roo.isGecko){// firefox overflow auto bug workaround
35021             this.bodyEl.unclip();
35022             if(this.tabs) {
35023                 this.tabs.bodyEl.unclip();
35024             }
35025             if(this.activePanel){
35026                 this.activePanel.getEl().unclip();
35027                 if(this.activePanel.afterSlide){
35028                     this.activePanel.afterSlide();
35029                 }
35030             }
35031         }
35032     },
35033
35034     initAutoHide : function(){
35035         if(this.autoHide !== false){
35036             if(!this.autoHideHd){
35037                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35038                 this.autoHideHd = {
35039                     "mouseout": function(e){
35040                         if(!e.within(this.el, true)){
35041                             st.delay(500);
35042                         }
35043                     },
35044                     "mouseover" : function(e){
35045                         st.cancel();
35046                     },
35047                     scope : this
35048                 };
35049             }
35050             this.el.on(this.autoHideHd);
35051         }
35052     },
35053
35054     clearAutoHide : function(){
35055         if(this.autoHide !== false){
35056             this.el.un("mouseout", this.autoHideHd.mouseout);
35057             this.el.un("mouseover", this.autoHideHd.mouseover);
35058         }
35059     },
35060
35061     clearMonitor : function(){
35062         Roo.get(document).un("click", this.slideInIf, this);
35063     },
35064
35065     // these names are backwards but not changed for compat
35066     slideOut : function(){
35067         if(this.isSlid || this.el.hasActiveFx()){
35068             return;
35069         }
35070         this.isSlid = true;
35071         if(this.collapseBtn){
35072             this.collapseBtn.hide();
35073         }
35074         this.closeBtnState = this.closeBtn.getStyle('display');
35075         this.closeBtn.hide();
35076         if(this.stickBtn){
35077             this.stickBtn.show();
35078         }
35079         this.el.show();
35080         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35081         this.beforeSlide();
35082         this.el.setStyle("z-index", 10001);
35083         this.el.slideIn(this.getSlideAnchor(), {
35084             callback: function(){
35085                 this.afterSlide();
35086                 this.initAutoHide();
35087                 Roo.get(document).on("click", this.slideInIf, this);
35088                 this.fireEvent("slideshow", this);
35089             },
35090             scope: this,
35091             block: true
35092         });
35093     },
35094
35095     afterSlideIn : function(){
35096         this.clearAutoHide();
35097         this.isSlid = false;
35098         this.clearMonitor();
35099         this.el.setStyle("z-index", "");
35100         if(this.collapseBtn){
35101             this.collapseBtn.show();
35102         }
35103         this.closeBtn.setStyle('display', this.closeBtnState);
35104         if(this.stickBtn){
35105             this.stickBtn.hide();
35106         }
35107         this.fireEvent("slidehide", this);
35108     },
35109
35110     slideIn : function(cb){
35111         if(!this.isSlid || this.el.hasActiveFx()){
35112             Roo.callback(cb);
35113             return;
35114         }
35115         this.isSlid = false;
35116         this.beforeSlide();
35117         this.el.slideOut(this.getSlideAnchor(), {
35118             callback: function(){
35119                 this.el.setLeftTop(-10000, -10000);
35120                 this.afterSlide();
35121                 this.afterSlideIn();
35122                 Roo.callback(cb);
35123             },
35124             scope: this,
35125             block: true
35126         });
35127     },
35128     
35129     slideInIf : function(e){
35130         if(!e.within(this.el)){
35131             this.slideIn();
35132         }
35133     },
35134
35135     animateCollapse : function(){
35136         this.beforeSlide();
35137         this.el.setStyle("z-index", 20000);
35138         var anchor = this.getSlideAnchor();
35139         this.el.slideOut(anchor, {
35140             callback : function(){
35141                 this.el.setStyle("z-index", "");
35142                 this.collapsedEl.slideIn(anchor, {duration:.3});
35143                 this.afterSlide();
35144                 this.el.setLocation(-10000,-10000);
35145                 this.el.hide();
35146                 this.fireEvent("collapsed", this);
35147             },
35148             scope: this,
35149             block: true
35150         });
35151     },
35152
35153     animateExpand : function(){
35154         this.beforeSlide();
35155         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35156         this.el.setStyle("z-index", 20000);
35157         this.collapsedEl.hide({
35158             duration:.1
35159         });
35160         this.el.slideIn(this.getSlideAnchor(), {
35161             callback : function(){
35162                 this.el.setStyle("z-index", "");
35163                 this.afterSlide();
35164                 if(this.split){
35165                     this.split.el.show();
35166                 }
35167                 this.fireEvent("invalidated", this);
35168                 this.fireEvent("expanded", this);
35169             },
35170             scope: this,
35171             block: true
35172         });
35173     },
35174
35175     anchors : {
35176         "west" : "left",
35177         "east" : "right",
35178         "north" : "top",
35179         "south" : "bottom"
35180     },
35181
35182     sanchors : {
35183         "west" : "l",
35184         "east" : "r",
35185         "north" : "t",
35186         "south" : "b"
35187     },
35188
35189     canchors : {
35190         "west" : "tl-tr",
35191         "east" : "tr-tl",
35192         "north" : "tl-bl",
35193         "south" : "bl-tl"
35194     },
35195
35196     getAnchor : function(){
35197         return this.anchors[this.position];
35198     },
35199
35200     getCollapseAnchor : function(){
35201         return this.canchors[this.position];
35202     },
35203
35204     getSlideAnchor : function(){
35205         return this.sanchors[this.position];
35206     },
35207
35208     getAlignAdj : function(){
35209         var cm = this.cmargins;
35210         switch(this.position){
35211             case "west":
35212                 return [0, 0];
35213             break;
35214             case "east":
35215                 return [0, 0];
35216             break;
35217             case "north":
35218                 return [0, 0];
35219             break;
35220             case "south":
35221                 return [0, 0];
35222             break;
35223         }
35224     },
35225
35226     getExpandAdj : function(){
35227         var c = this.collapsedEl, cm = this.cmargins;
35228         switch(this.position){
35229             case "west":
35230                 return [-(cm.right+c.getWidth()+cm.left), 0];
35231             break;
35232             case "east":
35233                 return [cm.right+c.getWidth()+cm.left, 0];
35234             break;
35235             case "north":
35236                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35237             break;
35238             case "south":
35239                 return [0, cm.top+cm.bottom+c.getHeight()];
35240             break;
35241         }
35242     }
35243 });/*
35244  * Based on:
35245  * Ext JS Library 1.1.1
35246  * Copyright(c) 2006-2007, Ext JS, LLC.
35247  *
35248  * Originally Released Under LGPL - original licence link has changed is not relivant.
35249  *
35250  * Fork - LGPL
35251  * <script type="text/javascript">
35252  */
35253 /*
35254  * These classes are private internal classes
35255  */
35256 Roo.bootstrap.layout.Center = function(config){
35257     config.region = "center";
35258     Roo.bootstrap.layout.Region.call(this, config);
35259     this.visible = true;
35260     this.minWidth = config.minWidth || 20;
35261     this.minHeight = config.minHeight || 20;
35262 };
35263
35264 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35265     hide : function(){
35266         // center panel can't be hidden
35267     },
35268     
35269     show : function(){
35270         // center panel can't be hidden
35271     },
35272     
35273     getMinWidth: function(){
35274         return this.minWidth;
35275     },
35276     
35277     getMinHeight: function(){
35278         return this.minHeight;
35279     }
35280 });
35281
35282
35283
35284
35285  
35286
35287
35288
35289
35290
35291 Roo.bootstrap.layout.North = function(config)
35292 {
35293     config.region = 'north';
35294     config.cursor = 'n-resize';
35295     
35296     Roo.bootstrap.layout.Split.call(this, config);
35297     
35298     
35299     if(this.split){
35300         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35301         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35302         this.split.el.addClass("roo-layout-split-v");
35303     }
35304     var size = config.initialSize || config.height;
35305     if(typeof size != "undefined"){
35306         this.el.setHeight(size);
35307     }
35308 };
35309 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35310 {
35311     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35312     
35313     
35314     
35315     getBox : function(){
35316         if(this.collapsed){
35317             return this.collapsedEl.getBox();
35318         }
35319         var box = this.el.getBox();
35320         if(this.split){
35321             box.height += this.split.el.getHeight();
35322         }
35323         return box;
35324     },
35325     
35326     updateBox : function(box){
35327         if(this.split && !this.collapsed){
35328             box.height -= this.split.el.getHeight();
35329             this.split.el.setLeft(box.x);
35330             this.split.el.setTop(box.y+box.height);
35331             this.split.el.setWidth(box.width);
35332         }
35333         if(this.collapsed){
35334             this.updateBody(box.width, null);
35335         }
35336         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35337     }
35338 });
35339
35340
35341
35342
35343
35344 Roo.bootstrap.layout.South = function(config){
35345     config.region = 'south';
35346     config.cursor = 's-resize';
35347     Roo.bootstrap.layout.Split.call(this, config);
35348     if(this.split){
35349         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
35350         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35351         this.split.el.addClass("roo-layout-split-v");
35352     }
35353     var size = config.initialSize || config.height;
35354     if(typeof size != "undefined"){
35355         this.el.setHeight(size);
35356     }
35357 };
35358
35359 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
35360     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35361     getBox : function(){
35362         if(this.collapsed){
35363             return this.collapsedEl.getBox();
35364         }
35365         var box = this.el.getBox();
35366         if(this.split){
35367             var sh = this.split.el.getHeight();
35368             box.height += sh;
35369             box.y -= sh;
35370         }
35371         return box;
35372     },
35373     
35374     updateBox : function(box){
35375         if(this.split && !this.collapsed){
35376             var sh = this.split.el.getHeight();
35377             box.height -= sh;
35378             box.y += sh;
35379             this.split.el.setLeft(box.x);
35380             this.split.el.setTop(box.y-sh);
35381             this.split.el.setWidth(box.width);
35382         }
35383         if(this.collapsed){
35384             this.updateBody(box.width, null);
35385         }
35386         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35387     }
35388 });
35389
35390 Roo.bootstrap.layout.East = function(config){
35391     config.region = "east";
35392     config.cursor = "e-resize";
35393     Roo.bootstrap.layout.Split.call(this, config);
35394     if(this.split){
35395         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
35396         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35397         this.split.el.addClass("roo-layout-split-h");
35398     }
35399     var size = config.initialSize || config.width;
35400     if(typeof size != "undefined"){
35401         this.el.setWidth(size);
35402     }
35403 };
35404 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
35405     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35406     getBox : function(){
35407         if(this.collapsed){
35408             return this.collapsedEl.getBox();
35409         }
35410         var box = this.el.getBox();
35411         if(this.split){
35412             var sw = this.split.el.getWidth();
35413             box.width += sw;
35414             box.x -= sw;
35415         }
35416         return box;
35417     },
35418
35419     updateBox : function(box){
35420         if(this.split && !this.collapsed){
35421             var sw = this.split.el.getWidth();
35422             box.width -= sw;
35423             this.split.el.setLeft(box.x);
35424             this.split.el.setTop(box.y);
35425             this.split.el.setHeight(box.height);
35426             box.x += sw;
35427         }
35428         if(this.collapsed){
35429             this.updateBody(null, box.height);
35430         }
35431         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35432     }
35433 });
35434
35435 Roo.bootstrap.layout.West = function(config){
35436     config.region = "west";
35437     config.cursor = "w-resize";
35438     
35439     Roo.bootstrap.layout.Split.call(this, config);
35440     if(this.split){
35441         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
35442         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35443         this.split.el.addClass("roo-layout-split-h");
35444     }
35445     
35446 };
35447 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
35448     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35449     
35450     onRender: function(ctr, pos)
35451     {
35452         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
35453         var size = this.config.initialSize || this.config.width;
35454         if(typeof size != "undefined"){
35455             this.el.setWidth(size);
35456         }
35457     },
35458     
35459     getBox : function(){
35460         if(this.collapsed){
35461             return this.collapsedEl.getBox();
35462         }
35463         var box = this.el.getBox();
35464         if(this.split){
35465             box.width += this.split.el.getWidth();
35466         }
35467         return box;
35468     },
35469     
35470     updateBox : function(box){
35471         if(this.split && !this.collapsed){
35472             var sw = this.split.el.getWidth();
35473             box.width -= sw;
35474             this.split.el.setLeft(box.x+box.width);
35475             this.split.el.setTop(box.y);
35476             this.split.el.setHeight(box.height);
35477         }
35478         if(this.collapsed){
35479             this.updateBody(null, box.height);
35480         }
35481         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35482     }
35483 });
35484 Roo.namespace("Roo.bootstrap.panel");/*
35485  * Based on:
35486  * Ext JS Library 1.1.1
35487  * Copyright(c) 2006-2007, Ext JS, LLC.
35488  *
35489  * Originally Released Under LGPL - original licence link has changed is not relivant.
35490  *
35491  * Fork - LGPL
35492  * <script type="text/javascript">
35493  */
35494 /**
35495  * @class Roo.ContentPanel
35496  * @extends Roo.util.Observable
35497  * A basic ContentPanel element.
35498  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35499  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35500  * @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
35501  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35502  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35503  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35504  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35505  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35506  * @cfg {String} title          The title for this panel
35507  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35508  * @cfg {String} url            Calls {@link #setUrl} with this value
35509  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35510  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35511  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35512  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35513  * @cfg {Boolean} badges render the badges
35514
35515  * @constructor
35516  * Create a new ContentPanel.
35517  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35518  * @param {String/Object} config A string to set only the title or a config object
35519  * @param {String} content (optional) Set the HTML content for this panel
35520  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35521  */
35522 Roo.bootstrap.panel.Content = function( config){
35523     
35524     this.tpl = config.tpl || false;
35525     
35526     var el = config.el;
35527     var content = config.content;
35528
35529     if(config.autoCreate){ // xtype is available if this is called from factory
35530         el = Roo.id();
35531     }
35532     this.el = Roo.get(el);
35533     if(!this.el && config && config.autoCreate){
35534         if(typeof config.autoCreate == "object"){
35535             if(!config.autoCreate.id){
35536                 config.autoCreate.id = config.id||el;
35537             }
35538             this.el = Roo.DomHelper.append(document.body,
35539                         config.autoCreate, true);
35540         }else{
35541             var elcfg =  {   tag: "div",
35542                             cls: "roo-layout-inactive-content",
35543                             id: config.id||el
35544                             };
35545             if (config.html) {
35546                 elcfg.html = config.html;
35547                 
35548             }
35549                         
35550             this.el = Roo.DomHelper.append(document.body, elcfg , true);
35551         }
35552     } 
35553     this.closable = false;
35554     this.loaded = false;
35555     this.active = false;
35556    
35557       
35558     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
35559         
35560         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
35561         
35562         this.wrapEl = this.el; //this.el.wrap();
35563         var ti = [];
35564         if (config.toolbar.items) {
35565             ti = config.toolbar.items ;
35566             delete config.toolbar.items ;
35567         }
35568         
35569         var nitems = [];
35570         this.toolbar.render(this.wrapEl, 'before');
35571         for(var i =0;i < ti.length;i++) {
35572           //  Roo.log(['add child', items[i]]);
35573             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35574         }
35575         this.toolbar.items = nitems;
35576         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
35577         delete config.toolbar;
35578         
35579     }
35580     /*
35581     // xtype created footer. - not sure if will work as we normally have to render first..
35582     if (this.footer && !this.footer.el && this.footer.xtype) {
35583         if (!this.wrapEl) {
35584             this.wrapEl = this.el.wrap();
35585         }
35586     
35587         this.footer.container = this.wrapEl.createChild();
35588          
35589         this.footer = Roo.factory(this.footer, Roo);
35590         
35591     }
35592     */
35593     
35594      if(typeof config == "string"){
35595         this.title = config;
35596     }else{
35597         Roo.apply(this, config);
35598     }
35599     
35600     if(this.resizeEl){
35601         this.resizeEl = Roo.get(this.resizeEl, true);
35602     }else{
35603         this.resizeEl = this.el;
35604     }
35605     // handle view.xtype
35606     
35607  
35608     
35609     
35610     this.addEvents({
35611         /**
35612          * @event activate
35613          * Fires when this panel is activated. 
35614          * @param {Roo.ContentPanel} this
35615          */
35616         "activate" : true,
35617         /**
35618          * @event deactivate
35619          * Fires when this panel is activated. 
35620          * @param {Roo.ContentPanel} this
35621          */
35622         "deactivate" : true,
35623
35624         /**
35625          * @event resize
35626          * Fires when this panel is resized if fitToFrame is true.
35627          * @param {Roo.ContentPanel} this
35628          * @param {Number} width The width after any component adjustments
35629          * @param {Number} height The height after any component adjustments
35630          */
35631         "resize" : true,
35632         
35633          /**
35634          * @event render
35635          * Fires when this tab is created
35636          * @param {Roo.ContentPanel} this
35637          */
35638         "render" : true
35639         
35640         
35641         
35642     });
35643     
35644
35645     
35646     
35647     if(this.autoScroll){
35648         this.resizeEl.setStyle("overflow", "auto");
35649     } else {
35650         // fix randome scrolling
35651         //this.el.on('scroll', function() {
35652         //    Roo.log('fix random scolling');
35653         //    this.scrollTo('top',0); 
35654         //});
35655     }
35656     content = content || this.content;
35657     if(content){
35658         this.setContent(content);
35659     }
35660     if(config && config.url){
35661         this.setUrl(this.url, this.params, this.loadOnce);
35662     }
35663     
35664     
35665     
35666     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35667     
35668     if (this.view && typeof(this.view.xtype) != 'undefined') {
35669         this.view.el = this.el.appendChild(document.createElement("div"));
35670         this.view = Roo.factory(this.view); 
35671         this.view.render  &&  this.view.render(false, '');  
35672     }
35673     
35674     
35675     this.fireEvent('render', this);
35676 };
35677
35678 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35679     
35680     tabTip : '',
35681     
35682     setRegion : function(region){
35683         this.region = region;
35684         this.setActiveClass(region && !this.background);
35685     },
35686     
35687     
35688     setActiveClass: function(state)
35689     {
35690         if(state){
35691            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35692            this.el.setStyle('position','relative');
35693         }else{
35694            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35695            this.el.setStyle('position', 'absolute');
35696         } 
35697     },
35698     
35699     /**
35700      * Returns the toolbar for this Panel if one was configured. 
35701      * @return {Roo.Toolbar} 
35702      */
35703     getToolbar : function(){
35704         return this.toolbar;
35705     },
35706     
35707     setActiveState : function(active)
35708     {
35709         this.active = active;
35710         this.setActiveClass(active);
35711         if(!active){
35712             this.fireEvent("deactivate", this);
35713         }else{
35714             this.fireEvent("activate", this);
35715         }
35716     },
35717     /**
35718      * Updates this panel's element
35719      * @param {String} content The new content
35720      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35721     */
35722     setContent : function(content, loadScripts){
35723         this.el.update(content, loadScripts);
35724     },
35725
35726     ignoreResize : function(w, h){
35727         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35728             return true;
35729         }else{
35730             this.lastSize = {width: w, height: h};
35731             return false;
35732         }
35733     },
35734     /**
35735      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35736      * @return {Roo.UpdateManager} The UpdateManager
35737      */
35738     getUpdateManager : function(){
35739         return this.el.getUpdateManager();
35740     },
35741      /**
35742      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35743      * @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:
35744 <pre><code>
35745 panel.load({
35746     url: "your-url.php",
35747     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35748     callback: yourFunction,
35749     scope: yourObject, //(optional scope)
35750     discardUrl: false,
35751     nocache: false,
35752     text: "Loading...",
35753     timeout: 30,
35754     scripts: false
35755 });
35756 </code></pre>
35757      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35758      * 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.
35759      * @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}
35760      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35761      * @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.
35762      * @return {Roo.ContentPanel} this
35763      */
35764     load : function(){
35765         var um = this.el.getUpdateManager();
35766         um.update.apply(um, arguments);
35767         return this;
35768     },
35769
35770
35771     /**
35772      * 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.
35773      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35774      * @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)
35775      * @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)
35776      * @return {Roo.UpdateManager} The UpdateManager
35777      */
35778     setUrl : function(url, params, loadOnce){
35779         if(this.refreshDelegate){
35780             this.removeListener("activate", this.refreshDelegate);
35781         }
35782         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35783         this.on("activate", this.refreshDelegate);
35784         return this.el.getUpdateManager();
35785     },
35786     
35787     _handleRefresh : function(url, params, loadOnce){
35788         if(!loadOnce || !this.loaded){
35789             var updater = this.el.getUpdateManager();
35790             updater.update(url, params, this._setLoaded.createDelegate(this));
35791         }
35792     },
35793     
35794     _setLoaded : function(){
35795         this.loaded = true;
35796     }, 
35797     
35798     /**
35799      * Returns this panel's id
35800      * @return {String} 
35801      */
35802     getId : function(){
35803         return this.el.id;
35804     },
35805     
35806     /** 
35807      * Returns this panel's element - used by regiosn to add.
35808      * @return {Roo.Element} 
35809      */
35810     getEl : function(){
35811         return this.wrapEl || this.el;
35812     },
35813     
35814    
35815     
35816     adjustForComponents : function(width, height)
35817     {
35818         //Roo.log('adjustForComponents ');
35819         if(this.resizeEl != this.el){
35820             width -= this.el.getFrameWidth('lr');
35821             height -= this.el.getFrameWidth('tb');
35822         }
35823         if(this.toolbar){
35824             var te = this.toolbar.getEl();
35825             height -= te.getHeight();
35826             te.setWidth(width);
35827         }
35828         if(this.footer){
35829             var te = this.footer.getEl();
35830             Roo.log("footer:" + te.getHeight());
35831             
35832             height -= te.getHeight();
35833             te.setWidth(width);
35834         }
35835         
35836         
35837         if(this.adjustments){
35838             width += this.adjustments[0];
35839             height += this.adjustments[1];
35840         }
35841         return {"width": width, "height": height};
35842     },
35843     
35844     setSize : function(width, height){
35845         if(this.fitToFrame && !this.ignoreResize(width, height)){
35846             if(this.fitContainer && this.resizeEl != this.el){
35847                 this.el.setSize(width, height);
35848             }
35849             var size = this.adjustForComponents(width, height);
35850             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35851             this.fireEvent('resize', this, size.width, size.height);
35852         }
35853     },
35854     
35855     /**
35856      * Returns this panel's title
35857      * @return {String} 
35858      */
35859     getTitle : function(){
35860         
35861         if (typeof(this.title) != 'object') {
35862             return this.title;
35863         }
35864         
35865         var t = '';
35866         for (var k in this.title) {
35867             if (!this.title.hasOwnProperty(k)) {
35868                 continue;
35869             }
35870             
35871             if (k.indexOf('-') >= 0) {
35872                 var s = k.split('-');
35873                 for (var i = 0; i<s.length; i++) {
35874                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
35875                 }
35876             } else {
35877                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
35878             }
35879         }
35880         return t;
35881     },
35882     
35883     /**
35884      * Set this panel's title
35885      * @param {String} title
35886      */
35887     setTitle : function(title){
35888         this.title = title;
35889         if(this.region){
35890             this.region.updatePanelTitle(this, title);
35891         }
35892     },
35893     
35894     /**
35895      * Returns true is this panel was configured to be closable
35896      * @return {Boolean} 
35897      */
35898     isClosable : function(){
35899         return this.closable;
35900     },
35901     
35902     beforeSlide : function(){
35903         this.el.clip();
35904         this.resizeEl.clip();
35905     },
35906     
35907     afterSlide : function(){
35908         this.el.unclip();
35909         this.resizeEl.unclip();
35910     },
35911     
35912     /**
35913      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35914      *   Will fail silently if the {@link #setUrl} method has not been called.
35915      *   This does not activate the panel, just updates its content.
35916      */
35917     refresh : function(){
35918         if(this.refreshDelegate){
35919            this.loaded = false;
35920            this.refreshDelegate();
35921         }
35922     },
35923     
35924     /**
35925      * Destroys this panel
35926      */
35927     destroy : function(){
35928         this.el.removeAllListeners();
35929         var tempEl = document.createElement("span");
35930         tempEl.appendChild(this.el.dom);
35931         tempEl.innerHTML = "";
35932         this.el.remove();
35933         this.el = null;
35934     },
35935     
35936     /**
35937      * form - if the content panel contains a form - this is a reference to it.
35938      * @type {Roo.form.Form}
35939      */
35940     form : false,
35941     /**
35942      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35943      *    This contains a reference to it.
35944      * @type {Roo.View}
35945      */
35946     view : false,
35947     
35948       /**
35949      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35950      * <pre><code>
35951
35952 layout.addxtype({
35953        xtype : 'Form',
35954        items: [ .... ]
35955    }
35956 );
35957
35958 </code></pre>
35959      * @param {Object} cfg Xtype definition of item to add.
35960      */
35961     
35962     
35963     getChildContainer: function () {
35964         return this.getEl();
35965     }
35966     
35967     
35968     /*
35969         var  ret = new Roo.factory(cfg);
35970         return ret;
35971         
35972         
35973         // add form..
35974         if (cfg.xtype.match(/^Form$/)) {
35975             
35976             var el;
35977             //if (this.footer) {
35978             //    el = this.footer.container.insertSibling(false, 'before');
35979             //} else {
35980                 el = this.el.createChild();
35981             //}
35982
35983             this.form = new  Roo.form.Form(cfg);
35984             
35985             
35986             if ( this.form.allItems.length) {
35987                 this.form.render(el.dom);
35988             }
35989             return this.form;
35990         }
35991         // should only have one of theses..
35992         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35993             // views.. should not be just added - used named prop 'view''
35994             
35995             cfg.el = this.el.appendChild(document.createElement("div"));
35996             // factory?
35997             
35998             var ret = new Roo.factory(cfg);
35999              
36000              ret.render && ret.render(false, ''); // render blank..
36001             this.view = ret;
36002             return ret;
36003         }
36004         return false;
36005     }
36006     \*/
36007 });
36008  
36009 /**
36010  * @class Roo.bootstrap.panel.Grid
36011  * @extends Roo.bootstrap.panel.Content
36012  * @constructor
36013  * Create a new GridPanel.
36014  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36015  * @param {Object} config A the config object
36016   
36017  */
36018
36019
36020
36021 Roo.bootstrap.panel.Grid = function(config)
36022 {
36023     
36024       
36025     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36026         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36027
36028     config.el = this.wrapper;
36029     //this.el = this.wrapper;
36030     
36031       if (config.container) {
36032         // ctor'ed from a Border/panel.grid
36033         
36034         
36035         this.wrapper.setStyle("overflow", "hidden");
36036         this.wrapper.addClass('roo-grid-container');
36037
36038     }
36039     
36040     
36041     if(config.toolbar){
36042         var tool_el = this.wrapper.createChild();    
36043         this.toolbar = Roo.factory(config.toolbar);
36044         var ti = [];
36045         if (config.toolbar.items) {
36046             ti = config.toolbar.items ;
36047             delete config.toolbar.items ;
36048         }
36049         
36050         var nitems = [];
36051         this.toolbar.render(tool_el);
36052         for(var i =0;i < ti.length;i++) {
36053           //  Roo.log(['add child', items[i]]);
36054             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36055         }
36056         this.toolbar.items = nitems;
36057         
36058         delete config.toolbar;
36059     }
36060     
36061     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36062     config.grid.scrollBody = true;;
36063     config.grid.monitorWindowResize = false; // turn off autosizing
36064     config.grid.autoHeight = false;
36065     config.grid.autoWidth = false;
36066     
36067     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36068     
36069     if (config.background) {
36070         // render grid on panel activation (if panel background)
36071         this.on('activate', function(gp) {
36072             if (!gp.grid.rendered) {
36073                 gp.grid.render(this.wrapper);
36074                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36075             }
36076         });
36077             
36078     } else {
36079         this.grid.render(this.wrapper);
36080         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36081
36082     }
36083     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36084     // ??? needed ??? config.el = this.wrapper;
36085     
36086     
36087     
36088   
36089     // xtype created footer. - not sure if will work as we normally have to render first..
36090     if (this.footer && !this.footer.el && this.footer.xtype) {
36091         
36092         var ctr = this.grid.getView().getFooterPanel(true);
36093         this.footer.dataSource = this.grid.dataSource;
36094         this.footer = Roo.factory(this.footer, Roo);
36095         this.footer.render(ctr);
36096         
36097     }
36098     
36099     
36100     
36101     
36102      
36103 };
36104
36105 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36106     getId : function(){
36107         return this.grid.id;
36108     },
36109     
36110     /**
36111      * Returns the grid for this panel
36112      * @return {Roo.bootstrap.Table} 
36113      */
36114     getGrid : function(){
36115         return this.grid;    
36116     },
36117     
36118     setSize : function(width, height){
36119         if(!this.ignoreResize(width, height)){
36120             var grid = this.grid;
36121             var size = this.adjustForComponents(width, height);
36122             var gridel = grid.getGridEl();
36123             gridel.setSize(size.width, size.height);
36124             /*
36125             var thd = grid.getGridEl().select('thead',true).first();
36126             var tbd = grid.getGridEl().select('tbody', true).first();
36127             if (tbd) {
36128                 tbd.setSize(width, height - thd.getHeight());
36129             }
36130             */
36131             grid.autoSize();
36132         }
36133     },
36134      
36135     
36136     
36137     beforeSlide : function(){
36138         this.grid.getView().scroller.clip();
36139     },
36140     
36141     afterSlide : function(){
36142         this.grid.getView().scroller.unclip();
36143     },
36144     
36145     destroy : function(){
36146         this.grid.destroy();
36147         delete this.grid;
36148         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36149     }
36150 });
36151
36152 /**
36153  * @class Roo.bootstrap.panel.Nest
36154  * @extends Roo.bootstrap.panel.Content
36155  * @constructor
36156  * Create a new Panel, that can contain a layout.Border.
36157  * 
36158  * 
36159  * @param {Roo.BorderLayout} layout The layout for this panel
36160  * @param {String/Object} config A string to set only the title or a config object
36161  */
36162 Roo.bootstrap.panel.Nest = function(config)
36163 {
36164     // construct with only one argument..
36165     /* FIXME - implement nicer consturctors
36166     if (layout.layout) {
36167         config = layout;
36168         layout = config.layout;
36169         delete config.layout;
36170     }
36171     if (layout.xtype && !layout.getEl) {
36172         // then layout needs constructing..
36173         layout = Roo.factory(layout, Roo);
36174     }
36175     */
36176     
36177     config.el =  config.layout.getEl();
36178     
36179     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36180     
36181     config.layout.monitorWindowResize = false; // turn off autosizing
36182     this.layout = config.layout;
36183     this.layout.getEl().addClass("roo-layout-nested-layout");
36184     
36185     
36186     
36187     
36188 };
36189
36190 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36191
36192     setSize : function(width, height){
36193         if(!this.ignoreResize(width, height)){
36194             var size = this.adjustForComponents(width, height);
36195             var el = this.layout.getEl();
36196             if (size.height < 1) {
36197                 el.setWidth(size.width);   
36198             } else {
36199                 el.setSize(size.width, size.height);
36200             }
36201             var touch = el.dom.offsetWidth;
36202             this.layout.layout();
36203             // ie requires a double layout on the first pass
36204             if(Roo.isIE && !this.initialized){
36205                 this.initialized = true;
36206                 this.layout.layout();
36207             }
36208         }
36209     },
36210     
36211     // activate all subpanels if not currently active..
36212     
36213     setActiveState : function(active){
36214         this.active = active;
36215         this.setActiveClass(active);
36216         
36217         if(!active){
36218             this.fireEvent("deactivate", this);
36219             return;
36220         }
36221         
36222         this.fireEvent("activate", this);
36223         // not sure if this should happen before or after..
36224         if (!this.layout) {
36225             return; // should not happen..
36226         }
36227         var reg = false;
36228         for (var r in this.layout.regions) {
36229             reg = this.layout.getRegion(r);
36230             if (reg.getActivePanel()) {
36231                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36232                 reg.setActivePanel(reg.getActivePanel());
36233                 continue;
36234             }
36235             if (!reg.panels.length) {
36236                 continue;
36237             }
36238             reg.showPanel(reg.getPanel(0));
36239         }
36240         
36241         
36242         
36243         
36244     },
36245     
36246     /**
36247      * Returns the nested BorderLayout for this panel
36248      * @return {Roo.BorderLayout} 
36249      */
36250     getLayout : function(){
36251         return this.layout;
36252     },
36253     
36254      /**
36255      * Adds a xtype elements to the layout of the nested panel
36256      * <pre><code>
36257
36258 panel.addxtype({
36259        xtype : 'ContentPanel',
36260        region: 'west',
36261        items: [ .... ]
36262    }
36263 );
36264
36265 panel.addxtype({
36266         xtype : 'NestedLayoutPanel',
36267         region: 'west',
36268         layout: {
36269            center: { },
36270            west: { }   
36271         },
36272         items : [ ... list of content panels or nested layout panels.. ]
36273    }
36274 );
36275 </code></pre>
36276      * @param {Object} cfg Xtype definition of item to add.
36277      */
36278     addxtype : function(cfg) {
36279         return this.layout.addxtype(cfg);
36280     
36281     }
36282 });        /*
36283  * Based on:
36284  * Ext JS Library 1.1.1
36285  * Copyright(c) 2006-2007, Ext JS, LLC.
36286  *
36287  * Originally Released Under LGPL - original licence link has changed is not relivant.
36288  *
36289  * Fork - LGPL
36290  * <script type="text/javascript">
36291  */
36292 /**
36293  * @class Roo.TabPanel
36294  * @extends Roo.util.Observable
36295  * A lightweight tab container.
36296  * <br><br>
36297  * Usage:
36298  * <pre><code>
36299 // basic tabs 1, built from existing content
36300 var tabs = new Roo.TabPanel("tabs1");
36301 tabs.addTab("script", "View Script");
36302 tabs.addTab("markup", "View Markup");
36303 tabs.activate("script");
36304
36305 // more advanced tabs, built from javascript
36306 var jtabs = new Roo.TabPanel("jtabs");
36307 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36308
36309 // set up the UpdateManager
36310 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36311 var updater = tab2.getUpdateManager();
36312 updater.setDefaultUrl("ajax1.htm");
36313 tab2.on('activate', updater.refresh, updater, true);
36314
36315 // Use setUrl for Ajax loading
36316 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36317 tab3.setUrl("ajax2.htm", null, true);
36318
36319 // Disabled tab
36320 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36321 tab4.disable();
36322
36323 jtabs.activate("jtabs-1");
36324  * </code></pre>
36325  * @constructor
36326  * Create a new TabPanel.
36327  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
36328  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
36329  */
36330 Roo.bootstrap.panel.Tabs = function(config){
36331     /**
36332     * The container element for this TabPanel.
36333     * @type Roo.Element
36334     */
36335     this.el = Roo.get(config.el);
36336     delete config.el;
36337     if(config){
36338         if(typeof config == "boolean"){
36339             this.tabPosition = config ? "bottom" : "top";
36340         }else{
36341             Roo.apply(this, config);
36342         }
36343     }
36344     
36345     if(this.tabPosition == "bottom"){
36346         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36347         this.el.addClass("roo-tabs-bottom");
36348     }
36349     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
36350     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
36351     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
36352     if(Roo.isIE){
36353         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
36354     }
36355     if(this.tabPosition != "bottom"){
36356         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
36357          * @type Roo.Element
36358          */
36359         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36360         this.el.addClass("roo-tabs-top");
36361     }
36362     this.items = [];
36363
36364     this.bodyEl.setStyle("position", "relative");
36365
36366     this.active = null;
36367     this.activateDelegate = this.activate.createDelegate(this);
36368
36369     this.addEvents({
36370         /**
36371          * @event tabchange
36372          * Fires when the active tab changes
36373          * @param {Roo.TabPanel} this
36374          * @param {Roo.TabPanelItem} activePanel The new active tab
36375          */
36376         "tabchange": true,
36377         /**
36378          * @event beforetabchange
36379          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
36380          * @param {Roo.TabPanel} this
36381          * @param {Object} e Set cancel to true on this object to cancel the tab change
36382          * @param {Roo.TabPanelItem} tab The tab being changed to
36383          */
36384         "beforetabchange" : true
36385     });
36386
36387     Roo.EventManager.onWindowResize(this.onResize, this);
36388     this.cpad = this.el.getPadding("lr");
36389     this.hiddenCount = 0;
36390
36391
36392     // toolbar on the tabbar support...
36393     if (this.toolbar) {
36394         alert("no toolbar support yet");
36395         this.toolbar  = false;
36396         /*
36397         var tcfg = this.toolbar;
36398         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
36399         this.toolbar = new Roo.Toolbar(tcfg);
36400         if (Roo.isSafari) {
36401             var tbl = tcfg.container.child('table', true);
36402             tbl.setAttribute('width', '100%');
36403         }
36404         */
36405         
36406     }
36407    
36408
36409
36410     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
36411 };
36412
36413 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
36414     /*
36415      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
36416      */
36417     tabPosition : "top",
36418     /*
36419      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
36420      */
36421     currentTabWidth : 0,
36422     /*
36423      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
36424      */
36425     minTabWidth : 40,
36426     /*
36427      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
36428      */
36429     maxTabWidth : 250,
36430     /*
36431      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
36432      */
36433     preferredTabWidth : 175,
36434     /*
36435      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
36436      */
36437     resizeTabs : false,
36438     /*
36439      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
36440      */
36441     monitorResize : true,
36442     /*
36443      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
36444      */
36445     toolbar : false,
36446
36447     /**
36448      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
36449      * @param {String} id The id of the div to use <b>or create</b>
36450      * @param {String} text The text for the tab
36451      * @param {String} content (optional) Content to put in the TabPanelItem body
36452      * @param {Boolean} closable (optional) True to create a close icon on the tab
36453      * @return {Roo.TabPanelItem} The created TabPanelItem
36454      */
36455     addTab : function(id, text, content, closable, tpl)
36456     {
36457         var item = new Roo.bootstrap.panel.TabItem({
36458             panel: this,
36459             id : id,
36460             text : text,
36461             closable : closable,
36462             tpl : tpl
36463         });
36464         this.addTabItem(item);
36465         if(content){
36466             item.setContent(content);
36467         }
36468         return item;
36469     },
36470
36471     /**
36472      * Returns the {@link Roo.TabPanelItem} with the specified id/index
36473      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
36474      * @return {Roo.TabPanelItem}
36475      */
36476     getTab : function(id){
36477         return this.items[id];
36478     },
36479
36480     /**
36481      * Hides the {@link Roo.TabPanelItem} with the specified id/index
36482      * @param {String/Number} id The id or index of the TabPanelItem to hide.
36483      */
36484     hideTab : function(id){
36485         var t = this.items[id];
36486         if(!t.isHidden()){
36487            t.setHidden(true);
36488            this.hiddenCount++;
36489            this.autoSizeTabs();
36490         }
36491     },
36492
36493     /**
36494      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
36495      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
36496      */
36497     unhideTab : function(id){
36498         var t = this.items[id];
36499         if(t.isHidden()){
36500            t.setHidden(false);
36501            this.hiddenCount--;
36502            this.autoSizeTabs();
36503         }
36504     },
36505
36506     /**
36507      * Adds an existing {@link Roo.TabPanelItem}.
36508      * @param {Roo.TabPanelItem} item The TabPanelItem to add
36509      */
36510     addTabItem : function(item){
36511         this.items[item.id] = item;
36512         this.items.push(item);
36513       //  if(this.resizeTabs){
36514     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
36515   //         this.autoSizeTabs();
36516 //        }else{
36517 //            item.autoSize();
36518        // }
36519     },
36520
36521     /**
36522      * Removes a {@link Roo.TabPanelItem}.
36523      * @param {String/Number} id The id or index of the TabPanelItem to remove.
36524      */
36525     removeTab : function(id){
36526         var items = this.items;
36527         var tab = items[id];
36528         if(!tab) { return; }
36529         var index = items.indexOf(tab);
36530         if(this.active == tab && items.length > 1){
36531             var newTab = this.getNextAvailable(index);
36532             if(newTab) {
36533                 newTab.activate();
36534             }
36535         }
36536         this.stripEl.dom.removeChild(tab.pnode.dom);
36537         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
36538             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
36539         }
36540         items.splice(index, 1);
36541         delete this.items[tab.id];
36542         tab.fireEvent("close", tab);
36543         tab.purgeListeners();
36544         this.autoSizeTabs();
36545     },
36546
36547     getNextAvailable : function(start){
36548         var items = this.items;
36549         var index = start;
36550         // look for a next tab that will slide over to
36551         // replace the one being removed
36552         while(index < items.length){
36553             var item = items[++index];
36554             if(item && !item.isHidden()){
36555                 return item;
36556             }
36557         }
36558         // if one isn't found select the previous tab (on the left)
36559         index = start;
36560         while(index >= 0){
36561             var item = items[--index];
36562             if(item && !item.isHidden()){
36563                 return item;
36564             }
36565         }
36566         return null;
36567     },
36568
36569     /**
36570      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
36571      * @param {String/Number} id The id or index of the TabPanelItem to disable.
36572      */
36573     disableTab : function(id){
36574         var tab = this.items[id];
36575         if(tab && this.active != tab){
36576             tab.disable();
36577         }
36578     },
36579
36580     /**
36581      * Enables a {@link Roo.TabPanelItem} that is disabled.
36582      * @param {String/Number} id The id or index of the TabPanelItem to enable.
36583      */
36584     enableTab : function(id){
36585         var tab = this.items[id];
36586         tab.enable();
36587     },
36588
36589     /**
36590      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
36591      * @param {String/Number} id The id or index of the TabPanelItem to activate.
36592      * @return {Roo.TabPanelItem} The TabPanelItem.
36593      */
36594     activate : function(id){
36595         var tab = this.items[id];
36596         if(!tab){
36597             return null;
36598         }
36599         if(tab == this.active || tab.disabled){
36600             return tab;
36601         }
36602         var e = {};
36603         this.fireEvent("beforetabchange", this, e, tab);
36604         if(e.cancel !== true && !tab.disabled){
36605             if(this.active){
36606                 this.active.hide();
36607             }
36608             this.active = this.items[id];
36609             this.active.show();
36610             this.fireEvent("tabchange", this, this.active);
36611         }
36612         return tab;
36613     },
36614
36615     /**
36616      * Gets the active {@link Roo.TabPanelItem}.
36617      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
36618      */
36619     getActiveTab : function(){
36620         return this.active;
36621     },
36622
36623     /**
36624      * Updates the tab body element to fit the height of the container element
36625      * for overflow scrolling
36626      * @param {Number} targetHeight (optional) Override the starting height from the elements height
36627      */
36628     syncHeight : function(targetHeight){
36629         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36630         var bm = this.bodyEl.getMargins();
36631         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
36632         this.bodyEl.setHeight(newHeight);
36633         return newHeight;
36634     },
36635
36636     onResize : function(){
36637         if(this.monitorResize){
36638             this.autoSizeTabs();
36639         }
36640     },
36641
36642     /**
36643      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
36644      */
36645     beginUpdate : function(){
36646         this.updating = true;
36647     },
36648
36649     /**
36650      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36651      */
36652     endUpdate : function(){
36653         this.updating = false;
36654         this.autoSizeTabs();
36655     },
36656
36657     /**
36658      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36659      */
36660     autoSizeTabs : function(){
36661         var count = this.items.length;
36662         var vcount = count - this.hiddenCount;
36663         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36664             return;
36665         }
36666         var w = Math.max(this.el.getWidth() - this.cpad, 10);
36667         var availWidth = Math.floor(w / vcount);
36668         var b = this.stripBody;
36669         if(b.getWidth() > w){
36670             var tabs = this.items;
36671             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36672             if(availWidth < this.minTabWidth){
36673                 /*if(!this.sleft){    // incomplete scrolling code
36674                     this.createScrollButtons();
36675                 }
36676                 this.showScroll();
36677                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36678             }
36679         }else{
36680             if(this.currentTabWidth < this.preferredTabWidth){
36681                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36682             }
36683         }
36684     },
36685
36686     /**
36687      * Returns the number of tabs in this TabPanel.
36688      * @return {Number}
36689      */
36690      getCount : function(){
36691          return this.items.length;
36692      },
36693
36694     /**
36695      * Resizes all the tabs to the passed width
36696      * @param {Number} The new width
36697      */
36698     setTabWidth : function(width){
36699         this.currentTabWidth = width;
36700         for(var i = 0, len = this.items.length; i < len; i++) {
36701                 if(!this.items[i].isHidden()) {
36702                 this.items[i].setWidth(width);
36703             }
36704         }
36705     },
36706
36707     /**
36708      * Destroys this TabPanel
36709      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36710      */
36711     destroy : function(removeEl){
36712         Roo.EventManager.removeResizeListener(this.onResize, this);
36713         for(var i = 0, len = this.items.length; i < len; i++){
36714             this.items[i].purgeListeners();
36715         }
36716         if(removeEl === true){
36717             this.el.update("");
36718             this.el.remove();
36719         }
36720     },
36721     
36722     createStrip : function(container)
36723     {
36724         var strip = document.createElement("nav");
36725         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36726         container.appendChild(strip);
36727         return strip;
36728     },
36729     
36730     createStripList : function(strip)
36731     {
36732         // div wrapper for retard IE
36733         // returns the "tr" element.
36734         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36735         //'<div class="x-tabs-strip-wrap">'+
36736           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36737           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36738         return strip.firstChild; //.firstChild.firstChild.firstChild;
36739     },
36740     createBody : function(container)
36741     {
36742         var body = document.createElement("div");
36743         Roo.id(body, "tab-body");
36744         //Roo.fly(body).addClass("x-tabs-body");
36745         Roo.fly(body).addClass("tab-content");
36746         container.appendChild(body);
36747         return body;
36748     },
36749     createItemBody :function(bodyEl, id){
36750         var body = Roo.getDom(id);
36751         if(!body){
36752             body = document.createElement("div");
36753             body.id = id;
36754         }
36755         //Roo.fly(body).addClass("x-tabs-item-body");
36756         Roo.fly(body).addClass("tab-pane");
36757          bodyEl.insertBefore(body, bodyEl.firstChild);
36758         return body;
36759     },
36760     /** @private */
36761     createStripElements :  function(stripEl, text, closable, tpl)
36762     {
36763         var td = document.createElement("li"); // was td..
36764         
36765         
36766         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36767         
36768         
36769         stripEl.appendChild(td);
36770         /*if(closable){
36771             td.className = "x-tabs-closable";
36772             if(!this.closeTpl){
36773                 this.closeTpl = new Roo.Template(
36774                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36775                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36776                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
36777                 );
36778             }
36779             var el = this.closeTpl.overwrite(td, {"text": text});
36780             var close = el.getElementsByTagName("div")[0];
36781             var inner = el.getElementsByTagName("em")[0];
36782             return {"el": el, "close": close, "inner": inner};
36783         } else {
36784         */
36785         // not sure what this is..
36786 //            if(!this.tabTpl){
36787                 //this.tabTpl = new Roo.Template(
36788                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36789                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36790                 //);
36791 //                this.tabTpl = new Roo.Template(
36792 //                   '<a href="#">' +
36793 //                   '<span unselectable="on"' +
36794 //                            (this.disableTooltips ? '' : ' title="{text}"') +
36795 //                            ' >{text}</span></a>'
36796 //                );
36797 //                
36798 //            }
36799
36800
36801             var template = tpl || this.tabTpl || false;
36802             
36803             if(!template){
36804                 
36805                 template = new Roo.Template(
36806                    '<a href="#">' +
36807                    '<span unselectable="on"' +
36808                             (this.disableTooltips ? '' : ' title="{text}"') +
36809                             ' >{text}</span></a>'
36810                 );
36811             }
36812             
36813             switch (typeof(template)) {
36814                 case 'object' :
36815                     break;
36816                 case 'string' :
36817                     template = new Roo.Template(template);
36818                     break;
36819                 default :
36820                     break;
36821             }
36822             
36823             var el = template.overwrite(td, {"text": text});
36824             
36825             var inner = el.getElementsByTagName("span")[0];
36826             
36827             return {"el": el, "inner": inner};
36828             
36829     }
36830         
36831     
36832 });
36833
36834 /**
36835  * @class Roo.TabPanelItem
36836  * @extends Roo.util.Observable
36837  * Represents an individual item (tab plus body) in a TabPanel.
36838  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36839  * @param {String} id The id of this TabPanelItem
36840  * @param {String} text The text for the tab of this TabPanelItem
36841  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36842  */
36843 Roo.bootstrap.panel.TabItem = function(config){
36844     /**
36845      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36846      * @type Roo.TabPanel
36847      */
36848     this.tabPanel = config.panel;
36849     /**
36850      * The id for this TabPanelItem
36851      * @type String
36852      */
36853     this.id = config.id;
36854     /** @private */
36855     this.disabled = false;
36856     /** @private */
36857     this.text = config.text;
36858     /** @private */
36859     this.loaded = false;
36860     this.closable = config.closable;
36861
36862     /**
36863      * The body element for this TabPanelItem.
36864      * @type Roo.Element
36865      */
36866     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36867     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36868     this.bodyEl.setStyle("display", "block");
36869     this.bodyEl.setStyle("zoom", "1");
36870     //this.hideAction();
36871
36872     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36873     /** @private */
36874     this.el = Roo.get(els.el);
36875     this.inner = Roo.get(els.inner, true);
36876     this.textEl = Roo.get(this.el.dom.firstChild, true);
36877     this.pnode = Roo.get(els.el.parentNode, true);
36878     this.el.on("mousedown", this.onTabMouseDown, this);
36879     this.el.on("click", this.onTabClick, this);
36880     /** @private */
36881     if(config.closable){
36882         var c = Roo.get(els.close, true);
36883         c.dom.title = this.closeText;
36884         c.addClassOnOver("close-over");
36885         c.on("click", this.closeClick, this);
36886      }
36887
36888     this.addEvents({
36889          /**
36890          * @event activate
36891          * Fires when this tab becomes the active tab.
36892          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36893          * @param {Roo.TabPanelItem} this
36894          */
36895         "activate": true,
36896         /**
36897          * @event beforeclose
36898          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36899          * @param {Roo.TabPanelItem} this
36900          * @param {Object} e Set cancel to true on this object to cancel the close.
36901          */
36902         "beforeclose": true,
36903         /**
36904          * @event close
36905          * Fires when this tab is closed.
36906          * @param {Roo.TabPanelItem} this
36907          */
36908          "close": true,
36909         /**
36910          * @event deactivate
36911          * Fires when this tab is no longer the active tab.
36912          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36913          * @param {Roo.TabPanelItem} this
36914          */
36915          "deactivate" : true
36916     });
36917     this.hidden = false;
36918
36919     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36920 };
36921
36922 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36923            {
36924     purgeListeners : function(){
36925        Roo.util.Observable.prototype.purgeListeners.call(this);
36926        this.el.removeAllListeners();
36927     },
36928     /**
36929      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36930      */
36931     show : function(){
36932         this.pnode.addClass("active");
36933         this.showAction();
36934         if(Roo.isOpera){
36935             this.tabPanel.stripWrap.repaint();
36936         }
36937         this.fireEvent("activate", this.tabPanel, this);
36938     },
36939
36940     /**
36941      * Returns true if this tab is the active tab.
36942      * @return {Boolean}
36943      */
36944     isActive : function(){
36945         return this.tabPanel.getActiveTab() == this;
36946     },
36947
36948     /**
36949      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36950      */
36951     hide : function(){
36952         this.pnode.removeClass("active");
36953         this.hideAction();
36954         this.fireEvent("deactivate", this.tabPanel, this);
36955     },
36956
36957     hideAction : function(){
36958         this.bodyEl.hide();
36959         this.bodyEl.setStyle("position", "absolute");
36960         this.bodyEl.setLeft("-20000px");
36961         this.bodyEl.setTop("-20000px");
36962     },
36963
36964     showAction : function(){
36965         this.bodyEl.setStyle("position", "relative");
36966         this.bodyEl.setTop("");
36967         this.bodyEl.setLeft("");
36968         this.bodyEl.show();
36969     },
36970
36971     /**
36972      * Set the tooltip for the tab.
36973      * @param {String} tooltip The tab's tooltip
36974      */
36975     setTooltip : function(text){
36976         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36977             this.textEl.dom.qtip = text;
36978             this.textEl.dom.removeAttribute('title');
36979         }else{
36980             this.textEl.dom.title = text;
36981         }
36982     },
36983
36984     onTabClick : function(e){
36985         e.preventDefault();
36986         this.tabPanel.activate(this.id);
36987     },
36988
36989     onTabMouseDown : function(e){
36990         e.preventDefault();
36991         this.tabPanel.activate(this.id);
36992     },
36993 /*
36994     getWidth : function(){
36995         return this.inner.getWidth();
36996     },
36997
36998     setWidth : function(width){
36999         var iwidth = width - this.pnode.getPadding("lr");
37000         this.inner.setWidth(iwidth);
37001         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37002         this.pnode.setWidth(width);
37003     },
37004 */
37005     /**
37006      * Show or hide the tab
37007      * @param {Boolean} hidden True to hide or false to show.
37008      */
37009     setHidden : function(hidden){
37010         this.hidden = hidden;
37011         this.pnode.setStyle("display", hidden ? "none" : "");
37012     },
37013
37014     /**
37015      * Returns true if this tab is "hidden"
37016      * @return {Boolean}
37017      */
37018     isHidden : function(){
37019         return this.hidden;
37020     },
37021
37022     /**
37023      * Returns the text for this tab
37024      * @return {String}
37025      */
37026     getText : function(){
37027         return this.text;
37028     },
37029     /*
37030     autoSize : function(){
37031         //this.el.beginMeasure();
37032         this.textEl.setWidth(1);
37033         /*
37034          *  #2804 [new] Tabs in Roojs
37035          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37036          */
37037         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37038         //this.el.endMeasure();
37039     //},
37040
37041     /**
37042      * Sets the text for the tab (Note: this also sets the tooltip text)
37043      * @param {String} text The tab's text and tooltip
37044      */
37045     setText : function(text){
37046         this.text = text;
37047         this.textEl.update(text);
37048         this.setTooltip(text);
37049         //if(!this.tabPanel.resizeTabs){
37050         //    this.autoSize();
37051         //}
37052     },
37053     /**
37054      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37055      */
37056     activate : function(){
37057         this.tabPanel.activate(this.id);
37058     },
37059
37060     /**
37061      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37062      */
37063     disable : function(){
37064         if(this.tabPanel.active != this){
37065             this.disabled = true;
37066             this.pnode.addClass("disabled");
37067         }
37068     },
37069
37070     /**
37071      * Enables this TabPanelItem if it was previously disabled.
37072      */
37073     enable : function(){
37074         this.disabled = false;
37075         this.pnode.removeClass("disabled");
37076     },
37077
37078     /**
37079      * Sets the content for this TabPanelItem.
37080      * @param {String} content The content
37081      * @param {Boolean} loadScripts true to look for and load scripts
37082      */
37083     setContent : function(content, loadScripts){
37084         this.bodyEl.update(content, loadScripts);
37085     },
37086
37087     /**
37088      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37089      * @return {Roo.UpdateManager} The UpdateManager
37090      */
37091     getUpdateManager : function(){
37092         return this.bodyEl.getUpdateManager();
37093     },
37094
37095     /**
37096      * Set a URL to be used to load the content for this TabPanelItem.
37097      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37098      * @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)
37099      * @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)
37100      * @return {Roo.UpdateManager} The UpdateManager
37101      */
37102     setUrl : function(url, params, loadOnce){
37103         if(this.refreshDelegate){
37104             this.un('activate', this.refreshDelegate);
37105         }
37106         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37107         this.on("activate", this.refreshDelegate);
37108         return this.bodyEl.getUpdateManager();
37109     },
37110
37111     /** @private */
37112     _handleRefresh : function(url, params, loadOnce){
37113         if(!loadOnce || !this.loaded){
37114             var updater = this.bodyEl.getUpdateManager();
37115             updater.update(url, params, this._setLoaded.createDelegate(this));
37116         }
37117     },
37118
37119     /**
37120      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37121      *   Will fail silently if the setUrl method has not been called.
37122      *   This does not activate the panel, just updates its content.
37123      */
37124     refresh : function(){
37125         if(this.refreshDelegate){
37126            this.loaded = false;
37127            this.refreshDelegate();
37128         }
37129     },
37130
37131     /** @private */
37132     _setLoaded : function(){
37133         this.loaded = true;
37134     },
37135
37136     /** @private */
37137     closeClick : function(e){
37138         var o = {};
37139         e.stopEvent();
37140         this.fireEvent("beforeclose", this, o);
37141         if(o.cancel !== true){
37142             this.tabPanel.removeTab(this.id);
37143         }
37144     },
37145     /**
37146      * The text displayed in the tooltip for the close icon.
37147      * @type String
37148      */
37149     closeText : "Close this tab"
37150 });